Skip to content

Repository files navigation

Parallel Builders for Claude Code

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.

tests License: MIT Python 3.10+ Docker

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

Why

  • 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.

How it works

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/&lt;run&gt;<br/>in your repo]
Loading
  1. 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.
  2. Baseline. Tests run at the starting commit first, so pre-existing failures are never blamed on a builder.
  3. Build. Each builder gets a self-contained task, works in /work inside its container, and is supervised with live status (turns, last tool used).
  4. 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, regression checks the new tests actually fail without the fix.
  5. 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.
  6. 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.

Example session

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

Requirements

  • 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

Install

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
  1. Create a token in a normal terminal (not through Claude Code — it needs a TTY):
claude setup-token
  1. 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.

Usage

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 CLI directly

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).

Safety model

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, verify and regression run 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 deliver refuses 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 through builder.py logs and builder.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 list shows sizes.

Configuration

Setting Default Purpose
config.jsonprotected_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.

FAQ

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.

Development

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 red

See CONTRIBUTING.md.

Roadmap

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.

License

MIT. Claude and Claude Code are trademarks of Anthropic, PBC; this project is not affiliated with or endorsed by Anthropic.

About

Run Claude Code agents in parallel, each in its own disposable Docker container with your repo mounted read-only. Reruns every builder's tests, flags out-of-scope edits and leaked tokens, merges onto one branch and verifies it with no network.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages