refactor(plugin): retire what the hand-bump scheme left behind — the README that taught it, the floor test, the stale rationale (#3328)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / integration (push) Successful in 32s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / Build & push image (push) Successful in 16s

#3127 checklist 19. The step's own deletion list turned out to be largely
spent: `check_version_bump()` came out with #3327, and the machinery the step
expected to delete alongside it is load-bearing for its replacement.

`manifest_version(ref=…)`, `--base`, `--no-version` and the `origin/main`
resolve path all STAY. Derivation makes the value right; it does not make the
comparison unnecessary. `check_version_is_minted` still has to ask "did the
version move when the shipped content did?", and that is a base-branch
question no matter who chose the number. The step was planned before #3327
landed, when the assumption was that these died with the guard.

What was actually still standing, all of it teaching or asserting the retired
scheme:

- `plugin/README.md` told the reader to "set a `version` bump per release."
  A shipped file, instructing the exact act the mint replaced — this is how a
  deleted control gets re-added by someone following the docs. Now says not to
  hand-edit the field, names `make mint-plugin`, and says what a forgotten
  mint costs. (`make` is not installed on every workstation, so the direct
  script invocation is given too.)
- `test_plugin_version_bumped_with_the_hook` asserted `version >= (0,1,31)`
  as a tuple of ints. Under a minted value it passes vacuously — every date
  clears a floor of 0.1.31 — and `int("0415")` silently eats the padding the
  format exists to keep. Superseded by
  `test_the_shipped_manifest_carries_a_minted_version`, which asserts the
  canonical shape instead of an ordering the comparator does not perform.
  Removed whole (rule 22).
- The module preamble still ended on "a written rule that depends on being
  remembered is not a control; this is" — true of the bump guard, and read as
  a stronger claim than the mint can support. Replaced with what the change
  did and did not remove: choosing a number is gone, running the mint is not,
  and the difference is that forgetting is now loud rather than silent.
- An orphaned `# --- the version bump ---` section header with nothing under
  it, and a test docstring still naming `check_version_bump`.

`--no-version` keeps its one legitimate case — on `main` the version is
measured against itself — and now says so in both the usage block and its
`--help`, so it does not read as an escape hatch. `check_session_context_
reports_its_version` stays untouched: a different check with a different job,
and the only thing that makes step 6 readable from a transcript (#2220).

Version minted 2026.09.02.0415 -> 2026.09.02.0438 for the README change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TcCs1CcQ1ormdnzSshKqvN
This commit is contained in:
2026-09-02 00:38:50 -04:00
co-authored by Claude Opus 5
parent 64cb719a12
commit f5a3643da8
5 changed files with 34 additions and 20 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"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.",
"version": "2026.09.02.0415",
"version": "2026.09.02.0438",
"author": {
"name": "Bryan Van Deusen"
},
+7 -2
View File
@@ -78,8 +78,13 @@ On install you'll be asked for:
## Notes
- Set a `version` bump in `.claude-plugin/plugin.json` per release so clients
pick up changes.
- **Do not hand-edit `version` in `.claude-plugin/plugin.json`.** It is minted
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
key; the MCP tools need **write** scope to create/update. Every hook is a GET
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
compares versions to decide whether to refresh its cache.
The rule for the second one was already written down and was still missed. A
written rule that depends on being remembered during a long session is not a
control; this is.
Both were fixed. The second was fixed TWICE — once by bumping the number, and
then properly, by removing the class it came from: `plugin.json`'s version 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
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.
Usage:
python3 scripts/check_plugin.py # all checks
python3 scripts/check_plugin.py --no-version # skip the version check
python3 scripts/check_plugin.py # all checks
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
@@ -217,8 +235,6 @@ def check_patterns() -> None:
ok(f"{rel}: no known-bad patterns")
# --- the version bump ------------------------------------------------------
# --- shellcheck ------------------------------------------------------------
def check_shellcheck() -> None:
@@ -668,7 +684,8 @@ def check_version_is_minted(base: str = "origin/main") -> None:
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
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",
help="branch the version is measured against")
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.
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.
"""
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
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():
"""#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