Run several Claude Code agents in parallel — each in its own disposable Docker container with your repo mounted read-only — and get their work back as one tested git branch.
You write (or already have) a plan with independent items. Your normal Claude Code session becomes the orchestrator: it works out which items can safely run at the same time, hands each one to a headless builder Claude session in an isolated container, reruns every builder's tests itself, merges the results onto a new branch, verifies the merged code in a container with no network, and gives you one branch to review.
Unofficial community project. Not affiliated with or endorsed by Anthropic.
Contents: Why · How it works · Example session · Requirements · Install · Usage · Safety model · Configuration · FAQ · Development · Roadmap
- Plans are slow when done one item at a time, but letting several agents loose on one working tree (or several worktrees on your machine) means they can step on each other and on anything else your user account can reach.
- Isolation by container, not by instruction. Builders see a read-only copy of your repo and can only write inside their own container and one output folder.
- Evidence over claims. A builder's "all tests pass" is never trusted: the toolkit reruns the tests, checks which files changed, and verifies the merged result.
- Your repo stays yours. The only thing ever written to it is a new branch
builders/<run>— no checkout, merge or push.
flowchart LR
P[PLAN.md] --> O[Orchestrator<br/>your Claude Code session]
O -->|task 1| B1[builder container 1<br/>repo read-only, claude -p]
O -->|task 2| B2[builder container 2]
O -->|task 3| B3[builder container 3]
B1 -->|patch + test rerun| I[integration clone]
B2 -->|patch + test rerun| I
B3 -->|patch + test rerun| I
I --> V[verify<br/>fresh container, no network]
V --> D[branch builders/<run><br/>in your repo]
- Plan the waves. Items that touch different files run together (max 3 at once by default). Items that must edit the same file go in a later wave.
- Baseline. Tests run at the starting commit first, so pre-existing failures are never blamed on a builder.
- Build. Each builder gets a self-contained task, works in
/workinside its container, and is supervised with live status (turns, last tool used). - Collect. The patch is exported, tests are rerun inside the container, files outside
the item's declared scope are flagged, and the patch is scanned for leaked tokens.
For bug fixes,
regressionchecks the new tests actually fail without the fix. - Integrate and verify. Patches are applied to a separate clone (conflicts resolved by the orchestrator, asking you when it matters), then the full test suite runs in a fresh container with no network.
- Deliver. After your OK, the branch
builders/<run>is added to your repo.
The orchestrator asks you before anything that spends your Claude usage, writes to your repo, drops an item, makes a non-trivial merge decision, or delivers.
Output from real runs on an earlier copy of the bundled examples/mathkit
project (trimmed, paths shortened).
$ builder.py status live-1428
item state attempt turns container collected last tool
gcd running 1 9 up no Bash python -m pytest -q
primes done 1 12 up no Bash python -m pytest -q
$ builder.py collect live-1428 primes
primes: 3 file(s) changed, patch 3005 bytes
mathkit/__init__.py
mathkit/primes.py
tests/test_primes.py
tests (rerun by collect): exit 0
| 12 passed in 0.01s
$ builder.py integrate live-1428 primes gcd
primes: applied cleanly
gcd: CONFLICT in mathkit/__init__.py # both items added an export; the orchestrator keeps both
$ builder.py regression final-1516 mean --tests 'tests/*'
regression-mean: tests on e3b4475551 in a fresh container with no network: exit 1
| FAILED tests/test_stats.py::test_mean_two_values - assert 2.0 == 3.0
mean: the new tests FAIL without the fix, as they should.
$ builder.py verify final-1516
verify: tests on eeb7a9a69f in a fresh container with no network: exit 0
| 4 passed in 0.00s
$ builder.py deliver final-1516
delivered branch builders/final-1516 to ~/src/mathkit (verified)
eeb7a9a Fix mean dividing by the wrong count
- Linux host, non-root user. Tested on x86-64 Ubuntu with rootful Docker Engine 29. The host's Claude Code binary is mounted into the containers, so macOS and Windows hosts are not supported yet (help wanted). Rootless Docker, Podman, Docker Desktop for Linux and SELinux-enforcing hosts are untested.
- Docker, Python 3.10+, git
- Claude Code from the native
installer (a single binary; the npm version is a Node script and the image has no
Node), a Claude subscription, and a long-lived token from
claude setup-token
The toolkit expects to live in ~/.claude/builders (the skill calls it there). If that
folder already exists, move it aside first.
# 1. Toolkit + skill
git clone https://github.com/digin1/claude-code-parallel-builders.git ~/.claude/builders
mkdir -p ~/.claude/commands
cp ~/.claude/builders/skill/build-parallel.md ~/.claude/commands/build-parallel.md
# 2. Base image (Python 3.11, git, pytest, ripgrep)
python3 ~/.claude/builders/builder.py image- Create a token in a normal terminal (not through Claude Code — it needs a TTY):
claude setup-token- Save it without echoing it or putting it in your shell history (works from bash or zsh):
bash -c 'read -rsp "Paste token: " t && echo && (umask 077; printf "%s\n" "$t" > ~/.claude/builders/oauth_token)'oauth_token, config.json and runs/ are git-ignored. To update later:
git -C ~/.claude/builders pull, then copy the skill file again and rerun builder.py image.
Uninstall: stop any runs (builder.py cleanup --all), delete
~/.claude/commands/build-parallel.md and the ~/.claude/builders folder (which holds
your token and run records), remove the claude-builder:base image, and revoke the token
in your Claude account settings.
In Claude Code, inside the git repo you want to change:
/build-parallel PLAN.md
/build-parallel PLAN.md 2 3 5 # only these items
A plan can be a simple Markdown checklist — see examples/mathkit/PLAN.md:
- [ ] 1. Add `is_prime(n)` and `primes_up_to(limit)` in `mathkit/primes.py` ...
- [ ] 2. Add `gcd(a, b)` and `lcm(a, b)` in `mathkit/gcdlcm.py` ...
- [ ] 3. Fix `mean()` in `mathkit/stats.py`: it divides by the wrong count ...The orchestrator shows you the waves (item → owned files), asks before launching, keeps
you updated while builders run, and ends with a report and the command to review the
branch: git diff <base>..builders/<run>.
The skill drives builder.py; you can use it by hand too (builder.py <command> -h).
| Command | What it does |
|---|---|
image [--project REPO] |
build claude-builder:base, or a project image from REPO/.claude/builder.Dockerfile |
init RUN --repo R --test-cmd C |
create a run at a base commit |
baseline RUN |
run the tests at the base commit (no network) |
start RUN ITEM --owns GLOB... |
start an item's container |
run RUN ITEM --task-file F |
start a headless Claude session in it (returns immediately) |
status / wait / logs |
live state, blocking wait, masked session log |
collect RUN ITEM |
export the patch, rerun tests, flag out-of-scope files, scan for tokens |
patch / regression |
masked patch view; check bug-fix tests fail without the fix |
overlap / integrate |
shared files; apply patches onto builders/RUN (--continue, --skip) |
verify RUN |
full tests on the integrated commit (no network) |
deliver RUN |
add branch builders/RUN to your repo |
stop / cleanup / list / remove-run |
housekeeping (remove-run previews unless --yes) |
Exit codes: 0 ok · 1 the tool itself failed or the arguments were wrong (never a
test result) · 2 wait timed out · 3 merge conflict · 4 patch did not apply ·
5 a check found a problem (tests failed, out-of-scope files, token found, regression
tests pass without the fix).
Enforced
- Repo mounted read-only; builders only see committed code at the base commit.
- Builder containers can write only to
runs/<run>/items/<item>/out/; the patch checksum recorded at collect time is checked before the patch is used. - No Linux capabilities, no privilege escalation, CPU/memory/process limits, and a TTL after which each container stops itself.
baseline,verifyandregressionrun with no network. Builder containers use Docker's default bridge network (they need the Claude API); no new network is created and no host ports are published.- Anything shaped like a Claude token is masked in CLI output. A token in a patch (binary
files included) or test log blocks that item, and
deliverrefuses a branch with a token anywhere in its history. - Git worktrees and submodules are refused. The repo, task files, build contexts, runs directory, token file and Claude binary are refused if they fall under a protected path — checked without touching that path.
- Commit messages are the task title only; integration refuses to run without a git identity instead of inventing one.
Not enforced — know these
- Builder containers can reach the network, and the token is readable inside them. A builder that is prompt-injected by content in your repo could send it out. Only build repos you trust, and revoke the token if you suspect a leak.
- Raw run files (
stream.jsonl,stderr.log,patch.diff,tests.log, the integration clone) are not masked; read them throughbuilder.py logsandbuilder.py patch. - Compressed files (zip, gz, npz) cannot be scanned for tokens.
- Every builder session uses the same Claude usage allowance as your interactive session.
- Each run keeps its logs and a full clone of your repo;
builder.py listshows sizes.
| Setting | Default | Purpose |
|---|---|---|
config.json → protected_paths |
none | absolute path prefixes the toolkit must never touch |
BUILDER_CONFIG |
config.json next to builder.py |
where to read that config |
BUILDER_PROTECTED_PATHS |
— | extra protected prefixes, :-separated |
BUILDER_MAX_PARALLEL / --max-parallel |
3 | active builder sessions; above 3 prints a warning |
BUILDER_RUNS_DIR |
runs/ next to builder.py |
where run records live (the skill assumes the default) |
BUILDER_TOKEN_FILE |
oauth_token next to builder.py |
token file (must be mode 600) |
BUILDER_CLAUDE_BIN |
claude on PATH |
binary mounted into containers |
<repo>/.claude/builder.Dockerfile |
— | extra tools for one project (ARG BASE=claude-builder:base / FROM ${BASE}) |
Protected paths are useful for network shares or data you never want an agent near.
Copy config.example.json to config.json and list real mount points: entries are
normalised (/mnt//data/ becomes /mnt/data) and match whole path components, but a
protected entry that is itself a symlink does not protect the directory it points to.
How is this different from Claude Code subagents or git worktrees? Subagents and worktrees run on your machine with your user's access. Builders here run in containers that can't write to your repo or home directory, and nothing is trusted until the toolkit has rerun the tests and verified the merged result.
How much usage does it take?
Each builder is a full Claude Code session, so three builders use roughly three sessions'
worth of your allowance, on top of the orchestrator. The default model for builders is
sonnet; pass --model to run to change it.
Can I use an API key instead of a subscription token? Not yet — see the roadmap.
What if my project needs Node, R, system libraries…?
Add .claude/builder.Dockerfile to the repo and build it with
builder.py image --project <repo>.
What if items touch the same files? The orchestrator puts them in separate waves, or merges small "registry" conflicts (both items adding an export) itself and asks you about anything bigger.
python3 -m venv .venv && . .venv/bin/activate && pip install pytest
python3 builder.py image # the Docker tests need the image
python3 -m pytest -q tests # unit + Docker tests; a stub replaces claude, so no usage
python3 tests/mutations.py # breaks each safeguard in a copy; every one must turn a test redSee CONTRIBUTING.md.
Ideas where community help is especially welcome:
- macOS / Windows hosts (install Claude Code inside the image instead of mounting the host binary)
- API-key authentication as an alternative to the subscription token
- a network allowlist for builder containers (Claude API only)
- rootless Docker / Podman support
- a small dashboard for live runs
Open an issue or start a discussion — real plans that did or didn't split well are especially useful.
MIT. Claude and Claude Code are trademarks of Anthropic, PBC; this project is not affiliated with or endorsed by Anthropic.