What Happens When Three AI Coding Sessions Share One Git Repo
AI coding agents are fast enough now that running just one at a time feels wasteful. So I don't. Most days I have two or three Claude Code sessions working on Bakery Buddy at once, each on its own slice of the product. For a stretch in August, that habit kept running into the same wall, over and over, in slightly different shapes: git was never built for more than one writer at a time, and it does not tell you when you've violated that.
The staging area isn't yours alone
On August 19th, one session ran a path-scoped `git add` on the exact files it was about to commit. Nothing careless about that; scoping an add to specific paths is supposed to be the safe move. Moments later, a completely unrelated session ran its own `git add`, and when the first session went to commit, its own diff had picked up someone else's `PLAN.md` and `docs/MAP.md` along for the ride.
The reason is simple once you see it: the git index, the staging area, is one file for the entire working tree. It is not scoped per terminal, per session, or per intention. "I only added my files" is true and irrelevant, because staging is a shared drawer, not a private one, and anyone can drop something else in it between your add and your commit.
The very next day it got worse. A different session ran `git add -A` on a directory, meaning "everything in here, tidy it up." "Everything in here" turned out to include another session's untracked files sitting in that same directory, which a narrower path-scoped add would never have touched. Two of that session's in-flight migrations got swept into a commit that had nothing to do with them.
The one that actually rewrote someone's work
The sharper version of the same problem is `HEAD`. On August 21st, one session checked `git log`, saw its own commit sitting at the tip, and ran `git commit --amend` to fold in one more change. In the gap between the check and the amend, a different session's ordinary commit had landed and become the new tip. The amend didn't touch the commit that had been checked. It rewrote the one that landed after, attaching one session's staged diff underneath a completely different session's message and identity.
`git diff --cached --stat` does not protect you here, because it diffs your staged changes against the parent commit, not against whatever HEAD happens to be the instant you run amend. The check and the action are two separate moments, and between them, HEAD is free to move.
The same day, the identical failure showed up a second way with no amend involved at all. One session had files sitting staged, ready to commit. Before it ran the command, another session made its own ordinary commit. That commit didn't fail or wait. It absorbed both diffs and shipped them under its own message, as if one person had written both.
Even a number can be claimed out from under you
This isn't only a git problem. Database migrations in this repo are flat numbered files, no directory, no tool assigning the next one for you. The convention is: list the folder, see the highest number, name yours one higher. Twice in one week, two different sessions listed the folder within seconds of each other, saw the same highest number, and both claimed it. One incident took two numbers, 169 and 170, right out from under a migration that was already mid-flight. The fix that came out of that wasn't "check once at the start of a plan." It was "re-check immediately before every single attempt," because a second collision happened inside the same minute as the first, on a completely different rename.
Why none of this shows up in a test
Every one of these is invisible to the tools that are supposed to catch mistakes, and for a reason worth sitting with: they aren't bugs in the code. The code that got committed in each case was fine. The failure lives one layer below the code, in shared, mutable state that everyone assumed was theirs alone the moment they weren't paying attention to another session. A green build has nothing to say about who else touched the staging area five seconds before you did.
The fix wasn't smarter agents. It was fewer shared assumptions.
None of this got solved by telling the AI sessions to be more careful, because "be more careful" doesn't survive a five-second race. It got solved by removing the shared state, or by checking it at the last possible moment instead of the first convenient one:
- `git diff --cached --stat` immediately before every commit, not once after staging, catches an unexpected file before it ships.
- `git log --oneline -1` gets re-read immediately before any `--amend`, not once earlier in the same session, because HEAD can move in the gap.
- A migration number gets re-listed right before each individual attempt, not once per plan.
- The end-to-end test suite runs behind a lock that names who is holding it, so two sessions stop fighting over the same dev server port and the same worker slots.
- `scripts/worktree.sh new <branch>` gives a session its own working tree entirely, which is the actual fix for the staging-area problem: don't share the drawer at all.
One evening in mid-August, that combination of sessions pushed 27 commits across 9 separate deploys, because the deploy gate runs per push, not per commit, and batching pushes instead of firing one per finished task is what keeps that gate from costing everyone else five minutes every time.
What generalizes past git
The pattern underneath every one of these is the same, and it has nothing to do with git specifically. Anything you treat as "obviously mine while I'm using it" is actually shared state the instant a second process, human or AI, can also touch it. A staging area, a HEAD pointer, a "next number" convention, a dev server port: none of them come with a lock by default. Concurrency bugs don't require threads or a distributed system to show up. They show up the moment two of anything write to one shared thing without one, and the check that would have caught it has to happen right before the action, not sometime earlier when it felt convenient to check.
If you're running more than one AI agent against the same project, the code each one writes is very likely fine. Go look instead at what they're both allowed to touch without asking each other first.
Frequently Asked Questions
What actually went wrong?
Nothing in the code. Running multiple AI coding sessions against one shared git repository at the same time exposed that git's staging area and HEAD pointer are shared, mutable state, not something scoped to whichever session is currently using them. Two sessions touching the index or the branch tip within seconds of each other produced commits with the wrong files or the wrong author attached, and a numbered-migration-file convention had the same race.
Did this cause real damage?
Files from one session's work landed in another session's commits twice (Aug 19 and Aug 20), and on Aug 21 an ordinary commit and a --amend each independently absorbed a different session's diff under the wrong commit message. In every case the underlying work was recovered by splitting the commits back apart, but it required noticing the mismatch first.
Why didn't a passing build or test suite catch any of this?
Because none of it was a code bug. The committed code was correct in every instance. The failure was one layer below the code, in shared git state that neither session had any way of seeing the other touch. A build only checks what got committed, not who else was reaching for the same drawer a moment earlier.
What's the actual fix?
Checking shared state immediately before acting on it instead of once earlier, plus removing the sharing where possible: re-reading git log right before an amend, re-listing migration files right before naming the next one, running a lock that names who holds it for the end-to-end suite, and giving a session its own isolated git worktree instead of sharing one working tree at all.
I build Bakery Buddy for my wife Lindsay's cake studio, Marin Cake Studio, running more than one Claude Code session against the same repo most days, which is exactly how I found every bug in this post.
Ready to put this into practice?
Join the Waitlist