Problem
merge-and-release step 7 refreshes this machine's Skills after a hub release by switching the hub checkout to main, fast-forwarding it, and running scripts/skills_install.py there. The procedure cannot be followed as written where that checkout is the primary checkout, and the obvious workaround has a side effect that breaks Claude Code's Skills.
-
The write guard refuses the step. git checkout main in the primary checkout is denied by gh-write-guard, which blocks mutating git operations against a primary checkout. The step names no alternative checkout, so the session has to improvise.
-
Running the installer from a worktree re-points Claude Code's marketplace. The natural improvisation is a linked worktree detached at origin/main. register_claude_marketplace() runs claude plugin marketplace add <ROOT> with ROOT being whichever checkout runs the script. When the projecttemplate-fleet marketplace is already registered from a different directory, the current claude CLI does not report "already added". It silently moves the source to the new directory:
Marketplace 'projecttemplate-fleet' was already added from dir:<primary checkout> and now points at dir:<release worktree>.
Claude Code then loads Skills from the temporary worktree, and removing that worktree during step 8's cleanup leaves the plugin pointing at a directory that no longer exists. The installer still exits 0 and prints Claude Code marketplace registered: True, and --report exits 0 too, since its exit reflects the snapshot alone, so nothing flags the move.
Reproduction (constructed)
- Register the marketplace from checkout
A by running python3 scripts/skills_install.py there.
- Create a linked worktree
B of the same repository at origin/main and run python3 scripts/skills_install.py from B.
claude plugin marketplace list --json now reports projecttemplate-fleet with path = B.
- Remove
B. The fleet plugin's source directory is gone.
Observed on 2026-09-28 while releasing 2.0.739. Running claude plugin marketplace add <A> pointed the source back at A before the worktree was removed, and --report then showed live back on A.
Expected
The refresh step works without mutating the primary checkout, and without moving Claude Code's registered source as a side effect. Options, recommendation first:
- (Recommended) Add an installer mode that refreshes only the Codex/opencode snapshot (for example
--snapshot-only) and leaves the Claude marketplace registration untouched. Then change step 7 to run it from a detached worktree at origin/main. The live channel already follows its registered checkout by design, so a release never needs to re-register it.
- Have
register_claude_marketplace() refuse, or warn and skip, when the marketplace is already registered from a different directory, unless an explicit flag asks to re-point it.
- Keep step 7 as is and document that it needs
GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT. This does not fix the silent re-point for anyone who runs the installer from a worktree for any other reason.
Options 1 and 2 are complementary: 1 fixes the procedure, and 2 stops the silent move whatever the entry point.
Scope
scripts/skills_install.py (register_claude_marketplace(), and a new mode if option 1)
.agents/skills/merge-and-release/SKILL.md step 7, and the generated distributions through scripts/build_dist.py
skill-lifecycle and docs/host-setup.md wherever they describe running the installer from a worktree
- A test covering the "already registered from another directory" case
Problem
merge-and-releasestep 7 refreshes this machine's Skills after a hub release by switching the hub checkout tomain, fast-forwarding it, and runningscripts/skills_install.pythere. The procedure cannot be followed as written where that checkout is the primary checkout, and the obvious workaround has a side effect that breaks Claude Code's Skills.The write guard refuses the step.
git checkout mainin the primary checkout is denied bygh-write-guard, which blocks mutating git operations against a primary checkout. The step names no alternative checkout, so the session has to improvise.Running the installer from a worktree re-points Claude Code's marketplace. The natural improvisation is a linked worktree detached at
origin/main.register_claude_marketplace()runsclaude plugin marketplace add <ROOT>withROOTbeing whichever checkout runs the script. When theprojecttemplate-fleetmarketplace is already registered from a different directory, the currentclaudeCLI does not report "already added". It silently moves the source to the new directory:Claude Code then loads Skills from the temporary worktree, and removing that worktree during step 8's cleanup leaves the plugin pointing at a directory that no longer exists. The installer still exits
0and printsClaude Code marketplace registered: True, and--reportexits0too, since its exit reflects the snapshot alone, so nothing flags the move.Reproduction (constructed)
Aby runningpython3 scripts/skills_install.pythere.Bof the same repository atorigin/mainand runpython3 scripts/skills_install.pyfromB.claude plugin marketplace list --jsonnow reportsprojecttemplate-fleetwithpath=B.B. The fleet plugin's source directory is gone.Observed on 2026-09-28 while releasing 2.0.739. Running
claude plugin marketplace add <A>pointed the source back atAbefore the worktree was removed, and--reportthen showedliveback onA.Expected
The refresh step works without mutating the primary checkout, and without moving Claude Code's registered source as a side effect. Options, recommendation first:
--snapshot-only) and leaves the Claude marketplace registration untouched. Then change step 7 to run it from a detached worktree atorigin/main. The live channel already follows its registered checkout by design, so a release never needs to re-register it.register_claude_marketplace()refuse, or warn and skip, when the marketplace is already registered from a different directory, unless an explicit flag asks to re-point it.GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT. This does not fix the silent re-point for anyone who runs the installer from a worktree for any other reason.Options 1 and 2 are complementary: 1 fixes the procedure, and 2 stops the silent move whatever the entry point.
Scope
scripts/skills_install.py(register_claude_marketplace(), and a new mode if option 1).agents/skills/merge-and-release/SKILL.mdstep 7, and the generated distributions throughscripts/build_dist.pyskill-lifecycleanddocs/host-setup.mdwherever they describe running the installer from a worktree