DEV Community

quintetkit
quintetkit

Posted on

Running Claude Code in True Parallelism - git worktree and Serial Steps

This article is for those who encountered issues when running multiple tasks simultaneously with Claude Code.

  • One task was reading package.json while the other was running npm install, causing a crash.
  • Build artifacts in dist/ mixed together, making it impossible to distinguish which output belonged to which task.
  • A git checkout by one task inadvertently included uncommitted changes from the other.

Even if you logically separate responsibilities, if the working directory is the same, physical conflicts will occur.
The solution is to isolate them using git worktree, but I got stuck automating the cleanup process.
Here is what I learned.

1. Separating Calls Does Not Mean Parallel Execution

This is the first hurdle you will face.

(Not parallel)
"Implement Issue 12"
  → Wait for completion
"Implement Issue 13"
  → Wait for completion
Enter fullscreen mode Exit fullscreen mode

Claude Code only runs multiple Agent calls simultaneously if they are issued within a single response. If you separate the conversation turns, the execution becomes serial at that point.

(Parallel)
"Implement Issues 12, 13, and 14.
  Issue three Agent calls simultaneously within one message."
Enter fullscreen mode Exit fullscreen mode

If you do not explicitly state "issue them simultaneously," they may be processed one by one. If the request format implies "one by one," the model tends to follow that pattern.

2. Running in the Same Directory Causes Accidents

The three incidents at the top of this article all come from one cause. Splitting
responsibilities logically does not help when there is only one working directory.

Isolate them using git worktree.

git worktree add -b issue/12-upload  ../worktrees/issue-12  main
git worktree add -b issue/13-profile ../worktrees/issue-13  main
git worktree add -b issue/14-export  ../worktrees/issue-14  main
Enter fullscreen mode Exit fullscreen mode

This creates three independent working directories. Assign each task to its own directory.

To be precise about disk: what is shared is only the objects in .git, and the
working tree is copied.
Measured locally, adding one worktree to a repository with
5,600KB of source added 5,608KB, while .git grew by 44KB. node_modules is not
shared either (see below). Three parallel branches means three copies of the source
and its dependencies.

Things to Watch Out For During Cleanup

You need to delete them when finished, but there are pitfalls here.

# Dangerous approach
git worktree list | ... | while read branch; do
  git merge-base --is-ancestor "$branch" main && git worktree remove ...
done
Enter fullscreen mode Exit fullscreen mode

This deletes worktrees that have no commits yet.
Because newly created branches point to the same commit as main, they are incorrectly judged as "merged with main."

Instead, first check if there are any own commits relative to the fork point.

fork="$(git merge-base main "$branch")"
own="$(git rev-list --count "${fork}..${branch}")"
[ "$own" -eq 0 ] && continue   # No work done. Do not delete
Enter fullscreen mode Exit fullscreen mode

There is another caveat. After merging, merge-base matches the branch tip.
This makes it impossible to distinguish between "merged" and "no work done."

The countermeasure is to record the fork point at the time of branch creation.

git worktree add -b "$branch" "$dir" main
git config "branch.${branch}.myBase" "$(git rev-parse main)"
Enter fullscreen mode Exit fullscreen mode

If you are operating based on Pull Requests, it is more reliable to use the GitHub PR merge status as the source of truth.

state="$(gh pr list --head "$branch" --state all --limit 1 --json state -q '.[0].state')"
case "$state" in
  MERGED) echo "Safe to delete" ;;
  OPEN|CLOSED) echo "Keep" ;;
esac
Enter fullscreen mode Exit fullscreen mode

Squash merges, however, cannot be detected with git alone.

You often see the advice to check whether every line of git cherry main "$branch"
is -. That advice is wrong. git cherry matches on patch-id, so a squash that
collapses several commits into one can never match. Measured:

Branch git cherry after squash
1 commit - (matches)
2 commits + + (does not match)

It only works by accident when the branch has a single commit, which is exactly
why people try it, see it work, and ship it. Rather than deleting branches on an
unreliable guess, read the PR state through gh when it is available. When it is
not, give up on detecting squashes and remove them by hand.

3. The Other Pitfalls Are in a Separate Article

There are other pitfalls encountered besides worktree, but since their nature is different, I will cover them separately.

  • Merges must be done serially; otherwise, debugging becomes difficult when things break.
  • The upper limit for parallel tasks is 3–4. The constraint lies with the AI reviewer, not the system.

These topics concern "how to split responsibilities" and are covered in a separate article. This article focuses solely on directory isolation.

Supplement: Fine Points When Using worktree

A branch can only be checked out from one worktree

Git will prevent you from opening the same branch in two worktrees. This is a safety mechanism, so do not try to bypass it; adhere to "one task per branch."

Deleting a directory by hand leaves the metadata behind

If you manually delete directories with rm -rf, the git management information remains.

rm -rf ../worktrees/issue-12    # Directory is gone, but metadata remains
git worktree prune              # Clean up management info
Enter fullscreen mode Exit fullscreen mode

Using git worktree remove avoids this hassle.

Dependency installation is required per worktree

The objects in .git are shared, but node_modules is not. Installation runs every time a worktree is created. This is the cost of isolation; you must either accept it or use tools like pnpm that have a global store.

CI configurations may not recognize worktrees

git rev-parse --git-dir returns /path/to/main/.git/worktrees/<name> instead of .git in a worktree. Scripts relying on this path will break. It is safer to use git rev-parse --show-toplevel.

Summary

  • Even if responsibilities are separated, physical conflicts occur if the working directory is the same.
  • Separate using git worktree. Only the objects in .git are shared; the working tree and node_modules grow with the number of branches
  • Automating cleanup is dangerous. Judging solely by "is it an ancestor of main" will delete branches that have no work yet.
  • First check if there are own commits relative to the fork point.
  • After merging, merge-base matches the branch tip, so record the fork point at creation time.
  • If using PRs, relying on the PR merge status is more reliable and supports squash merges.

I have defined this operation as a sub-agent configuration for Claude Code and distributed it along with scripts for creating and cleaning up worktrees. The free version is published under the MIT license.


Related


I publish the configuration for splitting Claude Code into separate personas —
Architect, Coder, Reviewer, Conflict Resolver — under MIT. Copy it, run
./setup.sh, and it works. It does not depend on your tech stack.

https://github.com/quintetkit/quartet

I built one real tool using nothing but this workflow. Every Issue, PR, review
and merge is still there. The parts that went wrong were not deleted.

https://github.com/quintetkit/mdlinkcheck

The version that adds a UI Designer persona, review criteria, a per-Issue
parallel execution script and a 10-chapter guide is on the
product page.

The full kit — five personas, the scripts and the complete guide in English and Japanese — is on BOOTH, a Japanese store with an English interface that takes international cards.

https://quartet-dev.booth.pm/items/8807156

Top comments (0)