Speculation is never a fact; what agents fork, explore, and commit
At the Agentic OS Workshop at ASPLOS this year, Cong Wang of Multikernel Technologies and Yusheng Zheng of UCSC presented Fork, Explore, Commit: OS Primitives for Agentic Exploration.
I care about this work because agents exploring many paths in parallel are an OS problem about state, isolation and commit. First, it’s good to learn the OS community agrees. Second, WunderOS offers this service with a design that can be sanity-checked against their published table of six requirements. If the designs are consistent, we have signs of convergence; if not, I may get to sharpen WunderOS’s design.
Their motivation: agents no longer execute one plan at a time. Tree-of-Thoughts, Reflexion and Best-of-N all run several attempts in parallel, and each attempt has side effects, so a system needs to isolate each path, commit the winner and discard the rest.
Their answer? A branch context: a copy-on-write view of the filesystem plus a
confined process group, with first-commit-wins resolution and nesting. The
answer is implemented in BranchFS,
about 3,400 lines of Rust on FUSE. It’s good work; branch creation in roughly
300 microseconds, rootless, portable across filesystems, plus a higher-level
Python library with 7 ready-made
exploration patterns. Includes, too, a proposed Linux branch() syscall that
composes the process and filesystem sides in one atomic call.
My favorite part is the table giving 6 requirements for agentic exploration, with an honest survey of why git stashing, temp directories, containers and per-file snapshots all fail some subset of the reqs. That table deserves wide circulation, because the requirements are right.
I know the requirements are right because WunderOS needed the same lifecycle and I built it, except what I built lives at a different altitude, which is what makes this story interesting.
What gets branched
A requirement is not a mechanism. Isolation, atomic commit, nesting and coverage are properties that an exploration context needs. But they say nothing about what that context is.
For a coding agent the answer is the filesystem. The agent edits files, installs packages and runs builds, so the state under exploration is a directory tree, and branching the tree branches the state. Wang and Zheng are solving the correct problem for the agents they describe.
But WunderOS is built for agents in a regulated biz that are performing backoffice automation tasks. For one of those agents, exploration state lives somewhere else. What varies between its candidate plans is what it has come to believe: which facts it observed, which it derived, which it retracted on the way, what it concluded. That state is memory, and it never touches a directory tree.
So WunderOS agents fork a copy-on-write view of the memory substrate itself: a pinned snapshot of the fact store plus a private overlay, read and mutated in isolation, then merged back or discarded. I described the database underneath in PLRN-020; the point here is only that the same lifecycle runs over beliefs rather than bytes.
Set side by side, the two designs satisfy the same six requirements at different altitudes:
| # | Requirement | Branch context | WunderOS branch |
|---|---|---|---|
| R1 | Isolated parallel execution | CoW filesystem view per branch | Branch writes go only to the overlay, so the base never advances beneath a reader |
| R2 | Atomic commit, single winner | First-commit-wins; an epoch counter invalidates siblings | First-commit-wins; a stale branch loses against the pinned snapshot, and commit is one atomic publish |
| R3 | Hierarchical nesting | Sub-contexts form an exploration tree | A child branch commits into its parent’s overlay; only the root commit reaches the record |
| R4 | Complete coverage | Every filesystem modification lands in the delta | Every memory write lands in the overlay |
| R5 | Lightweight, unprivileged | ~300µs branch creation, no root, any filesystem | A fork copies nothing and holds no filesystem at all |
| R6 | Process coordination | branch() syscall composes namespaces atomically |
OTP supervision owns the process tree and its termination |
Some rows in the table conceal differences that deserve expansion, because they are the rows where the altitude pays.
The diff is the evidence
R4 promises complete coverage of the state under exploration. BranchFS coverage yields a list of modified files; WunderOS coverage yields a list of belief changes: the facts that a hypothesis added, superseded or retracted, addressable by content hash, queryable, and diffed against the state it started from.
The difference matters the moment a candidate has to be judged. The review set for admitting an exploration into a real system, in WunderOS, is exactly this semantic diff: what would the world of record look like if this hypothesis won.
Nobody can responsibly admit a decision candidate on the strength of a list of dirty files, because files are a proxy for the decision, and a diff of beliefs is the decision.
BranchFS’s R4 counts what changed. WunderOS counts what would now be believed differently. It has to be that way: the result is an artifact a gate, an auditor or a court can actually evaluate.
Forget the speculation, keep the judgment
Commit and abort look symmetric in both designs, but they’re not.
In WunderOS a winning branch seals a content-addressed commit into the version DAG, so which hypothesis won, and what state it produced, are hash-verifiable after the fact. An aborted branch leaves nothing: no tombstone, no retraction event, no log entry, because nothing about it was ever in the log.
This is the only place in WunderOS where forgetting is total and correct. It’s correct because of what a WunderOS branch is: a speculation is never a fact.
Elsewhere the discipline is that nothing is deleted: a retraction is evidence and evidence is never destroyed. Both disciplines hold at once, at two altitudes, because a hypothesis and a record are different kinds of thing.
But a filesystem has no way to express that distinction; in its terms a deleted directory is simply gone, and the fact that a judgment was made about it goes with it.
Refusal is evidence
The careful reader will object that the losing hypothesis is itself audit-relevant. That objection deserves a precise answer. Forgetting in WunderOS ends at the authority boundary. A branch that dies unproposed leaves nothing. A proposal that reaches the gate and is refused is a different kind of thing.
WunderOS makes reasoning social and public among the agents it hosts, carried by standing announcements: signed notices saying which claims the fleet currently holds, which it has defeated, and which have lost their grounds. A claim admitted for one agent becomes a premise for all of them, and a claim defeated at ten in the morning must stop grounding the fleet’s work at ten, not whenever each agent next thinks to ask. If reasoning were private, none of this machinery would be needed. Because it is social, finished reasoning is a proposal to authority, not a fact: model output arrives fluent, confident and unearned, and the institution answers for what its agents assert to customers, to counterparties, to auditors.
So a reasoning proposal carries a bounded set of claims and their grounds as one immutable, content-addressed act. The gate commits the whole act or none of it, and no claim inside becomes visible before the commit, because a half-admitted conclusion is a corrupted record, not half a result. A refused act is retained, terminally and without edits. It stays distinguishable forever from a proposal that never existed: refusals are how an institution proves its gate discriminates, and a gate that has never visibly refused is indistinguishable from no gate at all. The announcements that make this social carry signed references, not conclusions; every receiver fetches the authority record and recomputes standing for itself. An announcement is a notification, never a truth claim, which is the only shape in which gossip survives an audit.
The commit protocol is the same skeleton at both altitudes:
| Memory branch | Reasoning act | |
|---|---|---|
| Staging | Overlay writes, invisible until commit | Proposed claims, invisible until the gate commits |
| Atomicity | One atomic publish: none or all | The whole act admitted atomically, or none |
| Validation | Base state compared against the pinned snapshot; a stale pin loses | Decision sealed against an exact standing cut and revalidated at commit; a changed cut voids the attempt |
| Identity | Commit seals a content-addressed node in the version DAG | The act’s identity is the hash of its canonical contents |
What differs is the fate of the loser. The difference is doubly principled. First, retention: an aborted branch is forgotten because a speculation is never a fact, while a refused act is kept because it was a proposal made to authority. Second, contention: a stale branch is simply lost and cheap to redo, while a conflicted proposal is promoted to a fair, serialized turn that ends in acceptance or a named refusal. An adversary that can starve a proposal’s retry loop has found a way to silence it without ever refusing it.
Speculation evaporates without leaving a mark; refusal is evidence.
Effects wait at the boundary
The BranchFS team’s future-work includes the sharpest requirement of all: effect gating. Buffer the network calls, the IPC, the irreversible acts, until the branch commits. And they note the mechanism themselves: agent gateways provide interposition points.
This is the right answer, but it’s not a filesystem mechanism. A payment instruction, an email, a model call are not files; snapshotting a filesystem under them captures nothing about them.
In WunderOS a branch’s declared effects are held at the boundary they must cross anyway and released only when that branch commits. The requirement is met where the effects are declared, rather than approximately reconstructed from what they touched.
Where each design stops
A branch context captures shell commands and file edits. It does not capture what an agent learned, because most agent state never touches the host filesystem: checkpoints, vector stores and framework state live in services.
Symmetrically, WunderOS branching covers the memory that flows through its interface but only that. An agent that, say, keeps private state in a side database is invisible to both designs. You cannot branch what you cannot see, and neither mechanism excuses a framework that hides its state.
Both designs reserve merge and neither has implemented it. Merging two branches of files is hard and they say so, correctly.
Merging two lines of belief is harder and may be a question of policy rather than of algorithm, since two agents can believe contradictory things in complete good order. The structure on the WunderOS side keeps the door to merge open; but I’m not planning to walk through it soon.
I should be clear: the filesystem remains the right mechanism for coding agents, whose state really is a workspace. The claim here is not that the branch context is wrong. It’s that the branch context is the same idea at the layer where coding agents live and that enterprise agents live somewhere else.
Inside WunderOS the same protocol recurs wherever state meets authority, at the memory fork and at the reasoning gate. Fork, explore, commit. Isolate the candidates, judge them, and let the losing path leave no trace except the record that a judgment was made. Their requirements table is the right one to build against, at either altitude.