Back to blog
Collaboration
Claude Code
Amp

Why I Stopped Running Parallel Agents in the Same Repo

Thang Doan
Thang Doan

If you typed git stash twice in the last hour to switch agent tasks, your workflow is cracking.

What collides

Three failure modes show up when two or more agents share one workspace.

Branch collision. Agent A is on feature/auth. Agent B starts on feature/rss. The second agent either rebases the first one's work or checks out a branch that pulls uncommitted changes from the first agent's task.

State collision. Agent A leaves a .env.local modified, a fixture file in tmp/, or a half-finished migration. Agent B picks up that state, treats it as ground truth, and builds on top of something that was never committed.

Review collision. Both agents produce a diff against the same working tree. The PR mixes changes from two tasks. You cannot review what you cannot isolate.

The git stash loop is the warning light. If you are stashing to switch context, you are running one workspace harder than it was designed to run.

The move that fixed it

Each agent works in its own worktree. Each worktree is a sibling of the main repo, never a subfolder inside it. Each one starts from origin/master, fresh.

The script that does this is short. The design decisions inside it are the part that matters.

WT_ROOT="$(cd "$REPO_ROOT/.." && pwd -P)/blog-devigner-worktrees"
git worktree add "$WT_PATH" -b "$BRANCH" "origin/master"
cd "$WT_PATH" && pnpm install --frozen-lockfile

Three choices in those lines, and each one prevents a self-sabotage mode I used to hit.

Choice 1: sibling, not subfolder

The worktree root is ../blog-devigner-worktrees, one level above the repo. Not ./worktrees inside the repo.

Subfolders inside the repo get indexed by your editor, scanned by your test runner, and picked up by globs like **/*.ts. The first time an agent writes a fixture inside an inner worktree, your typecheck or lint breaks in the main repo. Sibling directory dodges all of that.

Choice 2: always fresh base

The default base is origin/master, not local master. Local master drifts. If you forgot to pull, the worktree starts behind. If you have local commits on master (you should not, but you might), the worktree inherits them.

Pinning to origin/master means every worktree starts from the same published state. Two agents on two worktrees start from byte-identical code. The only variable is the work they do.

Choice 3: cleanup is dry-run by default

Cleanup lists merged worktrees by default. It does not remove them until you pass --force. Dirty worktrees survive a single --force and require --force --force.

That sounds annoying. It is the only reason I have not lost work to a typo.

An agent worktree accumulates state: log files, scratch code, half-finished refactors. If cleanup ran eagerly, every typo or forgotten flag would delete in-progress work. Dry-run-by-default means the default behavior is safe, and the explicit opt-in is the dangerous one.

What worktrees do not solve

Worktree isolation fixes physical collisions. It does not fix logical ones.

If agent A is changing the auth schema and agent B is building a feature that depends on the old auth schema, both worktrees build clean. Both PRs look fine in isolation. The merge breaks.

Worktrees give each agent a clean room. They do not give each agent a clean dependency graph. That part is still on you, at the planning layer.

Check yours

Run git worktree list in your repo. Count worktrees older than your last merge. If the answer is more than zero, your cleanup contract is missing, and your next agent will inherit whatever state those worktrees are holding.

Recommended for you

Enjoyed this article?

Subscribe for new articles. No spam. Unsubscribe anytime.

By subscribing you agree to receive the newsletter. See the Privacy page.