dev → main: rule usage telemetry, the plugin's derived version, and the backlog since b267037 #136

Merged
bvandeusen merged 19 commits from dev into main 2026-09-02 18:52:20 -04:00
5 changed files with 34 additions and 20 deletions
Showing only changes of commit f5a3643da8 - Show all commits
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "scribe", "name": "scribe",
"description": "Scribe system-of-record for Claude Code: MCP tools over your notes/tasks/projects/rules, a session-start push channel that surfaces your always-on rules + active-project context, process-skills (writing-plans, systematic-debugging, verification, brainstorming, reusing-code), and your saved Scribe Processes auto-surfaced as skills (/scribe:sync). Replaces superpowers + file-memory with one app-backed plugin.", "description": "Scribe system-of-record for Claude Code: MCP tools over your notes/tasks/projects/rules, a session-start push channel that surfaces your always-on rules + active-project context, process-skills (writing-plans, systematic-debugging, verification, brainstorming, reusing-code), and your saved Scribe Processes auto-surfaced as skills (/scribe:sync). Replaces superpowers + file-memory with one app-backed plugin.",
"version": "2026.09.02.0415", "version": "2026.09.02.0438",
"author": { "author": {
"name": "Bryan Van Deusen" "name": "Bryan Van Deusen"
}, },
+7 -2
View File
@@ -78,8 +78,13 @@ On install you'll be asked for:
## Notes ## Notes
- Set a `version` bump in `.claude-plugin/plugin.json` per release so clients - **Do not hand-edit `version` in `.claude-plugin/plugin.json`.** It is minted
pick up changes. from the clock — run `make mint-plugin` (or `python3
scripts/mint_plugin_version.py`) after changing anything under `plugin/`, and
commit the result. The installer decides whether to refresh the cache it
executes from by comparing that string, so content that ships without a new
version reaches the repo and stops there (#2209). CI fails the lane if you
forget.
- The session-start, auto-inject and prior-art hooks need only a **read**-scoped - The session-start, auto-inject and prior-art hooks need only a **read**-scoped
key; the MCP tools need **write** scope to create/update. Every hook is a GET key; the MCP tools need **write** scope to create/update. Every hook is a GET
for that reason — a read key cannot POST. for that reason — a read key cannot POST.
+25 -8
View File
@@ -13,9 +13,20 @@ separate defects have reached a live install through that path:
install, because `plugin.json`'s version wasn't bumped and the installer install, because `plugin.json`'s version wasn't bumped and the installer
compares versions to decide whether to refresh its cache. compares versions to decide whether to refresh its cache.
The rule for the second one was already written down and was still missed. A Both were fixed. The second was fixed TWICE — once by bumping the number, and
written rule that depends on being remembered during a long session is not a then properly, by removing the class it came from: `plugin.json`'s version is
control; this is. no longer a value anybody chooses. `scripts/mint_plugin_version.py` derives it
from the clock (`make mint-plugin`), and `check_version_is_minted` below fails
the lane when shipped content moved and the version did not.
State exactly what that did and did not remove, because a rationale that
overstates its own control is how the control gets trusted past its limit, and
because the paragraph this replaces was itself read that way. Gone: having to
remember which NUMBER to write, and the whole question of whether a chosen
number was the right one. Not gone: the mint still has to be RUN, and
forgetting to run it is still possible. What changed is that forgetting is now
LOUD — a red lane on the batch that forgot, instead of a silent no-op found
weeks later when somebody says "I don't think it updated" (#2220).
shellcheck and jq are NOT in `ci-python` (verified against CI-runner's Dockerfile shellcheck and jq are NOT in `ci-python` (verified against CI-runner's Dockerfile
and scripts/install-common.sh, not from memory — rule #37). CI installs both and scripts/install-common.sh, not from memory — rule #37). CI installs both
@@ -31,8 +42,15 @@ itself loudly, because a check that quietly no-ops is the failure mode this
whole file exists to prevent. whole file exists to prevent.
Usage: Usage:
python3 scripts/check_plugin.py # all checks python3 scripts/check_plugin.py # all checks
python3 scripts/check_plugin.py --no-version # skip the version check python3 scripts/check_plugin.py --no-version # on `main` only — see below
`--no-version` exists for ONE case. The version is measured against
`origin/main`, so on `main` itself the comparison is against itself and answers
nothing; the syntax, pattern and marker checks are the only ones that mean
anything there. It is NOT a way past a red lane — see
`check_version_is_minted`, whose whole design is shaped by keeping this flag
out of anyone's muscle memory.
""" """
from __future__ import annotations from __future__ import annotations
@@ -217,8 +235,6 @@ def check_patterns() -> None:
ok(f"{rel}: no known-bad patterns") ok(f"{rel}: no known-bad patterns")
# --- the version bump ------------------------------------------------------
# --- shellcheck ------------------------------------------------------------ # --- shellcheck ------------------------------------------------------------
def check_shellcheck() -> None: def check_shellcheck() -> None:
@@ -668,7 +684,8 @@ def check_version_is_minted(base: str = "origin/main") -> None:
def main() -> int: def main() -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--no-version", action="store_true", parser.add_argument("--no-version", action="store_true",
help="skip the minted-version check") help="skip the minted-version check; for `main`, where "
"it would be measured against itself")
parser.add_argument("--base", default="origin/main", parser.add_argument("--base", default="origin/main",
help="branch the version is measured against") help="branch the version is measured against")
args = parser.parse_args() args = parser.parse_args()
+1 -1
View File
@@ -203,7 +203,7 @@ def test_shipped_content_changed_reports_a_version_only_commit_as_unchanged(monk
"""End to end through the git seam, with git stubbed. """End to end through the git seam, with git stubbed.
The unit above proves the comparison; this proves it is actually WIRED to The unit above proves the comparison; this proves it is actually WIRED to
the path that `check_version_bump` reads. A correct helper nobody calls the path that `check_version_is_minted` reads. A correct helper nobody calls
would leave the circular check exactly as it was. would leave the circular check exactly as it was.
""" """
from scripts import check_plugin from scripts import check_plugin
-8
View File
@@ -867,14 +867,6 @@ def test_hook_skips_prose_and_data_files():
assert '/scribe_defs.sh"' in src # sourced, not copied assert '/scribe_defs.sh"' in src # sourced, not copied
def test_plugin_version_bumped_with_the_hook():
"""The #1040 lesson: a plugin change clients can't see is a change that didn't
ship."""
manifest = json.loads((PLUGIN / ".claude-plugin" / "plugin.json").read_text())
version = tuple(int(p) for p in manifest["version"].split("."))
assert version >= (0, 1, 31)
def test_hook_keeps_sync_and_reuse_dedup_apart(): def test_hook_keeps_sync_and_reuse_dedup_apart():
"""#2708's dedup audit, pinned: the hook holds TWO per-session id files and """#2708's dedup audit, pinned: the hook holds TWO per-session id files and
feeds each its own class — sync ids (snippets recording the edited file) to feeds each its own class — sync ids (snippets recording the edited file) to