You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The SessionStart install hook in hooks/hooks.json triggers pip install based on a comparison of an in-package version
string (the __version__ field read from gapp/__init__.py)
against a cached marker file under ${CLAUDE_PLUGIN_DATA}.
This approach has a compounding-failure mode with outer
plugin-cache staleness:
Claude Code decides whether to refresh the cached plugin on
disk by looking at .claude-plugin/plugin.json's version
field (see issue Plugin version not bumped on release — causes stale cache #23 — which documents that this has already
happened in practice).
When the outer plugin-cache is stale, the in-package version
string read by the SessionStart hook is reading the SAME stale
source file. Both sides of the compare reference the same old
value. The check sees "no change". pip install never runs.
Users reinstall, see no errors, and believe they picked up
the update — but their dependencies (and sometimes the server
code itself) stayed at the old version.
The preferred pattern for plugin-bundled dependency install
avoids this compounding by comparing the actual artifact that
would change when dependencies differ — requirements.txt —
rather than a human-maintained version field. That pattern
is documented in the upstream scaffold plugin and cited as
Anthropic-recommended in the plugins reference.
Proposed solution
Two pieces of work:
1. Switch the SessionStart hook to the canonical pattern
Replace the version-string-compare with a requirements.txt-diff
trigger. Sketch:
Trigger fires when requirements.txt actually changes (the
real signal).
Not affected by outer plugin-cache staleness — if the cache
didn't refresh, the diff still sees two identical files and
correctly does nothing; the failure mode is limited to "we
didn't install new deps because the cache didn't bring new
deps here," which is upstream of the install hook.
On install failure, the cached manifest is removed so the
next session retries.
2. Document the pattern + antipattern in .md files
So the rationale for the switch is preserved at head revision,
not buried in git history:
CONTRIBUTING.md — add a "Plugin dependency-install trigger"
section that:
Names requirements.txt-diff as the preferred pattern.
Calls out the in-package-version-string-compare antipattern
(abstract description of the failure mode above). No
third-party product names.
Keep the antipattern description abstract — this plugin is one
of the affected instances, but the guidance should be portable
to any plugin that might adopt the canonical approach.
Issue #23 is about the outer plugin-cache staleness — making
sure .claude-plugin/plugin.json's version is always bumped
on release so Claude Code invalidates the cached plugin. That's
the upstream layer.
This ticket is about the INNER install-trigger layer — which
currently compounds #23's failure by also failing silently when
the cache is stale. Fixing this ticket removes the compounding
even if #23 regresses in the future.
Both fixes are independent. Either can land first. Together
they eliminate both layers of the silent-no-update failure.
Deliverables
Update hooks/hooks.json SessionStart hook to the requirements.txt-diff pattern.
Remove obsolete ${CLAUDE_PLUGIN_DATA}/installed_version
marker cleanup (no longer referenced by the new hook).
Update CONTRIBUTING.md with the preferred-pattern +
antipattern documentation described above.
Verify the hook still runs correctly on a fresh install
and on a dependency change.
Problem
The SessionStart install hook in
hooks/hooks.jsontriggerspip installbased on a comparison of an in-package versionstring (the
__version__field read fromgapp/__init__.py)against a cached marker file under
${CLAUDE_PLUGIN_DATA}.This approach has a compounding-failure mode with outer
plugin-cache staleness:
disk by looking at
.claude-plugin/plugin.json'sversionfield (see issue Plugin version not bumped on release — causes stale cache #23 — which documents that this has already
happened in practice).
string read by the SessionStart hook is reading the SAME stale
source file. Both sides of the compare reference the same old
value. The check sees "no change".
pip installnever runs.the update — but their dependencies (and sometimes the server
code itself) stayed at the old version.
The preferred pattern for plugin-bundled dependency install
avoids this compounding by comparing the actual artifact that
would change when dependencies differ —
requirements.txt—rather than a human-maintained version field. That pattern
is documented in the upstream scaffold plugin and cited as
Anthropic-recommended in the plugins reference.
Proposed solution
Two pieces of work:
1. Switch the SessionStart hook to the canonical pattern
Replace the version-string-compare with a
requirements.txt-difftrigger. Sketch:
Key properties this restores:
requirements.txtactually changes (thereal signal).
didn't refresh, the diff still sees two identical files and
correctly does nothing; the failure mode is limited to "we
didn't install new deps because the cache didn't bring new
deps here," which is upstream of the install hook.
next session retries.
2. Document the pattern + antipattern in
.mdfilesSo the rationale for the switch is preserved at head revision,
not buried in git history:
CONTRIBUTING.md— add a "Plugin dependency-install trigger"section that:
requirements.txt-diff as the preferred pattern.(abstract description of the failure mode above). No
third-party product names.
plugin / Anthropic plugins reference).
Keep the antipattern description abstract — this plugin is one
of the affected instances, but the guidance should be portable
to any plugin that might adopt the canonical approach.
Relationship to #23
Issue #23 is about the outer plugin-cache staleness — making
sure
.claude-plugin/plugin.json'sversionis always bumpedon release so Claude Code invalidates the cached plugin. That's
the upstream layer.
This ticket is about the INNER install-trigger layer — which
currently compounds #23's failure by also failing silently when
the cache is stale. Fixing this ticket removes the compounding
even if #23 regresses in the future.
Both fixes are independent. Either can land first. Together
they eliminate both layers of the silent-no-update failure.
Deliverables
hooks/hooks.jsonSessionStart hook to therequirements.txt-diff pattern.${CLAUDE_PLUGIN_DATA}/installed_versionmarker cleanup (no longer referenced by the new hook).
CONTRIBUTING.mdwith the preferred-pattern +antipattern documentation described above.
and on a dependency change.
— the two tickets are complementary).
Verification
After the change, a contributor reading only the head revision
of this repo should see, in
CONTRIBUTING.md:approach avoids it.
No git-log excavation required.