Claude Code Agent Teams: Running Parallel Agents That Don't Collide
Anand B · 2026-07-31
Two agents editing the same checkout will eventually destroy each other's work, and it happens faster than you would guess. One switches branches while the other is mid-edit. One runs a build that rewrites a lockfile the other is reading. One commits a file the other was halfway through rewriting. None of that is a model failure, and no amount of prompting fixes it.
Claude Code agent teams fail on shared state, not on model quality. Isolation and queue discipline are close to the whole game, and both are cheap to set up.
The collision you get for free
Start 2 sessions in one clone and you have handed both of them the same working tree, the same .git/index, and the same current branch. Git maintains exactly one HEAD per checkout, so "which branch am I on" is a single global answer that either agent can change at any moment.

The failure is not usually a dramatic conflict. It is quieter: an agent reads a file, plans a change against what it read, and by the time it writes, the content has moved. Now you have an edit built on a file that no longer exists in that form. The tell is an error like "file has been modified since read" — which is the harness catching it. The dangerous version is when nothing errors at all, because the edit still applies cleanly to a file that now means something different.
Our own repo has a background committer that can commit, push, and switch branches mid-session, which turns this from "likely" into "guaranteed". The fix was the same one you would want anyway.
Give every agent its own worktree
git worktree creates a second working directory backed by the same object store. Same history, separate HEAD, separate files on disk, no second clone and no second fetch.

Three commands cover the whole lifecycle:
# Create an isolated tree + branch for one agent
git worktree add ../wt-agent-a -b agent/a
# See what exists (and what leaked)
git worktree list
# Clean up when the agent is done
git worktree remove ../wt-agent-a
Two things bite people here. A fresh worktree has no node_modules, no .venv, and no .build, so the first thing any agent does in a new tree is the install step; skipping it produces a failure that looks like broken code and is really a missing dependency. And a worktree that is never removed keeps its branch alive, so git worktree list growing without bound is the signal that your cleanup step is being skipped.
The cost is disk and a few hundred milliseconds per tree. That is why Claude Code worktrees are worth it only past a certain point: below two concurrent agents, isolation is overhead you are paying for nothing.
Worth being honest about the disk bill, because it is the objection people raise. The .git object store is shared, so you are not paying for history twice, but you are paying for a second checkout of every tracked file plus a second set of build artifacts. On our monorepo that is a few hundred megabytes per tree once node_modules and .build exist, and the Swift build directory dominates it. Four concurrent trees is fine on any modern machine. Forty is not, and that ceiling arrives long before the model's does.
Subagents are a different tool than parallel sessions
Claude Code subagents and separate sessions get conflated constantly, and they solve different problems.
A subagent runs inside one session, spawned through the Agent tool. It gets its own context window, does a bounded piece of work, and returns a result to the parent. Nothing lands on disk in parallel, so no isolation is needed. That makes subagents right for reading: sweep 40 files, summarise, hand back a conclusion. The parent's context stays clean because the file dumps never enter it, and that is the actual win — a search that would have cost 30k tokens of file contents in the main thread costs a paragraph.
Separate sessions each own a terminal, a tree, and a branch. That is what you want for writing, because two agents editing at once is exactly the case worktrees exist for.
One detail worth knowing before you delegate an edit to a subagent: a fresh subagent does not inherit the parent's file-read cache. If you hand it an edit task without telling it to read the target file first, its first write attempt fails with a "file has not been read yet" error. Tell it explicitly to read before it edits.
| Subagent | Separate session | |
|---|---|---|
| Context | Its own, discarded after | Its own, persistent |
| Writes to disk | Avoid | Yes, in its own worktree |
| Isolation needed | None | git worktree per agent |
| Best at | Reading, searching, summarising | Implementing, testing, shipping |
Cap the queue, and make claims explicit
Once more than one agent can pick up work, the work list becomes shared state, and shared state needs the same discipline as the working tree.

We split the roles. A scout runs on a cheap model and does nothing but find work and write it down. Executors run on a stronger model and do the work. The queue between them is a plain JSON file, and it is capped, because an uncapped queue grows faster than any fleet can drain it and you end up with a backlog nobody will ever read. Twenty items is plenty; past that, the oldest entries are stale anyway.
A minimal entry needs less than you would think:
{
"id": "q-0031",
"task": "Add a regression test for the token refresh 401 path",
"file": "services/accounts-api/server.py",
"claimed_by": null,
"status": "open"
}
That claimed_by field is the one that matters, and it exists because of a specific failure. Two executors started within a few seconds of each other, both read the queue, both saw the same top item, and both did the work. We got two pull requests for one task, each with a plausible but different implementation. Reviewing them cost more than the task did.
The fix is that an executor writes its own identifier into claimed_by and commits that before starting, then treats any item with a non-null claim as invisible. Cheap, boring, and it ends the whole class of problem. If your agents can't commit a claim, timestamped lease files in a directory work the same way: creating a file is atomic.
Everything merges through one gate
Isolation buys you parallelism, and it costs you integration. Three agents on three branches means three sets of changes that have never met.
Point them all at one integration branch rather than at main, and put the gates on that branch: typecheck, build, whatever test suite is real for the folder in question. Each agent's own branch only has to be individually sane; the integration branch is where you find out whether the changes are collectively sane, which is a different question and the one that actually breaks.
Keep the gate specific per folder. In our monorepo the web app's gates are npm run typecheck and npm run build because there is no web test suite to run, while the Swift package runs swift test and the Go sidecar runs go test ./.... A gate that claims to test something it doesn't is worse than no gate, because it produces confident green.
Two agents, then decide
Set up 2 agents with 2 worktrees and one integration branch before you build anything resembling a fleet. Two is enough to expose every problem in this article: the collision, the duplicate claim, the missing install step, the integration surprise. It is also small enough that you can watch all of it happen, which is how you learn which of these failures your setup actually has.
Most people find their honest ceiling is 3 or 4 concurrent agents, and it is set by review throughput rather than anything technical. Each agent produces a branch someone has to read, and an agent whose output nobody reviews is not producing work, it is producing a queue. When you notice unreviewed branches accumulating faster than you close them, the answer is fewer agents with tighter gates, not more agents. Adding a fifth git worktree does not help if the bottleneck is the person at the end.
If you are still working out the single-session basics, the Claude Code workflow piece covers what to get right before adding a second agent.