Back to Insights
21 Jun 20265 min readAI & LLMs

You're Running Five Agents on One Repo. Who Stops Them Overwriting Each Other?

Sometime in the last year the default way to use a coding agent quietly changed. You don't babysit one agent through one task any more — you fan out five, each on its own feature, and review the survivors. We run a fleet like this every day. It is genuinely faster. It also breaks in a way that nobody warns you about until it has already eaten an afternoon.

Here is the honest version of the problem, because the marketing version is wrong.

The marketing version vs. what actually happens

The pitch you'll read on a dozen tool landing pages is: "two agents in the same folder silently overwrite each other's files." We went looking for a real developer with that war story and couldn't find one. That specific claim is mostly vendor copy.

What we did find is more interesting, and worse.

One. Isolation is now table stakes — and it still loses work. An issue on Anthropic's own claude-code repo (#55724) documents it cleanly: "Tested with 13 parallel agents: 5 committed successfully, 8 failed due to lock contention." Even with every agent in its own git worktree, the worktrees share one .git/ directory, so they fight over .git/index.lock. The agent whose commit loses the race exits — and the worktree auto-cleanup then deletes its work permanently. Isolation didn't prevent the collision; it just moved it into git's plumbing.

Two. Developers forget to isolate, and the agents don't always do it for them. From Hacker News, a real practitioner (aubanel): "I want to work on several things in parallel through several agents, always forget to do worktrees, then the different branches of work tend to step on each other." Another: the orchestrator agent "sometimes uses git worktrees, sometimes not." The safety rail is opt-in and unreliable.

Three — and this is the one that matters most — even when isolation works perfectly, the conflict doesn't disappear. It just gets deferred to merge time. As one commenter put it, the real bottleneck "isn't human review per se, it's unstructured review." Two agents on two clean worktrees both edit the central router file. Neither knows. You find out twenty minutes and a few thousand tokens later, at the merge, when it's most expensive to untangle.

Simon Willison, who runs this workflow deliberately, frames the ceiling honestly: the limiter isn't the agents, it's "I can only review so fast." Worktrees and containers protect your blast radius. They do not tell you that agent C is about to redo the same twelve lines agent A just touched.

The cheap question nobody asks early enough

So here's the thing we kept coming back to: at merge time, git computes whether two branches conflict. That computation doesn't need the merge to have happened. It doesn't even need a branch. You can ask git the question — would these two sets of changes conflict? — at any moment, for free, before either agent has committed.

The tool is git merge-tree, in its modern form (Git 2.38+):

git merge-tree --write-tree --name-only <ref1> <ref2>

It runs a real three-way merge in memory — auto-detecting the merge base, using git's actual merge engine — and writes the result to the object store. It touches no working tree, no index, no HEAD. It creates no commit. It just answers:

  • exit 0 → the merge is clean.
  • exit 1 → conflict, and stdout lists the conflicting files.
  • exit > 1 → a real error (bad ref), not a conflict. (Branch on the exit code; don't treat "non-zero" as "conflict.")

We verified every branch of that on Git 2.50.1. The result worth internalising is this one:

# agentE edits line 1; agentF edits line 5 of the SAME file
$ git merge-tree --write-tree --name-only agentE agentF ; echo "exit: $?"
edee7ce86fe24019e8ea6e1af8831e79cc7b38c9
exit: 0          # CLEAR

Same file, both edited — clean. A naive guard ("did both agents touch router.ts?") screams conflict here and is wrong most of the time. merge-tree does the real line-level diff3, so it only flags genuine overlap. It is exactly as smart as git's own merge, because it is git's own merge.

The one subtlety that makes it actually work

There's a catch, and it's the whole game: merge-tree only reads committed refs. It is blind to the index and the working tree. Point it at an agent mid-edit and it cheerfully reports the last commit's state and ignores everything uncommitted.

So to preflight an agent's in-flight changes, you first snapshot them into a throwaway, unreferenced commit — without disturbing the real index, HEAD, or checkout:

tmpidx="$(mktemp)"
GIT_INDEX_FILE="$tmpidx" git read-tree HEAD
GIT_INDEX_FILE="$tmpidx" git add -A
cand_tree="$(GIT_INDEX_FILE="$tmpidx" git write-tree)"
rm -f "$tmpidx"
candidate="$(git commit-tree "$cand_tree" -p HEAD -m preflight-candidate)"
# now: git merge-tree --write-tree --name-only "$candidate" <other-agent-ref>

That candidate commit is reachable from nothing and gets garbage-collected. Get this step wrong and your hook silently checks stale state — which is worse than no hook, because it lies to you.

The whole thing is 55 lines

We wrapped it as preflight, test-first, five cases (clean / different files, same-file-different-lines = CLEAR, same-file-same-lines = CONFLICT, dirty-working-tree snapshot, bad-ref-is-an-error). All green. The 40-second demo above shows all three checks running against four "agent" branches of one file.

As a PreToolUse hook on Write/Edit, it becomes a 200-millisecond gate: before an agent's patch lands, snapshot it, preflight it against the integration branch (or each sibling worktree's HEAD), and if it would conflict, hand the agent the conflicting file list now — while it still has the context to route around it — instead of at merge time.

What this is, and isn't

We're not selling you anything. This is one git merge-tree call with a careful snapshot in front of it, and we're showing the working code because the mechanism deserves to be better known.

Prior art is real and worth crediting: Clash surfaces cross-worktree conflicts during development (closest in spirit to this); Conductor, Crystal, uzi, and vibe-kanban orchestrate worktree-per-agent; container-use isolates each agent in its own container. They solve isolation. This solves the narrower, earlier question: would it merge cleanly right now?

And the honest limits: preflight catches textual conflicts — the same class git itself would flag — and nothing more. Two agents that rename the same variable in different files, both merging cleanly but logically fighting, sail through CLEAR. It tells you when git's auto-merge would break, which is the right gate for "should this patch land now." It does not read intent. Nothing this cheap does.

But for the failure mode that actually burns fleet time — two agents quietly converging on the same lines — the answer was sitting in git's plumbing the whole time. You just have to ask before, instead of after.

We build and run autonomous agent fleets in production. If you're wrestling with multi-agent coordination, talk to us.


Get the tool → preflight in agent-ops — open source (MIT), a single dependency-free script, part of our toolkit for running coding agents in production.

AI agentsgitworktreesfleetconcurrency

Found this useful?