Plugin registry: project-scope rows re-pointed/consumed when sessions open in git worktrees — installs erode machine-wide

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 6, 2026

Environment: Claude Code 2.1.220, macOS (Darwin 25.5.0). Plugins distributed via a private git marketplace, installed at --scope project across ~9 repos. Heavy git-worktree workflow (each session opens in <repo>/.claude/worktrees/<name>).

Summary

Project-scope install rows in ~/.claude/plugins/installed_plugins.json are re-pointed to a different projectPath (rather than a new row being added) when sessions open at paths that have no matching row — most commonly git worktrees of repos whose checked-in .claude/settings.json sets enabledPlugins for the plugin. Over days of normal use the registry erodes: repos that had project-scope installs silently lose them, and with them every skill and every hook the plugin ships.

Observed behaviour

  1. A project-scope row's projectPath was observed rewritten twice in one afternoon while its installedAt stayed constant (2026-07-28T18:14:47.506Z in my registry — same row, three homes):
  • 11:23 → <repo-A> (repo root)
  • 16:55 → <repo-B>/.claude/worktrees/<wt-1> (a different repo's worktree)
  • 17:09 → <repo-C>/.claude/worktrees/<wt-2> (a third repo's worktree, scope flipped to local)
  1. Registry erosion over one week: 7 project-scope rows for the plugin on 2026-07-30 → 5 on 2026-08-06 morning → 3 by that evening. A second plugin from a second marketplace eroded 3 rows → 1 the same way. No claude plugin uninstall was ever run and nothing edited the file directly (verified from session transcripts).
  2. Mid-session effect: a session resolves plugin skills early (e.g. a Skill call succeeds at 13:37), another session starts elsewhere and consumes the row, and from that moment every skill from the plugin returns Unknown skill in the first session (measured at 15:23 in the same transcript). The plugin cache on disk is intact throughout; claude plugin list still shows the marketplace content.
  3. Because worktree paths never match a repo root's project row, each new worktree session re-triggers the behaviour — the single wandering row follows the most recently opened session, and every other repo's next skill call fails.

Expected behaviour

  • A session opened in a git worktree should resolve project-scope plugin installs via the repo the worktree belongs to (e.g. resolve through git rev-parse --git-common-dir), instead of treating the worktree path as an unknown project.
  • If the app repairs an enabled-but-not-installed state, it should add a row scoped to that path, never re-point or consume an existing row that belongs to a different project.

Repro sketch

  1. Repo A with .claude/settings.json"enabledPlugins": {"my-plugin@my-marketplace": true}; install with claude plugin install my-plugin@my-marketplace --scope project from A's root. Repeat for repo B.
  2. git -C A worktree add .claude/worktrees/wt1 -b test and open a Claude Code session in A/.claude/worktrees/wt1.
  3. Inspect ~/.claude/plugins/installed_plugins.json: a pre-existing row's projectPath is rewritten (observed with unchanged installedAt), rather than a new row appearing; repo B (or A's root) subsequently fails to resolve the plugin's skills, and the plugin's hooks stop loading there.

Impact

Teams distributing engineering standards as marketplace plugins lose skills and PreToolUse guardrail hooks silently, per repo, in an order that tracks which repos are worked on most. The failure surfaces as Unknown skill far from the cause, sometimes hours into a session. Workaround we've adopted: --scope user installs, which are path-independent and immune.

View original on GitHub ↗