Git & GitHubLesson
Git worktrees
Several agents editing one checkout at once trip over each other: one switches the branch under another, or commits another's half-finished files. A git worktree gives each agent a working folder of its own, while they all share one repository.
Know git worktrees already? Skip to In Terrarium.
Outside the box · General
The concept
A worktree is a second working folder of the same repository, with its own files and its own checked-out branch, sharing one history. git worktree add <path> <branch> creates one, git worktree list shows them all, and git worktree remove <path> deletes one. Two worktrees can't check out the same branch at once.
A worktree is a fresh checkout, so it has none of the main folder's untracked files, such as installed dependencies. Uncommitted work in it shows only in that folder's git status, and deleting the folder deletes that work.
Claude Code can give each subagent a fresh worktree. It creates the worktree under .claude/worktrees/, from the default branch unless the settings say to branch from the current HEAD, and removes it when the subagent finishes without changes.
Full referenceGit docs: git-worktree
In Terrarium
The dispatch , .agents/skills/dispatch-subagents/SKILL.md, tells apart the ways a worktree appears here:
18- **A dispatched subagent that touches git:** pass `isolation: 'worktree'` on the
19 Agent call. Without it, parallel agents share one checkout and race on
20 branches.
21- **`EnterWorktree`/`ExitWorktree`** moves your whole session. Use only when the
22 user says "worktree".
23- **Manual `git worktree add`** is for your own one-off inspection, never a
24 subagent's brief. Put it under `.claude/worktrees/NAME/`: path-scoped rules
25 don't load for files outside the project folder (issue #1944).
Each ends up under .claude/worktrees/, inside the project folder, which .gitignore leaves out of git. A subagent's brief has it run pnpm install in the new worktree and check that its HEAD is the commit the brief names.
Worktrees are swept before a session ends. pnpm check:worktrees and the session-log script's close step both run scripts/git/check-worktrees.ts, which lists every worktree with git worktree list and runs git status in each. The rule it applies:
159 const uncommitted = s.dirty
160 const unpushed = (s.unpushedCount ?? 0) > 0
161 const needsRescue = !s.isPrimary && (uncommitted || unpushed)
The main folder never fails. A linked worktree fails on any uncommitted change, untracked files included, or on a commit that no remote-tracking branch holds. Cases that hold nothing to rescue, such as a worktree whose HEAD is already in origin/main, are reported but excused; the script's EXCUSALS table lists them.
When a session closes, scripts/session/log-session.ts runs the same sweep. It removes each clean linked worktree whose HEAD is already in origin/main, except the one it runs in or one sitting at the origin/main tip, and it refuses a completed or partial log while another linked worktree holds work to rescue. For any other status it records a Friction instead.
The session log guards worktrees too: unless told otherwise, scripts/session/log-session.ts won't write a log from a linked worktree, which it tells by git's two directories:
208export function isLinkedWorktree(cwd: string): boolean {
209 try {
210 const gitDir = git(['rev-parse', '--git-dir'], cwd).trim()
211 const commonDir = git(['rev-parse', '--git-common-dir'], cwd).trim()
212 return resolve(cwd, gitDir) !== resolve(cwd, commonDir)
213 } catch {
214 return false
215 }
216}
A subagent there shares its parent's session, and a log it wrote would replace the parent's (Subagents).
Why it's built this way
- One checkout per agent. Parallel agents in one checkout raced on branches. A worktree per agent that touches git ends the race. The cost: each worktree needs its own install. .agents/skills/dispatch-subagents/SKILL.md.
- A pinned start. A new worktree can start from an older
main, so an agent that trusted it would build on stale code. Checking the commit before work starts closes that gap. The cost: one more step in every brief. .agents/skills/dispatch-subagents/SKILL.md. - Inside the project folder. Path-specific rules load only for files under the project folder, so an agent in a worktree beside the repo edited files with none of their rules loaded. Hand-made worktrees go under
.claude/worktrees/, where Claude Code already puts a subagent's. The cost: the folder must stay git-ignored. .agents/skills/dispatch-subagents/SKILL.md. - A sweep for lost work. "Commit before you stop" as an instruction failed: one agent ended its turn waiting on a background check, then died silently once resumed. An instruction can't catch an agent that never returns, so a script reads git's own list of worktrees and fails on left-behind work. It shows the loss; it can't prevent it. scripts/git/check-worktrees.ts.
What to watch for
- Make your own under
.claude/worktrees/. A worktree outside the project folder misses the project's path-specific rules when an agent reads its files. - A fresh worktree has no
node_modules. Runpnpm installthere beforepnpm gate:scopedor any test. - A squash-merged worktree with leftover changes still fails the sweep. Its
HEADnever reachesmain, so the leftovers aren't excused; remove the worktree once its PR merges.
Try it in your project
Add .claude/worktrees/ to your .gitignore. Run git worktree add .claude/worktrees/try-it -b try-it, change a file there, and run git status in both folders: each sees only its own change. Then run git worktree list, and remove the extra folder with git worktree remove .claude/worktrees/try-it.
Where next
- LessonSubagents
- LessonPath-specific rules
- LessonPull requests
- LessonSession logs
- LessonCommit and PR attribution
Walkthroughs through this Lesson
- WalkthroughAsking an agent for a change
Last updated · Latest source on GitHub ↗