Directing agents
Worktrees: give every parallel stream its own directory.
A worktree is a second working directory backed by the same repository. It's how you run more than one stream of work — yours, an agent's, a review — without them editing the same files underneath each other, and without the cost or drift of a second clone.
git worktree add -b fix-auth ~/code/.worktrees/example-myrepo/fix-auth origin/main Persistent lane from a stated basegit worktree list Every worktree and its branchgit worktree remove ~/code/.worktrees/example-myrepo/fix-auth Delete it when the work landsgit worktree prune Clean up after a manually deleted oneWhat a worktree actually is
Normally a repository has one working directory. Switching branches with git switch rewrites the files in place — the same folder becomes a different version of the project. That's fine when you do one thing at a time, and it's exactly the problem when you don't.
A worktree gives you another directory checked out to another branch, sharing one repository underneath. Not a copy — the commits, branches, tags, and stashes are the same objects. A commit made in one worktree is visible from all of them immediately, because there is only one history.
| vs. switching branches | Both branches exist on disk at once, in different folders. Nothing is rewritten underneath a running process |
| vs. a second clone | One history, not two. No second git fetch, no pushing between copies, no chance of the clones drifting apart |
| vs. a stash | The other work stays checked out and runnable, not parked in a stack you have to remember to pop |
Why this matters for agents
One checkout is a single shared mutable resource. Two writers on it produce failures that look like bugs in your code:
- Interleaved edits. Two agents editing the same file land a mix of both attempts. Neither one's tests describe what's actually on disk.
- The tree moving mid-task. One agent runs
git switchorgit stashwhile another is halfway through a build. The second agent's files change for reasons nothing in its context explains. - Unreviewable diffs. Two unrelated changes on one branch have to be reviewed, and reverted, together.
Worktrees make the isolation physical. Each stream has its own directory and its own branch, so "don't touch each other's work" stops being an instruction an agent has to remember and becomes a property of where it is running.
A workstream is one independently reviewable change. It gets one named branch and one worktree, and moves committed → pushed → draft PR → merged or abandoned → worktree removed → branch deleted.
Reading a repository that nobody is currently changing needs neither — just read the files. Several tightly related edits that will be reviewed together are one workstream, not several.
Reviewing an active change is different: a writer is moving the files under you, so you can end up reporting findings against a state that never existed as a single diff. Review after the writer commits, and for a genuinely concurrent review use a detached worktree pinned to the commit under review — git worktree add --detach ../myrepo-review <sha>.
The lifecycle
$ git fetch origin # make the base current first
$ git worktree add -b fix-auth ~/code/.worktrees/example-myrepo/fix-auth origin/main
$ cd ~/code/.worktrees/example-myrepo/fix-auth # work here as normal
$ git worktree list # see them all, with branches
$ git worktree remove ~/code/.worktrees/example-myrepo/fix-auth # when the PR is merged
$ git fetch origin # let your local base see the merge
$ git branch -d fix-auth # then retire the branch
That last argument — origin/main — is the commit the new branch starts from. Leave it off and Git uses whatever the current worktree happens to have checked out. Run it from an old feature branch and your new lane silently inherits that feature's commits, which then show up in its diff and its pull request.
This matters more with agents than without: you often create the worktree from wherever your shell already was, and nothing in the agent's context can reveal a wrong starting point. State the base every time.
The same applies to cleanup. git branch -d refuses when your local base branch hasn't yet observed the merge — the work is safely on the remote, but your clone doesn't know. Fetch first; if you then still need -D, confirm the commits really landed before forcing it.
For persistent agent work, use <configured-root>/.worktrees/<unique-repo-slug>/<lane>. With Groundwork's default repository root, one example is ~/code/.worktrees/example-myrepo/fix-auth. The repository slug should be stable and unique — use owner-project or another unambiguous name when common basenames such as api, docs, or web could collide. The path stays outside the main checkout, survives reboots, remains within groundwork-repos discovery, and makes the repository and lane obvious. This layout fits Groundwork's default max_depth; increase it if a customized repository-discovery depth no longer reaches the lane. If you changed the roots in ~/.config/groundwork/repos.conf, put .worktrees under one of those roots instead.
Never nest a linked worktree inside its main checkout: it appears as untracked content in the parent and invites an agent to commit one working directory into another. A location under /private/tmp is reasonable for proof you will discard in the same task, but it is normally outside configured roots, so Groundwork's cross-repository picker and dirty-work supervision will not see it.
To read an old revision without disturbing any branch, use a detached one and delete it after: git worktree add --detach ~/code/.worktrees/example-myrepo/review-v1.4.0 v1.4.0.
What surprises people
| Untracked files aren't shared | node_modules, .venv, .env, and build output do not come along. Each worktree needs its own install — that's the real cost, and why worktrees suit parallel work rather than quick one-offs |
| One branch, one worktree | Git refuses to check out a branch that's already checked out somewhere else. That's a safety feature, and it's why git branch marks those with + |
.git is a file here | In a linked worktree it's a small file pointing at the real repository, not a directory. Tools that test for a .git directory mis-detect worktrees |
| Deleting the folder isn't enough | rm -rf on a worktree leaves the repository still believing it exists. Use git worktree remove, or run git worktree prune afterward |
| Removal protects your work | git worktree remove refuses while a worktree "contains modified or untracked files" — untracked counts, so a stray scratch file blocks it too. If you're sure, --force — but read the diff first |
groundwork-repos lists linked worktrees alongside ordinary clones and marks them wt, because a worktree you've forgotten about is exactly the one holding uncommitted agent work. Set include_worktrees = false in ~/.config/groundwork/repos.conf if you'd rather not see them.
How many agents should be writing?
Worktrees make parallel agents possible. That is not the same as making them a good default. Every additional writer adds a merge to resolve and a diff you have to review, and review is the part that doesn't parallelize — it's still your attention, and it's the bottleneck.
One task, one branch, one worktree, one writing agent. Additional agents may read — review a diff, investigate a failure, check a claim — but only one writes at a time.
Agents do not spawn their own sub-agents to divide work. If a job genuinely needs to split, you split it, and you decide what each part may touch. Nested delegation is not the default; it needs your explicit approval and a plan you've seen.
The reason isn't timidity. A writing agent that spawns writing agents produces a diff that no single context — including yours — ever saw whole. You end up reviewing the output of a process you didn't design, which is the exact position agentic engineering is meant to keep you out of.
Two more rules that only bite once you have parallel streams:
- Never run two writing agents in one worktree at the same time. Claude Code and Codex running concurrently in one directory will overwrite each other's edits, and neither will report it, because neither can see the other's writes. Concurrent agents need separate worktrees. Handing work sequentially to a different agent in the same worktree is fine once the first has committed and stopped.
- Hand off through commits, not through descriptions. When one stream's work feeds another, the handoff is a pushed branch and a written statement of what was verified — not a summary in chat. The next agent starts from what's actually on disk.
When you do scale up: lanes and an integrator
Larger repositories do run several agents at once — Groundwork's platform-monorepo sibling, Roost, is the case this model was written for. The shape that holds:
| One integration owner | A single agent (or you) owns merging into the main branch. Lane agents never merge each other's work |
| Lanes with explicit writable paths | Each lane gets a worktree and a stated list of directories it may write. Overlapping lanes are a scheduling error to fix before starting, not a merge conflict to resolve after |
| Reviewers are read-only | A reviewing agent reports findings. It does not fix what it finds — otherwise its review and its edits land in one unreviewed diff |
| No nested spawning | Lane agents don't create more lane agents. The lane list stays something you can hold in your head |
Every rule exists to keep one property true: each lane has one accountable writer and a separate reviewer, and you can name both. If a workflow can't tell you who is answerable for a change, it's too parallel — regardless of how many worktrees are involved.
That is deliberately about accountability, not literal Git authorship. An integration owner will sometimes resolve a conflict, repair a break that only appears once lanes merge, or apply a correction a reviewer asked for. That's normal and necessary; what matters is that those edits are attributed as integration work rather than folded silently into someone else's lane.
Put it to use
Make one, prove it's shared
In any repo, run git worktree add -b scratch-test ../$(basename $PWD)-scratch. Commit something small in the new directory, then run git log --oneline -1 scratch-test from the original one. The commit is already there — one history, two directories. Clean up with git worktree remove and git branch -D scratch-test.
Hit the guardrail on purpose
From the second worktree, try git switch main while main is checked out in the first. Git refuses and names the worktree already holding it. Knowing this error means recognizing it instantly when an agent reports it.
Give an agent its own
Next time you hand an agent a bounded task, create the worktree yourself first and tell it the path. Then keep working in your main checkout while it runs. Review its branch as a diff, the same way you'd review anyone else's — that's the workflow worktrees are for.
Go deeper
- git-worktree documentation: the authoritative reference for every subcommand and flag.
- Git: branches, commits, and the daily loop these build on.
- AI agents & rules: what to put in an agent's instructions before you hand it a worktree.
- Repo navigation: finding the worktrees you've already made.