Skip to content

·  · 7 min read

Running Two AI Coding Agents on One Repo: Git Worktrees Are the Fix

Today a second AI agent started up in a directory where another session was mid-work on a dirty branch. It was one git switch away from committing onto the wrong branch. Here's the boring, native fix.

Today a second AI agent started up in a directory where another session was mid-work on a dirty branch. It was one git switch away from committing onto the wrong branch. Here's the boring, native fix.

Running two AI coding agents on one repo is quickly becoming a normal Tuesday. Two Claude Code terminals on the same project. Cursor open while a CLI agent grinds through a backlog. An overnight agent working while you sleep, or, in my case today, while I was actively editing. Nobody warns you about the failure mode, because it doesn’t look like an AI problem. It looks like a git problem, and it is one: both sessions share a single working tree. Same files, same index, same HEAD. The first agent to run git switch moves the ground under the other one.

It happened to me today. The only reason it cost me nothing is that a hook caught it before the second agent touched git. This post is the incident, the two-part fix (both halves native, no new dependencies), and two gotchas I would have missed if I hadn’t hit them myself.

One working tree, one HEAD, two writers

A standard git checkout gives you exactly one working tree. HEAD is a single pointer that says “commits go here.” Every session you launch inside that directory shares all of it: the files on disk, the staging area, and that one HEAD.

The sharing is invisible right up until two sessions write. Then it fails in three distinct ways:

  1. Commits land on the wrong branch. Agent A is on feature-x. Agent B runs git switch -c feature-y, does some work, and moves on. Agent A’s next commit quietly lands on feature-y, because HEAD moved and nothing told it.
  2. Checkouts collide with uncommitted work. If agent A has dirty files, agent B’s branch switch either refuses (best case, and an agent may “helpfully” force past it) or drags A’s uncommitted changes onto a branch where they make no sense.
  3. Plain file collisions. Both agents edit the same file at the same time. No git command required; last writer wins, silently.

None of this is new. Two humans SSH’d into one shared checkout could always do this to each other. What’s new is that agents made it likely: they are fast, they run unattended, and they never glance at the other terminal before typing.

The day it almost happened to me

Here is the exact incident, because the abstract description undersells how quiet this failure is.

A live Claude Code session was working in my heroweb.dev repo on a branch called en-full-parity, with a dirty working tree: uncommitted changes to HomePage.astro and es.json. Meanwhile, a second agent started in the same directory to handle a separate task.

The second agent’s first git action would have been a git switch to a new branch, or a commit. Either one moves HEAD out from under the first session. Best case, the first session’s next commit lands on the wrong branch. Worst case, the switch tramples the uncommitted HomePage.astro work that existed nowhere else but that tree.

What actually happened instead: a SessionStart hook printed a warning the moment the second agent booted.

ANOTHER LIVE CLAUDE SESSION is active in this same directory. You are sharing a working tree.

The second agent read that and, instead of touching git, moved itself into an isolated worktree. Total cost: one warning line and a bun install. Without the hook, the cost would have been a forensic afternoon untangling which commits belonged on which branch.

Running two AI coding agents on one repo, safely: a worktree per agent

The isolation half of the fix is git worktree, which has been in git since 2.5, back in 2015. A worktree is a second working directory attached to the same repository: its own files on disk, its own index, and, the part that matters here, its own HEAD.

# From anywhere inside the repo: create a sibling directory
# on its own branch, cut from origin/main
git worktree add ../heroweb-autorun -b autorun/two-agents-post origin/main

# node_modules is not shared, so install once per worktree
cd ../heroweb-autorun
bun install

Now the second agent lives in ../heroweb-autorun. It can switch branches, commit, and push without moving anything in the original checkout. The two directories share one object store (the .git data), so commits made in either are instantly visible to both, branches are common, and a push from the worktree is a completely normal push. It is not a clone; there is no second copy of history and no syncing step.

The working agreement I’ve settled on: one worktree per agent, and inside it, one branch per unit of work. The agent commits, pushes, opens a PR, and the other session’s dirty tree never notices any of it. This is the same mechanism my overnight agent setup uses to keep parallel units from clobbering each other. Today was just the daytime edition of the same lesson.

When the work is merged, clean up with git worktree remove ../heroweb-autorun, and git worktree prune sweeps any stale records.

The other half: agents that know about each other

Worktrees stop the collision, but notice what they don’t do: they don’t tell anyone a collision was coming. My second agent only reached for a worktree because something told it another session was live.

That something is a small shared context layer. Mine is two pieces:

  • A session pointer: each session records where it’s working (directory, branch, timestamp) in a shared spot, so there’s a cheap, greppable answer to “who is active right now?”
  • A SessionStart hook that scans recent session records on boot and prints the warning you saw above when another live session claims the same directory.

That’s coordination, not isolation, and you want both. Isolation without coordination means every agent has to defensively create a worktree every single time, which is wasteful but survivable. Coordination without isolation means the agents know about each other and still share a HEAD, which is polite but doomed. The coordination half is the same shared-memory layer I already use to carry context between Claude Code sessions; “who else is live in this directory” is just one more question that layer answers.

The principle underneath: an agent should learn “you are not alone here” from a warning at startup, not from a corrupted commit at the end.

Two gotchas that will find you

Both of these bit me while wiring up the detection, and both generalize well past my setup.

Key on the launch directory, not the git root. My projects live in one parent folder that holds several repos, and I usually launch tools from that parent, which is not itself a git repo. There, git rev-parse --show-toplevel returns nothing. Any concurrency check keyed on “the git root” silently no-ops in exactly the situation it exists for. Key it on the invocation directory instead; that one always exists.

Exclude yourself. A naive “is another agent live in this directory?” check finds itself. And its own subagents, if it spawns helpers that also register sessions. The check has to filter out the current session’s ID and anything that session spawned, or every boot produces a false alarm, you learn to ignore the warning, and an ignored warning is worse than no warning.

The honest cost

Worktrees are not free. node_modules is not shared between them, so every worktree needs its own bun install (or npm, pnpm, whatever you run), which costs a minute and some disk. If your project carries heavyweight local state, a dev database or large fixtures, that needs a per-worktree story too.

That’s the whole bill. Weigh it against the alternative, a commit on the wrong branch or overwritten uncommitted work in a tree an agent was actively using, and it’s the cheapest insurance you’ll ever buy.

Boring on purpose

There is nothing to install and no product to buy. Git solved the isolation problem a decade ago; a pointer file and a startup hook solve the awareness problem. If you’re going to run more than one agent against a repo, and you will, wire up both halves before the day one agent moves HEAD out from under the other. I got the warning version of that lesson today. The other version costs an afternoon.

    Share:

    Enjoyed this post?

    Get new posts on web dev, AI and SEO straight to your inbox. No spam, unsubscribe anytime.

    No spam. By subscribing you agree to the privacy policy .

    Back to blog

    Related Posts

    View All Posts »
    How I Cut My Homepage Image Weight 88% (Astro + Sharp + WebP)

    How I Cut My Homepage Image Weight 88% (Astro + Sharp + WebP)

    A static Astro site with 15KB of JavaScript was shipping 3.4MB of images. Here are the three failure modes that got past the image pipeline — the fallback-always-ships rule, the screenshot the optimizer never touched, and the boilerplate nobody referenced — and the 15 lines of Sharp that fixed all of it.