Git is the wrong abstraction for multi-agent scaling
Why we built copy-on-write symlink worktrees with jj-style auto-rebase instead of using git for multi-agent coding.
How humans work
A developer checks out a branch, edits files, commits when ready. One working copy, one HEAD, one index. Git was designed around this. The working tree is singular because the human is singular. When a second developer joins, they clone the repo. Each gets their own .git, their own index, their own working copy. Isolation is per-machine because humans are per-machine.
This works because humans are slow. One person produces maybe 50 commits a day. Two people editing the same function at the same time is uncommon at human speed. When it happens, one of them rebases, maybe resolves a conflict, moves on.
Agents are not slow. Ten agents working in parallel on the same codebase need ten concurrent working copies. A hundred need a hundred. git worktree add exists, but each worktree gets its own checkout directory, its own HEAD, its own index. It shares the object store with the parent repo and nothing else. It doesn't share node_modules/. It doesn't share .env. It doesn't share the build cache. Everything in .gitignore has to be duplicated or hacked around per worktree.
A hundred git worktrees means a hundred copies of node_modules/, or fragile symlink scripts that break on the next npm install.
Why jj is the right model
Jujutsu (jj) gets the model right. The working copy is just another commit. No staging area, no index. Every save is automatically snapshotted. When you change an ancestor commit, jj rebases all descendants automatically. No manual git rebase. The DAG stays clean.
Think about agent hierarchies. An orchestrator commits a refactor to a shared interface. Its three worker agents, each touching different files downstream, see the interface change propagated automatically. If their work doesn't conflict, they keep going. If it does, they're told where.
But jj is still a version control system. It tracks source files. It ignores node_modules/, .env, build artifacts, IDE configs, local overrides. It needs a .jj directory per working copy.
For humans that's fine. Agents need the full working environment: type checker, LSP, test runner, build system. Source files alone aren't enough. The agent needs a workspace that works, not a checkout that needs bootstrapping.
We took jj's model and implemented it at the filesystem level.
Copy-on-write symlink worktrees
Each agent gets a worktree directory. Root agents symlink to the real workspace. Child agents symlink to their parent's worktree. This creates a chain: workspace ← root worktree ← child worktree ← grandchild worktree.
Every file in a child's worktree is a symlink until the child modifies it. On first write, the symlink is replaced with a real file. A manifest tracks what each agent has actually touched: created, modified, deleted. Everything else is inherited through the symlink chain.
Symlinks don't know about .gitignore. They inherit everything. node_modules/ is a symlink. .env is a symlink. The TypeScript build cache, the Rust target/ directory, the local .vscode/settings.json you never committed. Every file that git would ignore, every agent gets for free. No npm install per agent. No copying environment files. No rebuilding caches. The agent starts with a working, runnable project.
Creating a hundred agent worktrees costs almost nothing. The disk footprint is proportional to what each agent changes, not the size of the workspace. An agent that modifies 3 files in a 10,000-file project has 3 real files and 9,997 symlinks. A read-only investigator agent has zero real files.
Compare this to git worktree add. In a project with 500MB of node_modules, 100 git worktrees need 50GB of disk, or you skip node_modules and nothing runs. With symlink worktrees, 100 agents share one node_modules/. Total overhead: the files they changed.
Cascade rebase
This is jj's auto-rebase, applied to the agent tree.
When a parent agent commits, the system emits a WORKTREE_CHANGED event to all direct children. Each child rebases its materialized files against the parent's new state. The rebase runs a 3-way merge using Myers diff in Rust/WASM: base is the old parent state, ours is the child's version, theirs is the new parent state. Non-overlapping changes merge cleanly. Parent edits line 10, child edits line 50, no conflict.
Clean rebase: the cascade continues. The child's children receive WORKTREE_CHANGED and rebase against the child's now-updated state. Propagation is recursive through the whole agent tree.
Conflict: the cascade pauses at that node. The child gets a WORKTREE_CONFLICT_DETECTED event with standard conflict markers. Once resolved, WORKTREE_CONFLICT_RESOLVED fires and the cascade resumes.
When children run on different machines (cloud sandboxes), the event embeds the changed file contents directly. The remote node applies the rebase without reading the parent's disk. The event carries the data, not a pointer to it.
No polling, no locks across agents, no central merge coordinator. Each agent rebases independently against its parent. The system converges because events propagate in order.
The event-sourced filesystem
The worktree system works because every file change is an event. When an agent commits, it emits an AGENT_COMMIT_CREATED event containing a list of operations: put, delete, move. The event log is the source of truth. Worktree state at any point can be reconstructed by replaying commit events in order.
This is what makes migration work. When an agent moves from a local machine to a cloud sandbox, the target node doesn't receive a tarball. It receives the agent's commit events and replays them. The lineage collector walks the ancestor chain, gathers commits from each parent, ships them root-first. The remote node replays and gets an identical worktree, including the full symlink chain to its ancestors. Content-addressed storage deduplicates files that haven't changed.
It's also a complete audit trail. Every file change every agent made, ordered. Replay to any point, compare two agents' states, trace exactly why a cascade rebase conflicted. Full version history without git.
Git tracks source files in a commit graph. That's what humans need. Agents need something different: an event-sourced filesystem over full workspaces, where isolated overlays inherit everything the project needs to actually run.