Back to blog
Collaboration
Claude Code
Amp

Why I Stopped Running Parallel Agents in the Same Repo

Thắng Đoàn
Thắng Đoàn

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 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 modified .env.local, 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 stash 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, a second copy of the repo where you can check out a different branch at the same time. 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. A sibling directory avoids 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, 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 need --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 work in progress. Dry-run by default means the safe path is the default, 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 builds 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.

Share:

Recommended for you

Enjoyed this article?

Subscribe for new articles. No spam. Unsubscribe anytime.

By subscribing you agree to receive the newsletter. No spam, and you can unsubscribe anytime.