Problem
Removing a git worktree leaves behind every project-scope plugin install record that pointed into it. Claude Code keys project-scope installs in ~/.claude/plugins/installed_plugins.json by a literal projectPath, and nothing reaps them when that path goes away.
Measured on one machine, confirmed twice independently:
- 108 project-scope install records across 8 marketplaces —
melodic-software 45, claude-plugins-official 46, dotnet-agent-skills 10, anthropic-agent-skills 3, openai-codex 1, cloudflare 1, compound-engineering-plugin 1, karpathy-skills 1.
- All 108 name one path:
D:\worktrees\medley-p51, a worktree that no longer exists.
- Distinct
projectPath values machine-wide: 1. Every project-scope record on the machine is an orphan.
D:\worktrees is this plugin's configured worktree_root, and this plugin's own babysit_worktree_root option describes what lives there as "ephemeral" worktrees. These are lifecycle garbage from a directory this plugin created and destroyed.
plugins/source-control/skills/worktree/ greps clean for installed_plugins, plugin uninstall, pluginConfigs, and project-scope — the cleanup path removes the directory and stops.
Why this plugin owns it
claude-ops:plugins sees the wreckage (it reports these as actionable version skew and routes them to a converge that cannot run — see the sibling issue), but it must not reach across into another plugin's userConfig to decide what is "ephemeral", and it does not own the lifecycle. source-control creates the worktree, knows worktree_root, has worktree_stale_days, and destroys the directory. Reaping at teardown stops these at the source.
The safety boundary
Reap on a teardown this plugin performs, never on "a path that does not currently resolve". A project-scope record for a live repository on an unmounted network share or a detached external volume is indistinguishable from a dead worktree to a bare existence check, and destroying those records is real data loss.
Two further constraints:
- Removal goes through
claude plugin uninstall <id> -s project, never a direct edit of installed_plugins.json (Claude Code internal state, not a published contract).
-s project has no path flag — it acts on the literal cwd (verified 2.1.228, unchanged 2.1.240). A reap must therefore run while the directory still exists, i.e. before removal. If a reliable reap proves unachievable through the CLI, ship the detection-and-report half rather than something that silently half-works.
Pre-existing orphans (the 108 above) are not reachable by a teardown hook that did not exist when they were created — reporting them from status/audit is safe; reaping them is not, absent an explicit user action.
Related
No linked issue.
Problem
Removing a git worktree leaves behind every project-scope plugin install record that pointed into it. Claude Code keys project-scope installs in
~/.claude/plugins/installed_plugins.jsonby a literalprojectPath, and nothing reaps them when that path goes away.Measured on one machine, confirmed twice independently:
melodic-software45,claude-plugins-official46,dotnet-agent-skills10,anthropic-agent-skills3,openai-codex1,cloudflare1,compound-engineering-plugin1,karpathy-skills1.D:\worktrees\medley-p51, a worktree that no longer exists.projectPathvalues machine-wide: 1. Every project-scope record on the machine is an orphan.D:\worktreesis this plugin's configuredworktree_root, and this plugin's ownbabysit_worktree_rootoption describes what lives there as "ephemeral" worktrees. These are lifecycle garbage from a directory this plugin created and destroyed.plugins/source-control/skills/worktree/greps clean forinstalled_plugins,plugin uninstall,pluginConfigs, andproject-scope— the cleanup path removes the directory and stops.Why this plugin owns it
claude-ops:pluginssees the wreckage (it reports these as actionable version skew and routes them to aconvergethat cannot run — see the sibling issue), but it must not reach across into another plugin'suserConfigto decide what is "ephemeral", and it does not own the lifecycle. source-control creates the worktree, knowsworktree_root, hasworktree_stale_days, and destroys the directory. Reaping at teardown stops these at the source.The safety boundary
Reap on a teardown this plugin performs, never on "a path that does not currently resolve". A project-scope record for a live repository on an unmounted network share or a detached external volume is indistinguishable from a dead worktree to a bare existence check, and destroying those records is real data loss.
Two further constraints:
claude plugin uninstall <id> -s project, never a direct edit ofinstalled_plugins.json(Claude Code internal state, not a published contract).-s projecthas no path flag — it acts on the literal cwd (verified 2.1.228, unchanged 2.1.240). A reap must therefore run while the directory still exists, i.e. before removal. If a reliable reap proves unachievable through the CLI, ship the detection-and-report half rather than something that silently half-works.Pre-existing orphans (the 108 above) are not reachable by a teardown hook that did not exist when they were created — reporting them from
status/auditis safe; reaping them is not, absent an explicit user action.Related
No linked issue.