Skip to main content

Worktrees

Agentic development has a shape: an orchestrating session fans subagents out into git worktrees, each working a branch in parallel. The build tool either makes that shape cheap or it quietly forbids it — because if every fresh worktree starts with a cold build, nobody forks worktrees.

Bleep makes a fork of a multi-million-line build verified green in under a minute. This page is the recipe, the measured numbers, and — for the sceptics — exactly how it works.

The recipe

git worktree add ../feature-x
cd ../feature-x
bleep copy-state ../main # clone the sibling's compiled state
bleep compile # verifies everything, compiles nothing

That's all of it. No per-worktree daemon, no per-worktree configuration, no cache server. Agents do the same over MCP with bleep.copy-state.

Watch two fresh worktrees side by side — one pays for a full rebuild, the other seeds itself and its first build recompiles nothing:

The numbers

Measured on the repo bleep's authors work in daily — 4.9M lines of Java and 169k of Scala (4.6M of the Java is generated and checked in) in 132 projects, out of 17M tracked lines. Apple M5 Pro, 18 cores, APFS. Bleep snapshot of current master. Each step timed with /usr/bin/time:

StepTime
git worktree add (materialize the 17M-line checkout)6.2 s
bleep copy-state ../main (132 projects, 1.8 GB of compiled state)14.5 s
bleep compile — first build: verifies all 132 projects green33 s
Fork → verified green~54 s
The same bleep compile in a worktree without copy-state (cold)4 min 22 s
Every subsequent bleep compile (up-to-date check, 132 projects)9 s

Code sizes measured 2026-08-15 at commit {stats.repo.commit}; timings are from the run described above. Both come from bleep-site/src/data/build-stats.json, which is the only place the site keeps these figures.

The cold build is not slow because bleep is slow — five million lines is simply a lot of compiler work. The point is that no fork ever needs to do that work again: compiled state is a value you clone, not a side effect you recompute.

How it works, for the sceptical

The copy is a constant-time clone

bleep copy-state doesn't copy bytes if the filesystem can avoid it: on macOS it clones via APFS clonefile (cp -Rc — O(metadata), blocks shared copy-on-write until modified), on Linux via cp -a --reflink=auto (CoW on btrfs/XFS). 1.8 GB of classes and analyses "copies" without duplicating a block.

It runs inside the compile daemon, under the compile locks

The copy is routed through the shared compile server and takes the same per-project locks compiles take — a Shared lock on each source project, which blocks writers but not other readers. That's the whole point of putting it in the daemon: state is never copied mid-compile, no matter which client — CLI, IDE, or another agent — is compiling the source worktree at that moment. The daemon needs neither workspace's build loaded; projects are enumerated from disk, and the target has typically never connected.

What's copied — and what deliberately isn't

Per project: the classes directories, the zinc incremental analysis, and generated sources/resources. Not copied: the no-op manifest, whose keys are absolute paths into the source worktree — a copied manifest would validate against the parent's files and yield a false "nothing to do" that points the fork at the parent's classes. Caches and lock files are also skipped: caches regenerate, and a lock must never be inherited.

The first build verifies instead of trusting

That first 33-second bleep compile is not a formality and not a rebuild: zinc does one round-trip per project against the copied analysis and writes the fork's own manifest. The round-trip is fast because the copied analysis is byte-identical to one already resident in the daemon's analysis cache — the daemon has been serving the parent worktree all along.

One daemon serves every worktree

There is no per-worktree JVM. All checkouts on the machine share one compile server (compile servers) that keeps incremental state hot and stores identical dependency analyses once. Every command names its workspace explicitly, so parallel builds in parallel worktrees don't race — they queue on per-project locks.

When there is no sibling to copy from

CI machines and fresh clones have no warm neighbour. For those, the local directory cache / remote cache serves the same purpose from content-addressed storage: cache keys are content digests and the shipped zinc analysis is path-portable, so bleep remote-cache pull restores classes and analysis for every project a sibling — or a CI run — already built.

For agents

Register the MCP server once (claude mcp add --scope user bleep -- bleep mcp-server) and every session and subagent can call bleep.copy-state with a directory and a from, then compile and test any checkout on the machine. Runs land in per-worktree history, and bleep history diff --base-dir ../main diffs a fork's run against its parent's.

The payoff, in one sitting: an agent proves its change as a diff — first against its own previous run (test --diff), then against the worktree it was forked from (history diff --base-dir):

Nor is the fan-out hypothetical: the repo carries a scripted, rerunnable Claude session (demo-claude-agents/ at the repo root) in which an orchestrator spawns two parallel subagents into fresh worktrees over the MCP tools — one seeds with copy-state, one deliberately compiles cold — and closes by asking bleep for the cross-worktree timing diff. The model never invents a number; every figure below came back from a tool call. This is the run recorded in the cast on the front page, verbatim:

agent[seeded] copy-state: 756835 bytes in 71ms -> first compile: success
agent[cold] first compile: success (compiled everything from scratch)
bleep history diff-timing (cold -> seeded): total 3529ms -> 585ms
verdict: the cold worktree's first compile took 3529ms while the seeded
worktree's first compile took 585ms.

Milliseconds, because the demo build is tiny — it has to fit on camera. The mechanism is the same one that turns the four-and-a-half minutes above into under a minute on a real codebase; only the scale differs.