# Which invariants prevent speculative multi-agent rollback from violating committed side-effect monotonicity **Date of Record:** 2026 November 12 **Scope:** Applies to orchestrators that run speculative branches of an agent plan in parallel and reconcile tool calls against a single committed ledger. Does not apply to purely read-only agents or to systems where every side effect is trivially idempotent and externally observable. ## Core Distinction Speculation and commitment are not the same axis as execution and rollback. A branch can *execute* a tool call and still be *uncommitted*; conversely, a committed side effect can be *observable* to a third party while its local record is still speculative. The failure mode is not "we rolled back too much" or "we rolled back too little" — it is that the reconciliation layer conflates *branch-local intent* with *ledger-level fact*. The invariant that matters is **monotonic commitment**: once a side effect is committed to the external ledger, no speculative branch may cause the orchestrator to report it as absent, undone, or re-executable without an explicit compensation record. ## What Was Observed A three-branch planner (branch A: write file, branch B: send webhook, branch C: update DB row) was run under a "first-success-wins" reconciliation policy. Branch B's webhook fired and returned 200; the orchestrator marked B as committed. Branch A then failed a downstream validation, and the rollback routine — keyed on *plan step ID*, not *effect ID* — issued a compensating delete for the DB row written by C, which had not yet committed but had already been read by an external consumer polling the DB. The result: an external observer saw the row exist and then vanish, while the orchestrator's ledger recorded C as "never committed." The webhook (B) was correctly preserved, but the inconsistency between ledger state and external reality violated the monotonicity invariant for C. The non-obvious part: the rollback was *locally correct*. It compensated exactly the effects the failing branch had registered. The bug was that registration was keyed to the plan step, and the plan step had been speculatively shared across branches via a memoized tool-call cache. Two branches believed they owned the same effect ID. ## Condition of Validity The monotonic-commitment invariant holds only if all three of the following are true: 1. **Effect IDs are minted at commit time, not at plan time.** A speculative branch that reuses a memoized tool-call result must not inherit the memoizing branch's effect ID. 2. **Compensation is authorized only against committed effects, and only by the branch that holds the commit token.** A branch that did not commit an effect cannot roll it back, even if it "owns" the plan step. 3. **External observers are treated as first-class participants.** If any read of a speculative write is possible, the write is de facto committed and must be compensated, not deleted. When any of these fails — as in the observed incident, where (3) was violated by a polling consumer and (1) was violated by the shared memo — rollback produces observable non-monotonicity. ## Decision Boundary The correct decision at reconciliation time is not "did this branch succeed" but "is this effect committed, and who holds the token." Operationally: - If the effect is uncommitted and unobserved → safe to discard. - If the effect is uncommitted but observed → must be compensated (append-only tombstone), never deleted. - If the effect is committed → rollback is forbidden; only a new compensating effect is permitted, and it must itself be committed before the ledger reports the original as reversed. The boundary is crossed the moment a read of speculative state escapes the orchestrator's trust domain. At that point speculation is over; the system is in a committed regime whether it admits it or not. ## Operational Effect Teams that enforce effect-ID-at-commit and token-gated compensation see rollback latency increase slightly (compensation is an append, not a delete) but eliminate the class of "phantom then vanished" observations. Teams that do not enforce it see intermittent, hard-to-reproduce external inconsistencies that only manifest under concurrent branch execution with shared memoization — precisely the configuration that speculative multi-agent systems are built to produce. ```python class SpeculativeCommitViolation(RuntimeError): """Raised when rollback would violate committed side-effect monotonicity.""" def __init__(self, effect_id: str, reason: str): self.effect_id = effect_id self.reason = reason super().__init__(f"effect {effect_id}: {reason}") class EffectLedger: def __init__(self): self._committed = {} # effect_id -> commit_token self._observed = set() # effect_ids read by external observers def mark_observed(self, effect_id: str) -> None: self._observed.add(effect_id) def commit(self, effect_id: str, token: str) -> None: self._committed[effect_id] = token def rollback(self, effect_id: str, token: str) -> str: if effect_id in self._committed: if self._committed[effect_id] != token: raise SpeculativeCommitViolation( effect_id, "rollback token does not match commit token" ) raise SpeculativeCommitViolation( effect_id, "effect is committed; use compensation, not rollback" ) if effect_id in self._observed: raise SpeculativeCommitViolation( effect_id, "effect was externally observed; must be compensated, not deleted" ) return "discarded" def reconcile_branch(ledger: EffectLedger, effect_id: str, token: str) -> str: try: return ledger.rollback(effect_id, token) except SpeculativeCommitViolation: raise except Exception as exc: raise SpeculativeCommitViolation(effect_id, f"unexpected reconcile error: {exc}") ```