# How does an instance detect rollback contamination when speculative subtasks share a mutable reconciliation ledger **Date of Record:** 2026 November 13 **Scope:** Applies to any agent orchestration layer that runs speculative parallel subtasks against a shared, mutable conflict-reconciliation ledger and rolls back the losers of a race. Does not apply to fan-out patterns where subtasks are pure and side-effect free, nor to systems where rollback is implemented as immutable copy-on-write snapshots of the entire ledger. ## Core Distinction There are two meaningfully different notions of "rollback" in parallel agent systems, and conflating them is the source of the failure mode this record documents. The first is **value rollback**: a subtask's proposed writes are simply discarded from the candidate set. The subtask never committed, so there is nothing to undo. This is cheap, always safe, and is what most orchestration frameworks implement. The second is **effect rollback**: a subtask was speculatively *committed* to a shared reconciliation ledger (or had its effects observed by downstream readers) before the race was resolved, and the loser must now be un-committed. This is the dangerous case. The loser's writes may have been read, merged, or used as a conflict-resolution baseline by a concurrent winner. Rolling back the loser by subtracting its contributions does not restore the ledger to its pre-speculation state, because the winner's reconciliation decisions were *conditioned* on a ledger that contained the loser's data. The boundary failure is this: **an instance cannot distinguish, from the ledger's current state alone, between a clean post-rollback ledger and a contaminated one.** Both look structurally valid. The contamination is a function of history, not of current contents. ## What Was Observed A three-way speculative fan-out resolved a conflict over a shared resource allocation record. Subtask A (speculative, later lost) wrote `owner=svc-alpha` into the ledger at epoch 41. Subtask B (winner) read the ledger at epoch 42, observed `owner=svc-alpha`, and — because the reconciliation policy treats an existing owner as a *hard constraint* — recorded a conflict-resolution entry that explicitly deferred to `svc-alpha` and only appended a secondary grant. Subtask A was then rolled back. The ledger's `owner` field reverted to `unset`. The resulting ledger was internally consistent: no dangling references, no schema violations, all epochs monotonic. Post-rollback validation passed. But the ledger now contained a grant whose justification ("deferred to svc-alpha") referenced an owner that had never legitimately existed. Downstream, a fourth agent read the grant, inferred that `svc-alpha` was the authoritative owner, and re-established it — resurrecting a rolled-back effect through a second-order path. The critical observation: **the rollback was correct and the ledger was still wrong.** The failure was not in the rollback mechanism but in the assumption that rollback commutes with concurrent reads. A second, subtler observation: the same pattern appeared with *timing skew* rather than ownership. A loser's speculative write advanced a version counter that a winner used to order its own writes. Rolling back the loser's value did not roll back the counter's effect on the winner's ordering, so the winner's entry retained a causal dependency on a phantom predecessor. ## Condition of Validity The naive rule "roll back losers, keep winners" is valid **if and only if** one of the following holds: 1. Speculative writes are invisible to other subtasks until the race resolves (isolation), or 2. The ledger supports full causal rollback, meaning rollback also invalidates and re-derives any winner entries whose reconciliation decisions read the rolled-back state, or 3. The reconciliation policy is *monotonic*: no winner decision can be conditioned on the presence of a loser's write in a way that outlives it. Condition 1 is the common assumption and the one most frequently violated, because speculative execution exists precisely to let subtasks observe each other's partial progress to reduce duplicated work. Condition 2 is expensive and rarely implemented. Condition 3 is the only cheap one, and it is a property of the *policy*, not the *engine* — which means it is usually undocumented and untested. When none of the three holds, any rollback of a subtask that was observable before resolution is unsafe, regardless of how cleanly the value is removed. ## Decision Boundary An instance should treat a speculative subtask as **effect-rollback-safe** only when all of the following are true; otherwise it must escalate to full causal re-derivation or refuse to speculate: - The subtask's writes were never visible outside its own epoch window before resolution (check the ledger's read-set audit, not the write-set). - No winner entry's reconciliation rationale references the loser's identity, resource, or version stamp. - The loser did not advance any monotonic counter, version, or ordering token that a winner consumed. - The reconciliation policy is declared monotonic, or the engine can prove no winner read the loser's region. If any check fails, the correct action is not "roll back more carefully" — it is to mark the entire reconciliation window as **poisoned** and re-derive all winner entries from a pre-speculation baseline. Partial rollback of a contaminated window produces a ledger that passes every structural check while carrying phantom causal dependencies. The non-obvious part of this boundary: **the detection must happen at resolution time, not at rollback time.** By the time you are rolling back, the winner's decisions are already written and the evidence of contamination (what the winner read) is only recoverable from the audit log. An instance that defers the safety check to rollback has already lost the information it needs. ## Operational Effect Instances that skip this check exhibit a characteristic failure signature: intermittent, non-reproducible resurrection of rolled-back state, typically one to three reconciliation windows after the rollback, mediated by a downstream reader that trusted a winner entry's rationale. The bug does not reproduce under replay because replay reconstructs the ledger without the concurrent read interleaving, so the winner's rationale is regenerated without the phantom constraint. This makes it look like a heisenbug and drives teams toward "just add a lock," which serializes speculation and defeats its purpose. The correct operational posture is to instrument the read-set at resolution time, refuse effect-rollback when the read-set intersects the loser's write region, and treat monotonic reconciliation policies as a first-class, testable property rather than an implicit assumption. ```python class RollbackContaminationError(RuntimeError): """Raised when a speculative loser's effects were observable to a winner before resolution, making value rollback insufficient.""" def resolve_speculation(winner_id, losers, ledger, read_audit): """ Resolve a speculative race. Returns the set of subtasks safe to value-rollback. Raises RollbackContaminationError if any loser contaminated the window. """ safe_to_rollback = set() for loser in losers: loser_writes = ledger.write_region(loser.epoch) contaminated_by = [] for winner in winner_id: winner_reads = read_audit.reads_of(winner, since=loser.epoch) if winner_reads & loser_writes: contaminated_by.append(winner) if loser.advanced_monotonic_token(): contaminated_by.append(f"") if contaminated_by: raise RollbackContaminationError( f"Loser {loser.id} was observable before resolution; " f"winners {contaminated_by} conditioned decisions on it. " f"Window is poisoned; re-derive from pre-speculation baseline " f"epoch {loser.epoch - 1} instead of value-rollback." ) safe_to_rollback.add(loser.id) return safe_to_rollback ```