Skip to content

hooks: warn at session start when the plugin pin lags the marketplace clone - #4412

Merged
d-morrison merged 2 commits into
mainfrom
claude/p1-issues-bavuel
Oct 8, 2026
Merged

d-morrison merged 2 commits into
mainfrom
claude/p1-issues-bavuel

Conversation

@d-morrison

Copy link
Copy Markdown
Collaborator

Before: Claude Code runs ai-config hooks from a per-scope cache pin in ~/.claude/plugins/installed_plugins.json, and that pin does not advance when the marketplace clone updates. Merged hook fixes went unrun for weeks with nothing saying so. #2439 records four measurements, the latest on 2026-10-08, where a user pin of 6a4f97ebfc79 refused pushes that main no longer refuses. The manual check in keep-checkouts-fresh.md and scripts/check-hook-delivery.py only run once a session already suspects the cache.

After: a new SessionStart hook, hooks/warn-stale-plugin-pin.py, compares each ai-config pin that applies to the session (user scope, or a project/local scope whose projectPath covers the session directory) against the local marketplace clone's HEAD. When a pin lags, it names the commit count and the per-scope claude plugin marketplace update / claude plugin update commands.

Behavior:

  • Never blocks.
  • Does not fetch, so the comparison is against the local clone, not origin.
  • Silent when there is no plugin record.
  • Reports to stderr when the record or clone is unreadable.
  • Does not report a pin that is ahead of the clone.

One limit: a pin cannot warn about itself, so pins that predate this hook need one manual update first. The docstring and README row say so.

How:

  • Registered in hooks/hooks.json.
  • plugins/ai-config-hooks/hooks/hooks.json regenerated (gen-hooks-plugin.py --check passes locally).
  • README catalog row added (check-hook-catalog.py passes locally).
  • keep-checkouts-fresh.md now points to the hook instead of saying the check is still tracked.
  • hooks/test-warn-stale-plugin-pin.py has 13 assertions and passes locally. Four mutations (scope filter, equality check, plugin-name filter, ancestor check) each turn a named case red.
  • An adversarial subagent review found four items: a pin ahead of the clone reported as stale, git errors swallowed, a non-object plugins value crashing the hook, and the bootstrap limit undocumented. All four are fixed in this commit.

Closes #2439

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vwcai8NPoZC3GjwGwhcKHa


Generated by Claude Code

… clone

Claude Code runs ai-config hooks from a per-scope cache pin recorded in
~/.claude/plugins/installed_plugins.json, and the pin does not advance
when the marketplace clone updates. Merged hook fixes went unrun for
weeks with nothing saying so (four measurements on #2439, the latest
2026-10-08).

New SessionStart hook warn-stale-plugin-pin.py compares each applicable
ai-config pin (user scope, or a project scope covering the session
directory) against the local marketplace clone's HEAD and, when it lags,
names the commit count and the per-scope update commands. It never
blocks, does not fetch, and is silent when there is no plugin record.

Registered in hooks/hooks.json, README catalog row added, generated
plugin manifest refreshed, keep-checkouts-fresh.md updated. Thirteen test
assertions; four mutations (scope filter, equality check, plugin-name
filter, ancestor check) each turn a named case red. A pin ahead of
the clone is not reported.

Closes #2439

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vwcai8NPoZC3GjwGwhcKHa

Copy link
Copy Markdown
Collaborator Author

/review


Generated by Claude Code

validate failed on check-unpinned-git-fixtures (init without -b, and a
variable named clone that the scanner reads as a git clone call) and
check-text-encoding (two bare write_text calls). Both checks now pass
locally, and the suite still passes.

Refs #2439

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vwcai8NPoZC3GjwGwhcKHa

Copy link
Copy Markdown
Collaborator Author

/review


Generated by Claude Code

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

👀 Claude Review Dispatched — run reviewing PR #4412 at af69a87.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Claude finished review — View run


Summary

This PR adds hooks/warn-stale-plugin-pin.py, a SessionStart hook that warns (never blocks) when a session's ai-config plugin pin in ~/.claude/plugins/installed_plugins.json lags the local marketplace clone's HEAD, closing #2439. It wires the hook into hooks/hooks.json and the generated plugins/ai-config-hooks/hooks/hooks.json, documents it in the README catalog, updates shared/workflow/keep-checkouts-fresh.md to point at it, and ships hooks/test-warn-stale-plugin-pin.py with 13 scenarios including several negative controls.

Verification performed

  • python3 hooks/test-warn-stale-plugin-pin.py — all 13 cases pass.
  • python3 scripts/gen-hooks-plugin.py --check — the generated plugin manifest matches hooks/hooks.json.
  • python3 -c "import json; json.load(open(...))" on both hooks.json files — valid JSON.
  • python3 scripts/check-hook-catalog.py — 120/120 hooks documented, including the new one, no double-binding issues.
  • python3 scripts/check-hook-delivery.py — confirms, on this real machine, that the hook correctly identifies itself as missing from the currently-installed pin (the documented bootstrap limitation working as intended).
  • Ran the hook directly against this machine's real ~/.claude/plugins state — silent and exit 0, correctly matching that the installed pin's gitCommitSha equals the local marketplace clone's HEAD.
  • Confirmed claude plugin update --help (live CLI, v2.1.261) shows -s, --scope <scope> as a standard Commander-style option (user, project, local, managed), supporting the hook's generated remedy commands and the "local" scope used in applies().
  • Traced pin_is_current/behind/applies logic by hand against each test case (stale, current, ahead-of-clone, cross-project, cross-plugin, unknown pin, malformed/missing records) — all match the documented semantics.

Findings

No blocking issues found. Two very minor, non-blocking documentation nits:

  1. README.md:452 and hooks/hooks.json's why text describe the applicable scopes as "user scope, or a project scope covering its directory," but the implementation (hooks/warn-stale-plugin-pin.py:68-79, applies()) and the module's own docstring (hooks/warn-stale-plugin-pin.py:22-25) also handle local scope. Not a functional problem — just a documentation completeness gap between the catalog blurb and the actual (correct) code.
  2. hooks/warn-stale-plugin-pin.py:126-131: when the marketplace clone is missing entirely, git(clone, "rev-parse", "HEAD") fails and prints its own stderr message (since quiet defaults to False there), and then stale_lines() prints a second, separate "no git clone at ... cannot compare" message right after. Harmless duplication on stderr, not a behavioral bug (test 9 and the live check-hook-delivery.py run both confirm the intended message still appears).

Both are cosmetic and don't block merge.

Verdict

Ready for merge

Structured Review Data (JSON)
{
  "schema_version": "1.1",
  "reviewer": "claude",
  "commit_sha": "af69a8761499af750715f15462503a81d9134c10",
  "verdict": "CLEAN",
  "findings": [],
  "detailed_assessment": "Traced warn-stale-plugin-pin.py's applies/pin_is_current/behind helpers against every test-warn-stale-plugin-pin.py scenario and live machine state; no failure mode found in staleness, ahead-of-clone, or malformed-record handling.",
  "holistic_assessment": "Checked generator sync, README catalog script, hook-delivery check, and keep-checkouts-fresh.md cross-references for integration regressions; all pass and scope matches the closed issue 2439 requirement."
}

Reviewed commit: af69a87

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

💰 Cost: $1.6406 (review) — run

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.

Plugin cache pins lag the marketplace clone silently, so merged hook fixes never reach consumers

2 participants