Context: #2597
Problem
source-control:worktree is the canonical owner of the parallel-session worktree convention for this plugin fleet, and the convention is stored where only this plugin can read it. A Claude Code session that does not load the plugin, a different agent (Codex, Cursor), a script, or a human typing git worktree add cannot discover it. The convention is therefore unenforceable by construction against most of the things that create worktrees.
Evidence
Measured on one machine by enumerating git worktree list --porcelain across all 19 canonical repositories: 292 linked worktrees across ten location conventions, and zero in the configured worktree_root.
| n |
Convention |
| 108 |
ghq flat sibling <repo>-<suffix> |
| 82 |
home loop root |
| 37 |
Codex tool-owned |
| 36 |
central dev-drive root, inside ghq root |
| 10 |
raw temp |
| 7 |
nested <repo>/.claude/worktrees/ |
| 6 |
nested sibling <repo>-worktrees/<branch> |
| 3 |
session scratchpad |
| 2 |
nested <repo>/.claude-worktrees/ |
| 1 |
Cursor tool-owned |
| 0 |
the configured worktree_root |
38 of those are attributable to tools the convention explicitly exempts. The other ~254 were created by things that had no way to read it.
Proposed change
Move the machine truth into a layer every consumer already has a reader for, and make the prose layers cite it rather than copy it.
| Layer |
Artifact |
Read by |
| Machine truth |
a namespaced git config key, e.g. melodic.worktreeroot |
anything that can run git config --get — humans, scripts, every agent, CI |
| Agent-readable prose |
AGENTS.md (cross-tool) and CLAUDE.md, both pointing at the git key rather than restating the path |
Codex, Cursor, Claude Code, future tools |
| Plugin consumption |
worktree_root resolves from the git key |
this plugin fleet |
Restating the path in three places is exactly the drift already on disk, so the prose layers must cite, never copy.
Namespace verification (git 2.55, git help --config, 1015 documented keys): Git itself defines exactly two worktree.* keys — worktree.guessRemote and worktree.useRelativePaths — plus core.worktree, gc.worktreePruneExpire, advice.worktreeAddOrphan, color.branch.worktree, maintenance.worktree-prune.auto, and the extensions.* group. Do not use worktree.*: Git owns it, useRelativePaths is a recent addition explicitly incompatible with older Git, and Git is extending includeIf into worktree semantics in 2.56.
Confirmed prior art for the vendor-section pattern, all reading placement from git config: ghq.root (multi-valued, last-wins, GHQ_ROOT overrides, ghq.<url>.root via --get-urlmatch), git-town.* (~45 keys), and k1LoW/git-wt's wt.basedir (--get-all, last-wins, 12-key namespace). Tools storing placement outside git config (gwq, worktrunk, wtp, git-worktree-manager, wtree, gh, jj) cannot collide. melodic.* collides with nothing.
Recommended shape, matching both precedents:
git -C "$p" rev-parse --git-dir >/dev/null 2>&1 || return # mandatory gate, see below
root=$(git -C "$p" config --get-all --type=path melodic.worktreeroot | tail -n1)
Multi-value last-wins lets an include append rather than override. --type=path expands a leading ~. A per-forge variant via --get-urlmatch mirrors ghq.<url>.root.
Two hazards that must be documented wherever this lands, both verified in a hermetic lab on git 2.55.0:
- Never pass a scope flag without
--includes. git-config(1): --includes "Defaults to off when a specific file is given (e.g., using --file, --global, etc) and on when searching all config files." A scoped read silently skips every include.
- Gate on
rev-parse --git-dir. Under dubious ownership (safe.directory), git -C <repo> config --get <key> returns the global default as though it were the repository's answer — rc=0, no stderr, --show-scope reporting global.
Acceptance criteria
- The worktree root is readable by a plain
git config --get from outside this plugin.
source-control:worktree resolves its root from that key.
AGENTS.md/CLAUDE.md reference the key rather than duplicating the path.
- The chosen key does not collide with Git's own namespace or with
ghq, git-town, or git-wt.
Context: #2597
Problem
source-control:worktreeis the canonical owner of the parallel-session worktree convention for this plugin fleet, and the convention is stored where only this plugin can read it. A Claude Code session that does not load the plugin, a different agent (Codex, Cursor), a script, or a human typinggit worktree addcannot discover it. The convention is therefore unenforceable by construction against most of the things that create worktrees.Evidence
Measured on one machine by enumerating
git worktree list --porcelainacross all 19 canonical repositories: 292 linked worktrees across ten location conventions, and zero in the configuredworktree_root.<repo>-<suffix>ghq root<repo>/.claude/worktrees/<repo>-worktrees/<branch><repo>/.claude-worktrees/worktree_root38 of those are attributable to tools the convention explicitly exempts. The other ~254 were created by things that had no way to read it.
Proposed change
Move the machine truth into a layer every consumer already has a reader for, and make the prose layers cite it rather than copy it.
melodic.worktreerootgit config --get— humans, scripts, every agent, CIAGENTS.md(cross-tool) andCLAUDE.md, both pointing at the git key rather than restating the pathworktree_rootresolves from the git keyRestating the path in three places is exactly the drift already on disk, so the prose layers must cite, never copy.
Namespace verification (git 2.55,
git help --config, 1015 documented keys): Git itself defines exactly twoworktree.*keys —worktree.guessRemoteandworktree.useRelativePaths— pluscore.worktree,gc.worktreePruneExpire,advice.worktreeAddOrphan,color.branch.worktree,maintenance.worktree-prune.auto, and theextensions.*group. Do not useworktree.*: Git owns it,useRelativePathsis a recent addition explicitly incompatible with older Git, and Git is extendingincludeIfinto worktree semantics in 2.56.Confirmed prior art for the vendor-section pattern, all reading placement from git config:
ghq.root(multi-valued, last-wins,GHQ_ROOToverrides,ghq.<url>.rootvia--get-urlmatch),git-town.*(~45 keys), andk1LoW/git-wt'swt.basedir(--get-all, last-wins, 12-key namespace). Tools storing placement outside git config (gwq,worktrunk,wtp,git-worktree-manager,wtree,gh,jj) cannot collide.melodic.*collides with nothing.Recommended shape, matching both precedents:
Multi-value last-wins lets an include append rather than override.
--type=pathexpands a leading~. A per-forge variant via--get-urlmatchmirrorsghq.<url>.root.Two hazards that must be documented wherever this lands, both verified in a hermetic lab on git 2.55.0:
--includes. git-config(1):--includes"Defaults tooffwhen a specific file is given (e.g., using--file,--global, etc) andonwhen searching all config files." A scoped read silently skips every include.rev-parse --git-dir. Under dubious ownership (safe.directory),git -C <repo> config --get <key>returns the global default as though it were the repository's answer — rc=0, no stderr,--show-scopereportingglobal.Acceptance criteria
git config --getfrom outside this plugin.source-control:worktreeresolves its root from that key.AGENTS.md/CLAUDE.mdreference the key rather than duplicating the path.ghq,git-town, orgit-wt.