Skip to content

Migrate SessionStart install hook to requirements.txt-diff pattern and document #31

@krisrowe

Description

@krisrowe

Problem

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:

"command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/requirements.txt\" \"${CLAUDE_PLUGIN_DATA}/requirements.txt\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_ROOT}\" && cp requirements.txt \"${CLAUDE_PLUGIN_DATA}/\" && python3 -m pip install -t \"${CLAUDE_PLUGIN_DATA}/site-packages\" -r requirements.txt) || rm -f \"${CLAUDE_PLUGIN_DATA}/requirements.txt\""

Key properties this restores:

  • 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.
    • References the canonical pattern source (upstream scaffold
      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'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.
  • Bump plugin version (per the discipline targeted by Plugin version not bumped on release — causes stale cache #23
    — the two tickets are complementary).

Verification

After the change, a contributor reading only the head revision
of this repo should see, in CONTRIBUTING.md:

  • What the install-trigger pattern is and why.
  • What the antipattern is, how it fails, and why the current
    approach avoids it.

No git-log excavation required.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions