Skip to content

source-control: the worktree convention lives where only this plugin can read it, so nothing else on the machine can honor it #2610

Description

@kyle-sexton

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions