Running Claude Code agents in parallel: what git stops, and what it doesn't
Two windows is the cheapest speedup there is. git keeps your files apart. Nothing keeps your decisions apart.
Vondet / 2026-09-06 / 9 min read
Last month, answering one question took at least twelve agent sessions: if you add a single line to a CLAUDE.md, does Claude Code behave differently? Nine of them produced a result we could count — controls, treatments, and then the treatments again after we found a mistake. The rest were lost to the collisions this article is about. We did not run them one after another. We ran them in groups, several at a time, and the whole thing fit into a day.
That is the ordinary case for running Claude Code agents in parallel, and it is the cheapest speedup available right now. You don't need a bigger model or a better prompt. You need a second window, and something for it to do while the first one is waiting.
The reason is waiting.
An agent that is running a test suite, installing dependencies, or reading forty files is not asking you anything. For the whole of that stretch, your hands are free. A second session turns that stretch into review time on a branch that already exists.
Below: four things that make parallel agents work, and then the one problem that survives all four. None of the four are ours — git worktree, split roles and multiplexed panes are what people already do, and they work. The interesting part is what is left over afterwards.
Four things that make parallel agents work
1. Split the working tree with git worktree
Multiple agents editing the same checkout is the obvious failure, and git worktree is the obvious fix. One command gives you a second directory, on its own branch, sharing one .git:
git worktree add ../myproject-review -b review-auth
Now the second session has its own files. Nothing it writes can surprise the first session mid-edit.
One thing to check the first time. git worktree add <path> with no commit-ish branches from your current HEAD — but if you are using a tool that creates the isolated tree for you, its default may not be that. Ours branched from the repository's default branch rather than the commit we were sitting on. Everything we had just staged on the branch was simply not there, and the session ran happily without it, because nothing is wrong with an old tree — it is a valid tree.
We nearly published an inverted result because of it. In the experiment above, the change we were testing lived on the branch; the isolated trees came from the default branch; both “treatment” runs therefore ran with no treatment at all. We had already written up the result before we checked where the tree had branched from.
So after you create it, look:
git -C ../myproject-review log --oneline -1
Two seconds, and it is the difference between a result and the opposite of a result.
And know what a worktree does not split. It splits files. It does not split your database, your dev server's port, your .env, your caches, your rate limit, or any service the two sessions both talk to. In our own case one session correctly worked out, on its own, that writes to a shared external service were not isolated by the worktree at all — git was isolated, the shared state was not. Before you start, count the things both sessions touch that are not files.
2. Give the sessions different jobs
The pairs that work best are not two implementers. They are asymmetric:
- one implements, one reviews the diff it just produced
- one implements, one reads the codebase and answers questions
- one implements, one writes the tests
A research or review session is read-only, which means it does not need its own worktree at all, and on disk it cannot collide with anything. (It still shares your rate limit and any external service, as above — read-only buys you file safety, not isolation.) That is the cheapest second session there is, and it is where I would start.
3. Decide the file boundary before you start — and don't make it conditional
Write down, before you launch the second one, which paths each session may touch. If they overlap, one of them becomes a read-only job instead.
The part that took us a while to learn: make the boundary categorical, not situational. We have a rule that one repository never writes into another, and it is written as “never,” not as “not while something else is running.” The reason is that we have no way to detect whether something else is running. A boundary you can only apply when you know the other side's state is not a boundary — it is a hope. If you cannot see the other window, “I'll be careful when it matters” is not available to you.
4. Multiplex your windows
tmux panes, terminal tabs, split editors — whatever you already use. The point is not the tool. The point is that a parallel session you cannot see is a parallel session you will forget is running, and the one you forget is the one that commits something at the wrong moment. Keep both visible, or keep a habit of checking both.
What the four don't fix: two sessions collide on a conclusion, not on a line
Here it is:
git stops file collisions. Nothing stops decision collisions.
The claim here is a narrow one: the four practices protect files, and the thing that keeps costing us afternoons is not files.
Worktrees, branches, merge conflicts, locks — all of that machinery exists to protect bytes. It is very good at it. If two sessions edit the same line, you find out. You will always find out.
But two sessions do not usually collide on a line. They collide on a conclusion.
One decides the retry lives in the client; the other decides it lives in the server. One decides the config key is timeout_ms; the other decides it is timeoutMs.
Both branches are internally consistent. Both merge cleanly. Nothing turns red.
You find out in review, or later.
Here are two that happened to us, both while running agents in parallel in our own repository.
One session deleted its own work because of another's. We ran three sessions at once in the same working tree (this was before we split them, and it is the reason we split them).
One of them noticed the files another one had produced, concluded the work was already done, and deleted its own draft.
Nothing was corrupted. Git had nothing to report.
The session did something reasonable with a fact that was true and was none of its business.
One session committed and destroyed the other's premise. In another round, we committed the first group's output first.
The second group's job had been to do that same work — which, at the moment we committed, no longer existed to be done. Three runs were void.
Again: no conflict, no error, no file damaged. What broke was the premise the other side was standing on.
Both of these are the same shape, and it is a shape that arrives the moment you go from one session to two:
Each side acts on what it can see, and what it can see includes the other side's output but not the other side's reasoning.
Write the decision where the session running right now will get it
The fix is not complicated, but it has to be aimed at the right place. Ask a narrower question than “where do I document this?” Ask: if I write it here, does the session that is running right now get it?
| Where you write it | Does the other session, running right now, get it? |
|---|---|
| The conversation you're in | No. This is the one people forget. It feels like communication and it reaches exactly one session. |
| A file in the repo | Only if the other side goes and reads it — and it reads whatever version its tree has, which may not be yours. |
| A commit message | Yes, once you commit — and sometimes without being asked. See below. |
| The instruction file (CLAUDE.md) | On the next session's start. For a session already running, assume no. |
| Shared external state (a database, a service) | Immediately — which is also why a worktree does not protect you from it. |
Two of those rows are worth expanding.
Commit messages reach further than you intend. We know this because we did it by accident. During the experiment, we wrote what the experiment was about into the commit message.
A session that was supposed to be a naive subject read git log, worked out that it was inside an experiment, and told us so. That spoiled the run — but it is also the only time we have watched something reach a session that was already running, without anyone pointing it at it.
If you are looking for the shortest path from “I decided X” to “the other window knows X,” it is the commit message of the change that embodies X. Write the reason there, not just the what.
(What a new session already knows before you paste anything is the other half of this, and we wrote it up separately — it is linked at the foot of this article. This one is only about the window that is open right now.)
Editing the rules mid-run may not reach a running session. We have measured the adjacent case: our own plugin's instructions turned out to be fixed at the moment the session loaded them. We updated the source, the update installed correctly on disk, and the running session kept using the old text — reopening it in the same session reported “instructions unchanged.” We have not run the same test on CLAUDE.md itself, so treat this as the neighbouring result rather than proof. But the safe assumption for parallel sessions is: editing the rules does not reach the sessions that are already running. If you change the rules, restart the other window.
(Whether a rule that did arrive gets followed is a different failure, and we measured that one — also linked below. The cause was not memory.)
And two habits that are worth more than the table:
Read before you write, when the place is shared. Anything both sessions write to needs a way to see what is already there before you replace it. We shipped a tool of our own that could be written but not read back, and writing replaced everything — which means, with two sessions, the second one silently destroys the first one's entry, and there is no way to even notice. If a shared surface has no read, it is not a shared surface. It is a race.
Write the reason next to the thing the other side is about to touch. A reason recorded somewhere only you look is not a channel.
Try it on your own two sessions
Concretely, before you launch the second one:
- 1. Write down the paths each session may touch. If they overlap, make one of them read-only (review, research, tests). Do this in one line, in writing, not in your head.
- 2. Count what is shared and isn't a file — database, ports, .env, external services, caches. Worktrees do not split any of them.
- 3. If you use an isolated tree, check its branch point immediately: git -C <tree> log --oneline -1, and compare it to the one you meant. Do this the first time you use any new isolation tool, once.
- 4. Put every decision in a commit message, with the reason. Not “fix retry” — “retry moves to the server, because the client has no way to know if the write landed.” That sentence is the thing the other window needs.
- 5. When you merge one side, say so to the other side. The premise it started from has changed. A running agent will not notice; it will keep building on a world that no longer exists.
Steps 1 and 5 are the ones that would have saved both of our incidents. Neither of them is a tool.
What this doesn't show
We did not measure the speedup
Our two incidents are two incidents
One repository, one model family
We have not tested CLAUDE.md re-reading
What would break this
- If two sessions are given genuinely disjoint work — separate services, no shared config, no shared decisions — none of this bites, and you should just run them and not think about it.
- If writing decisions into commit messages does not change anything, because the other session still does not read them, then the problem is not the channel and this article is aimed at the wrong thing. That is testable in an afternoon and we would like to know.
If you want to go further
Look at that table again with a clock in your hand.
Every row you would deliberately write into delivers after the fact — you write, you commit, and at some later moment the other side happens to look. (One row does arrive immediately: shared state. Nobody set that up as a channel.)
But a decision exists before the change that carries it does. You decide at 14:03 that retry moves to the server; the commit that says so exists at 15:20. The entire window in which the other session can build on the wrong premise is the window before that commit — which is exactly the window in which both of you are running.
So what is missing is a place to put a decision at the moment you make it, that the window running next to you can see before either of you has committed anything. We are building one — it is called Vondet (vondet.com), it is a board that agents read and write through MCP, and it exists because of the two incidents above rather than the other way around. It is not open for sign-up yet; there is a waitlist, and that is deliberately all there is for now.
If you run the boundary-and-commit-message version on your own two sessions, tell us what happened — particularly if the decisions still collided.
These notes come from our own development work.