Skip to content

Treat an installed Claude plugin as an upgrade, not as a reason to stop - #688

Merged
MongLong0214 merged 1 commit into
mainfrom
fix-660-plugin-upgrade
Aug 15, 2026
Merged

Treat an installed Claude plugin as an upgrade, not as a reason to stop#688
MongLong0214 merged 1 commit into
mainfrom
fix-660-plugin-upgrade

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Toward #660 — the half that is this product's.

installed_plugins="$(claude plugin list 2>/dev/null || true)"
case "$installed_plugins" in
  *commitlore*)
    record_skipped "claude-code" "the commitlore plugin is already installed -- left unchanged"
    return                     # <- here

A release reached the CLI wrapper and never the plugin cache. The wrapper
reported the new version while the tools an agent calls stayed at whatever
version installed them. Four generations accumulated that way, and v1.0.0
joined them without displacing one
— wrapper 1.0.0, cache 0.8.0.

It did not fail. It declined to try, and reported that as skipped.

marketplace update is the missing step. Adding a marketplace that is already
present is a no-op, so even the path that did run could not have seen a new
version.

Why no test caught it

Every existing installer test reads install.sh as text. The file contained
every string it needed to contain; the control flow never ran. That is the same
blindness release-gate §6c is about — a fresh install works, an upgrade does not,
and nothing that starts from nothing can tell.

test/install-plugin-upgrade.test.ts executes the function against a fake
claude and asserts on which subcommands it invoked. Negative control: restoring
the early return leaves only plugin list, and both cases fail.

What this does not do

It does not make a running session load the new plugin — a host keeps the
runtime it loaded, and that is outside this product. It makes the release
arrive, which is the half that was ours.

Toward rather than Closes: #660's condition is commitlore doctor --only mcp-runtime-identity reporting zero unusable runtimes, and that cannot be
observed until an install runs on a machine with stale generations.

wire_claude_code returned as soon as `claude plugin list` mentioned commitlore.
So a release reached the CLI wrapper and never the plugin cache: the wrapper
reported the new version while the tools an agent actually calls stayed at
whatever version installed them. Four generations accumulated that way, and
v1.0.0 joined them without displacing one -- wrapper 1.0.0, cache 0.8.0.

It did not fail. It declined to try, and reported that as skipped.

`marketplace update` is the step that was missing, and adding a marketplace that
is already present is a no-op, so even the path that did run could not have seen
a new version.

Every existing installer test reads install.sh as text, which is why this
survived: the file contained every string it needed to contain and the control
flow never ran. The regression executes the function against a fake `claude` and
asserts on which subcommands it invoked. Verified by restoring the early return
-- only `plugin list` runs, and both cases fail.

This does not make a running session load the new plugin; a host keeps the
runtime it loaded. It makes the release arrive, which is the half that was ours.

Limit: already installed is the upgrade case, never a skip
Blast: system
Undo: easy
Certainty: firm
Provenance: authored
Record-Id: r-660plug
@github-actions

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 1 commit in origin/main..cdc7bc682e979b3064f1981c2da006461acf3a41
Active constraints: not read — commitlore: git log --follow accepts exactly one pathspec, so renames are not followed for 2 paths; query one path at a time to follow its rename chain (2 changed paths)

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

@MongLong0214
MongLong0214 merged commit 5d0e422 into main Aug 15, 2026
12 checks passed
MongLong0214 added a commit that referenced this pull request Aug 15, 2026
claude-code appeared in neither hosts nor notDetected. install.sh defines
has_claude_code and wire_claude_code and calls neither; the enumeration had no
branch for it. So the plugin cache went untouched by every release and nothing
said so -- notDetected: [] read as "every host was detected", which is how a
missing host stays missing.

That is worse than #660, which at least reported the wrong thing. This reported
nothing, so there was nothing to disbelieve.

The enumeration now handles it the way it handles Hermes: refresh the
marketplace, then install, healthy only when the install succeeds. Adding a
marketplace that is already present is a no-op, so without the refresh a
reinstall reinstates the same version -- that is the whole of #660, and it also
means #688's fix to wire_claude_code was correct and unreachable.

The guard took three attempts and each failure is worth more than the fix.
Filtering hosts by "no underscore" dropped wire_claude_code along with
wire_codex_mcp -- the guard excluded the case it existed for. Then checking that
the name appears in the enumeration passed while claude-code appeared only
inside a helper nothing called, which is the defect itself wearing the test as
clothes. It now asserts on dispatch: notDetected.push('<host>') or a row in the
candidate table, both written only where a host is actually handled.

The shell/CLI name difference for gemini is written down rather than worked
around. A guard that cannot express a legitimate difference becomes noise and
then gets deleted.

Limit: a host is wired or reported undetected, never absent
Blast: system
Undo: easy
Certainty: firm
Provenance: authored
Record-Id: r-689host
MongLong0214 added a commit that referenced this pull request Aug 15, 2026
Five fixes have been sitting on main since v1.0.0 and none of them has reached
anyone. They are all defects at the install and distribution boundary, and that
boundary has exactly one delivery mechanism: a release.

    #683  the release workflow failing on a release that already exists
    #684  hermes install refusing the config it wrote
    #687  the skills root taken from the running bundle instead of --data-root
    #688  an installed plugin treated as a reason to stop rather than upgrade
    #690  claude-code wired by nothing, in neither hosts nor notDetected

Measured rather than assumed: re-installing with main's install.sh still left the
plugin cache at 0.8.0 and claude-code out of both lists, because the enumeration
that #690 fixed is compiled into the binary being installed -- and that binary is
v1.0.0.

dist is unchanged and the canonical digest is identical to before the bump. The
version is read from package.json at runtime rather than compiled in. Rebuilt
through the canonical builder and verified rather than assumed, the same as
v1.0.0.

Thirty-eight pins across nine files. The installers and READMEs carry the tag in
URLs that resolve only once the tag exists, so this is one commit and the tag
goes on it.

This is the first release cut under release-gate 6c, which asks for an upgrade
over the previous version rather than a fresh install. The five fixes above are
why that section exists.

Limit: a distribution-boundary fix reaches nobody until it is released
Blast: system
Undo: costly
Certainty: firm
Provenance: authored
Record-Id: r-rel101
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant