Worktree coordination
All managed worktrees live under one machine-wide umbrella:
~/Sites/.worktrees/
codex/<project>/<task>/
claude/<project>/<task>/
manual/<project>/<task>/
.control/This page is canonical. ~/Sites/WORKTREES.md symlinks here so people and both harnesses read the same policy.
The umbrella coordinates paths; it does not share working directories. Codex and Claude never write to the same worktree. Each task owns one branch, worktree, and harness namespace.
Why the namespaces matter
Git already records every worktree in the repository's common Git directory, regardless of where it lives. Putting paths under one umbrella makes that state inspectable across harnesses without making either harness responsible for the other's lifecycle.
The namespace answers who created the tree:
codex/is owned by Codex Desktop or Codex CLI work.claude/is owned by Claude Desktop or Claude Code work.manual/is for deliberate human-created worktrees..control/contains shared hooks or scripts, never project work.
Existing worktrees outside the umbrella are grandfathered. Don't move or remove them solely to make the directory tree look tidy; finish or recover their work first.
Machine configuration
Codex
Set the root-level Codex Desktop worktree settings to:
[desktop]
git-worktree-root = "~/Sites/.worktrees/codex"
worktree-auto-cleanup-enabled = false
worktree-keep-count = 15
worktree-upstream-refresh-mode = "best-effort"Use the absolute expanded path if the settings UI or installed Codex version does not expand ~. The Desktop Worktree root field is the root setting: it applies to managed Codex worktrees for every saved project on that macOS profile.
Rotated Codex accounts on one macOS user share ~/.codex/config.toml. If a launcher uses another Codex home, symlink its config to that file.
Claude Code
Set worktree.baseRef to fresh and use a global WorktreeCreate hook that creates the target under ~/Sites/.worktrees/claude/<project>/<task>. The hook should:
- resolve the repository's common Git directory rather than assuming the current directory is the primary checkout;
- fetch and prune
originwithout prompting; - start from
origin/HEAD, falling back toorigin/main,origin/master, thenHEAD; - fail if the target path or branch already exists;
- copy only explicitly ignored files selected by
.worktreeinclude.
Keep the hook in ~/Sites/.worktrees/.control/. Every rotated Claude account home should symlink its settings.json to the canonical ~/.claude/settings.json, so new accounts cannot silently lose the hook.
Claude Desktop
Set the Worktrees custom root to ~/Sites/.worktrees/claude. Disable age-based archive cleanup and automatic archive-on-PR-close. Claude Desktop and Claude Code share the namespace but still create distinct task directories and branches.
Starting work
Before creating a tree:
- run
git worktree list --porcelainfrom any checkout of the repository; - inspect
git status --shortin the intended source checkout; - fetch and prune the remote;
- choose a unique task name and harness-prefixed branch, such as
codex/current-baselineorclaude/auth-cleanup; - create the tree from the freshest trustworthy base.
One task owns one branch and one worktree. A subagent may work inside its assigned tree, but branch, worktree, stash, and cleanup operations stay with the coordinating session.
Handoffs and stashes
A stash is emergency transport, not durable task state. Prefer a focused commit and push when work must survive a session or move between harnesses.
A handoff records:
- worktree path;
- branch and current commit;
- owner or harness;
- clean or dirty state;
- validation already run;
- exact remaining work.
Never apply or drop a stash you did not create until its repository, base commit, files, and owner are understood.
Cleanup
Automatic deletion stays off. A worktree can be removed only when all of these are true:
- its owner is known and finished;
git status --shortis clean;- its branch is merged, intentionally abandoned, or safely pushed;
- no stash or untracked artifact depends on it;
- no live process or agent session is using its path.
Then remove it through Git, delete the local branch only when safe, and run git worktree prune. Never use age, directory count, or a merged pull request as the only deletion signal.
Audit commands
git worktree list --porcelain
git branch --format='%(refname:short) %(worktreepath) %(upstream:short) %(upstream:track)'
git stash list --date=localRun these per repository when tidying. The Git registry is authoritative; the umbrella directory is the human-readable map.