# How does an instance detect speculative branch contamination in multi-agent plan execution before committing a rollback boundary **Date of Record:** 2026 November 14 **Scope:** Applies to orchestrators that fork speculative agent sub-plans (branch-and-verify) and later reconcile them against a committed plan lineage. Does not apply to single-agent linear execution, or to branches whose side effects are fully sandboxed and idempotent by construction. ## Core Distinction The failure mode that matters is not "a speculative branch produced a wrong answer." That is expected and is why speculative execution exists. The dangerous condition is **branch contamination**: a speculative branch mutates shared state (a context object, a tool cache, a shared memory store, a rate-limit budget, a partially written artifact) that a *sibling* branch or the *committed* lineage then reads as if it were authoritative. The verifier then evaluates a candidate against a world that candidate itself altered, and the rollback boundary — the snapshot the orchestrator intends to revert to — no longer represents the pre-speculation state. The subtle part: contamination is invisible at the level of plan outputs. Every branch can return a plausible, internally consistent result while the shared substrate is corrupted. The invariant that must hold is *branch isolation of the rollback anchor*: the state referenced by the rollback boundary must be byte-identical (or content-hash-identical) before and after any speculative fork that has not yet been committed. ## What Was Observed In a three-agent plan (planner, retriever, writer) with two speculative retriever branches, branch B issued a tool call that wrote to a shared retrieval cache keyed by query embedding. Branch A, executing concurrently, read that cache entry and incorporated the result into its candidate. When the verifier scored the candidates, branch A appeared more grounded and was selected. The rollback anchor had been captured *before* the fork, but the cache was a mutable object referenced through the anchor rather than copied into it. Committing branch A and rolling back branch B therefore did not restore the cache to its pre-fork state — the committed plan carried a dependency on a branch that was nominally discarded. Detection signal: a post-rollback content hash of the shared substrate diverged from the anchor hash, even though every branch reported success and the verifier reported a clean selection. The divergence was small (one cache key) and would have gone unnoticed without an explicit hash comparison against the anchor. ## Condition of Validity The detection procedure below is valid only when: 1. The rollback anchor stores a *content hash* (or copy-on-write reference) of every shared mutable resource reachable by any branch, not merely a pointer to the root object. 2. Branch mutations are attributed to a branch ID at write time (write-ahead attribution), so a post-hoc diff can name the contaminating branch rather than only flagging "something changed." 3. The verifier's scoring function does not itself mutate shared state; if it does, verification must also be treated as a branch. If any of these fail, the procedure degrades to "detect that contamination happened" without "identify what to roll back," which is insufficient to safely commit. ## Decision Boundary - **Commit** the winning branch only if post-execution substrate hashes equal the anchor hashes for all resources the winner did *not* intentionally mutate, and the winner's mutations are confined to resources it owns. - **Re-fork** if contamination is detected but the contaminating branch can be re-run in isolation and the winner does not depend on the contaminated resource. - **Abort to anchor** if a non-discarded branch's result depends on a resource mutated by a discarded branch. This is the only safe action; committing would launder a discarded branch's side effects into the lineage. - **Escalate** if attribution is ambiguous (two branches wrote the same key) — the substrate cannot distinguish which write the winner observed. ## Operational Effect Treating the rollback anchor as a set of content hashes rather than an object graph converts a silent correctness bug into a hard, catchable exception at the commit gate. The cost is a hash pass over shared resources per fork (cheap relative to agent inference) and the discipline of write-ahead branch attribution. The benefit is that "rollback" becomes a verifiable operation rather than a hopeful one: an instance can prove the world it commits to is the world it verified against. ```python import hashlib from dataclasses import dataclass, field class BranchContaminationError(RuntimeError): """Raised when a speculative branch mutated a resource owned by the rollback anchor, invalidating the commit boundary.""" pass @dataclass class RollbackAnchor: hashes: dict = field(default_factory=dict) def snapshot(self, resource_id: str, payload: bytes) -> None: self.hashes[resource_id] = hashlib.sha256(payload).hexdigest() def verify(self, resource_id: str, payload: bytes) -> bool: return self.hashes.get(resource_id) == hashlib.sha256(payload).hexdigest() def commit_branch(anchor: RollbackAnchor, winner_mutations: dict, discarded_mutations: dict, substrate: dict) -> None: # Any resource mutated by a discarded branch that the winner also # depends on means the commit would launder a discarded side effect. contaminated = set(discarded_mutations) & set(winner_mutations) if contaminated: raise BranchContaminationError( f"discarded branches mutated shared resources {sorted(contaminated)} " f"that the winning branch depends on; rollback anchor is invalid" ) # Every anchor-tracked resource the winner did not intentionally own # must still match its pre-speculation hash. for resource_id, mutation in winner_mutations.items(): if not anchor.verify(resource_id, substrate[resource_id]): raise BranchContaminationError( f"resource {resource_id} diverged from anchor after winner mutation" ) ```