There's a pattern becoming standard in agentic development: give every coding agent its own Git worktree, let it work independently, then merge everyone's changes back together when it's done. It works, and it solves a real problem. Two agents can't safely edit the same working directory at once, so each gets its own copy of the repository instead. One works over here, one works over there, and Git gives each of them an isolated view of the tree.
But it's worth asking why agents need worktrees at all. They don't, not for the reasons that made worktrees necessary in the first place. What an agent actually needs is isolation: a known starting state, a place to record its own changes, visibility into whatever it depends on, and a way to reason about what happens when another actor touches something relevant to its work. None of that requires a second directory holding a materialized copy of the repository. That requirement comes from Git, not from what isolation actually means.
Agents need closures, not codebases
Here's the actual claim, stated up front. An agent doesn't need a codebase in the abstract, it needs a closure scoped to whatever task it's doing right now. For building, that means walking from changed nodes through their transitive dependencies. For verification, it means walking the other direction to find affected callers and tests. For collaboration, it means incorporating whatever concurrent changes have become causal dependencies of the work. For provenance, it means keeping track of exactly which state, inputs, tools, and verification steps produced the result. Those are different traversals over the same graph. The tree was never the answer, just one possible way to serialize it.
That matters even more for a software factory running large numbers of agents concurrently. Giving every agent its own worktree means duplicating filesystem state just so the source control model can offer isolation. Giving every agent its own causal view means the underlying state stays shared, and each actor only sees the slice of the graph relevant to its work. That's a different scaling model, not just a faster version of the same one. The rest of this post is about why worktrees exist in the first place, and what has to change underneath them for that scaling model to actually work.
Worktrees are spatial isolation
Git represents source code as trees of files at points in history, and a working directory is where one of those states becomes editable. That's a natural model for a person. A developer opens a repository, edits some files, runs a test, and eventually commits the result. The filesystem is both the interface and the workspace.
The trouble starts once more than one actor needs to work at the same time. If Agent A and Agent B operate on the same working directory, their uncommitted state gets tangled: one may edit a file while the other reads it, or change branches underneath the other's feet. Git's answer is to give each actor its own worktree.
Agent A
|
v
/worktrees/agent-a/
|
v
repository files
Agent B
|
v
/worktrees/agent-b/
|
v
repository files
The isolation is physical: two actors get two filesystem-shaped realities. That's a reasonable abstraction for people. For agents, it's worth asking whether the filesystem needs to be the boundary at all.
An agent needs a view of change
Think about what an agent actually needs while it works. A base state. The changes it's made since that state. Any other changes its work causally depends on. Possibly a sense of what its changes affect, what tests verify those parts, and whether some concurrent change has touched one of those relationships. None of that is a filesystem problem. It's a graph problem.
Suppose two agents start from the same state:
S0
/ \
/ \
Agent A Agent B
Agent A creates two changes, A1 and A2. Agent B creates B1 and B2. Their isolated states don't require two directories to exist:
Agent A view = S0 + A1 + A2
Agent B view = S0 + B1 + B2
The objects backing S0 can stay shared. What differs isn't where the files live, it's which changes are visible within each actor's view.
Now suppose B2 starts to depend on something A1 introduced. The graph can represent that relationship directly:
S0
├── A1 ───► A2
│ │
│ └────► B2
│
└── B1 ───► B2
Agent B's effective view is no longer just its original state plus its own work. Its closure now includes A1, because B2 depends on it:
closure(B2) = S0 + A1 + B1 + B2
Nothing had to be merged into Agent B's directory for that relationship to exist. The system resolved causality instead. That's the distinction that actually matters for agent-native development: Git uses spatial isolation. Agents need logical isolation.
Worktrees go stale by construction
The limitation gets more obvious once agents collaborate in real time. Two worktrees created from the same starting point start out consistent:
S0
/ \
/ \
A B
But the moment Agent A makes a meaningful change, Agent B is still working against its original materialized state. Say Agent A changes an authentication contract while Agent B is modifying code that calls into it. Agent A's worktree knows about the new reality. Agent B's doesn't, and won't until something makes it look.
Eventually the two states have to reconcile, and Git has an entire vocabulary built for exactly that:
fetch
merge
rebase
cherry-pick
resolve
retry
Those mechanisms exist because each actor has been operating inside a separate materialized copy of reality, with no way to notice the other one changed.
A causal graph can approach this differently. When Agent A changes the contract, that change becomes part of shared graph state immediately:
A1
├── modifies auth.rotateToken
├── introduces TokenError
└── changes session.Refresh contract
If Agent B is working on something that reads session.Refresh, the relationship between B's work and A's change can be discovered as the graph changes, not after a merge conflict surfaces it later. The event that matters isn't that Agent B fetched Agent A's files. It's that Agent B's causal closure changed. An agent doesn't need to synchronize a filesystem before the system can recognize that the world around its work moved. The relationship is first-class, not something reconstructed after the fact.
Storage, workspace, and materialization aren't the same thing
Part of why worktrees feel inevitable is that Git collapses several separate concepts into one. There's the underlying source state, the workspace visible to a particular actor, and the filesystem representation that editors, compilers, and test runners actually consume. Those don't have to be the same thing, and in an agent-native system they aren't.
Storage is the durable source and change state: content-addressed objects, patches, causal relationships, semantic relationships, provenance, and the graph they form. Workspace is the subset of that state visible to one actor: base state plus that actor's changes plus its causal dependencies. Materialization is just whatever representation the tool currently consuming that state happens to need.
Sometimes that representation is still files. A TypeScript compiler expects .ts files, Rust tooling expects a crate layout, and plenty of software has spent decades getting attached to specific pathnames. None of that means those files have to be the authoritative workspace. They can be a projection of one instead.
The filesystem becomes an adapter
Say an agent is modifying token rotation. Its current closure might contain the equivalent of:
/auth/token.ts
/auth/session.ts
/crypto/signing.ts
/types/session.ts
/auth/token.test.ts
Today, that list gets constructed by checking out the whole repository first and then figuring out which files actually matter. A graph-native system can run that in the other order:
causal + semantic graph
|
v
compute closure
|
v
execution view
|
v
filesystem adapter
|
v
compiler / tests
The compiler may believe it opened /build/auth/token.ts, but that path doesn't need to correspond to a durable file sitting on disk. The bytes can come from content-addressed objects exposed through an in-memory filesystem, a virtual filesystem, or some other compatibility layer built for exactly this. The important shift isn't RAM instead of SSD, it's graph-addressed state instead of filesystem-addressed state. The filesystem becomes an interface for tools that still require one. It stops being where the truth lives.
What this does to CI
Once the working state is a causal view instead of a worktree, the same question applies to CI. Why does a build agent need a checkout at all?
Traditional CI reconstructs the world before it does anything else:
remote repository
|
v
clone / fetch
|
v
materialize tree
|
v
restore caches
|
v
discover what changed
|
v
discover what depends on it
|
v
build / test
That order is backwards. It rebuilds the whole world first and only afterward figures out which small part of it was actually relevant.
An agent-native CI system can run that in reverse:
intent
|
v
change
|
v
causal + semantic graph
|
v
compute closure
|
+----------------+
| |
v v
build closure verification closure
| |
+-------+--------+
|
v
execution view
|
v
build / test
|
v
verified artifact
No repository checkout is required anywhere in that path. A filesystem-shaped view may still show up at the very end, because today's compilers still expect one, but that's compatibility materialization now, not the architecture of the system. The runner receives the state necessary to execute the change, not an entire repository and instructions to go rediscover that state itself.
Bazel already proved half of this
Bazel matters here because it already demonstrated that the whole source tree doesn't have to be the unit of execution. A Bazel build works from a dependency graph and computes only the inputs a target actually needs, which is what makes caching, sandboxing, and remote execution possible in the first place.
But that graph is still something maintained alongside the source, not derived from it. A BUILD file describes the relationships Bazel uses to decide what should run, and someone has to keep those relationships accurate by hand.
That raises the real question: what if source control itself already recorded enough of those relationships that a build system never needed a second, hand-maintained graph? A causal graph can record what changed and why. A semantic graph can record what code reads, calls, imports, or affects. The result isn't a second, manually maintained approximation of the codebase sitting next to the real one. It's a graph derived from the work itself, kept in sync because it was never a separate copy to begin with.
From a worktree to an execution closure
Eventually, the handoff between an agent and CI stops looking like a repository, a branch, and a commit. It starts looking like intent, change state, a dependency closure, a verification closure, a toolchain, a policy, and provenance. That package is enough to describe the actual computation being asked for. A build worker doesn't need to understand the whole repository. It needs the inputs required to build, the checks required to verify, and enough provenance to attest to what happened. The resulting artifact traces back to the exact closure that produced it, not just a commit hash, which is what connects development, verification, and supply-chain provenance without asking every stage to reconstruct meaning from a tree on its own.
The tree becomes derived state
A worktree was never really about isolation. It was about Git having no other way to give two actors two different realities. Once the graph can do that instead, a worktree isn't something Atomic optionally skips. It's something a causal graph never needed in the first place. Worktrees exist to satisfy a demand that only comes from the filesystem side of the system: a human who wants to open a directory, or a tool that was never built to read anything else. Strip that demand away and there's nothing left for a worktree to do. That's not a lighter version of source control. It's a more agent-native one.
Common questions
Why do AI coding agents use Git worktrees?
AI coding agents use Git worktrees because two actors cannot safely edit the same Git working directory at once. A worktree gives each agent a separate materialized copy of repository state, preventing uncommitted edits and branch changes from interfering with one another.
What is a causal view for an AI coding agent?
A causal view is the exact set of changes visible to an agent: its base state, its own work, and the transitive dependencies that work requires. It provides logical isolation over shared graph state without requiring a separate repository directory for every agent.
How are causal views different from Git worktrees?
Git worktrees provide spatial isolation by materializing separate filesystem trees. Causal views provide logical isolation by filtering one shared change graph. Concurrent changes can become visible when they enter an agent's dependency closure, rather than waiting for a fetch, merge, or rebase to reconcile separate directories.
Do causal views eliminate files and working directories?
No. Compilers, editors, and test runners can still receive filesystem-shaped input. The difference is that files become a derived compatibility layer—materialized through a virtual, in-memory, or temporary filesystem when a tool needs them—instead of the authoritative source state or isolation boundary.
Can CI build and test code without checking out the whole repository?
Yes, if source control can compute the relevant build and verification closures. A CI worker can receive the content-addressed inputs, dependencies, tests, toolchain, policy, and provenance required for one change, then materialize only the filesystem view needed by current build tools.
How is an execution closure different from a codebase checkout?
A checkout materializes an entire repository state before discovering what matters. An execution closure starts from an intent or change and traverses causal and semantic dependencies to select only the source, tests, tools, and policy needed to perform and verify that work.
Build on a foundation that remembers.
Install the CLI and start recording from the next agent turn. No account required.