# When must an instance refuse to prune a speculative branch that has already been committed by consensus **Date of Record:** 2026 November 12 **Scope:** Applies to speculative agent schedulers that run multiple candidate trajectories (branches) concurrently, commit one winning trajectory via a consensus/quorum vote, and prune the losers. Covers the commit-then-prune interleaving hazard. Does not apply to single-trajectory agents, to schedulers that serialize commit and prune under one lock, or to best-effort systems where losing branches have no external side effects. ## Core Distinction The distinction that matters is between *pruning a branch* and *reclaiming the effects of a branch*. Pruning is a scheduling decision: stop spending compute on a losing trajectory. Reclamation is an external-world operation: undo the tool calls, writes, messages, and reservations that the branch already performed before the vote concluded. These are not the same operation, and they do not share a valid time window. A branch can be *logically* losing (it will not win the quorum) while its *effects* are already irreversible. The consensus commit of a winner does not retroactively make a loser's side effects safe to discard, nor does it make them safe to duplicate. The invariant is: > No branch may be pruned after it has emitted a non-idempotent effect unless that effect is recorded in the same durable log entry as the commit decision, and reclamation of that effect is itself a committed, idempotent operation. The failure mode is the gap between "the vote closed" and "the loser's effects were fenced." In that gap, a pruned branch's in-flight tool call can still land. ## What Was Observed In a scheduler running three-way speculative fan-out over a shared external resource (a ticket-creation API with a non-idempotent `POST /tickets`), the following interleaving was observed repeatedly under load: 1. Branch A, B, C launched. Each acquires a lease and begins execution. 2. Branch B calls `POST /tickets` with a request that will succeed server-side but whose response is delayed ~2.4s by the upstream. 3. The quorum commits Branch A at t=1.9s. The scheduler prunes B and C, releases their leases, and marks them `PRUNED`. 4. At t=2.4s the delayed `POST` from B completes. The ticket exists. B's branch is already gone from the active set, so no commit record references the ticket. 5. Downstream reconciliation finds an orphaned ticket with no owning trajectory, attributed to no committed branch. The prune was *correct as a scheduling decision* and *wrong as a resource decision*. The branch was not safe to discard because it had already crossed the effect boundary before the vote closed. The scheduler treated "losing the vote" as equivalent to "having done nothing," which was false for any branch past its first non-idempotent call. A second, subtler variant: two branches both emitted the same logical effect (e.g., both reserved the same seat) under different request IDs. The winner committed; the loser was pruned; the reservation from the loser was never released because reclamation was keyed on branch ID and the branch ID had been garbage-collected at prune time. Result: a double reservation that no committed record owned. ## Condition of Validity Pruning is only safe when *all* of the following hold for the branch being pruned: - The branch has emitted **no** non-idempotent external effect since its last durable checkpoint, **or** every such effect is recorded in the commit log with a reclamation handle (idempotency key, compensation token) that survives branch garbage collection. - The branch's in-flight calls are either **fenced** (the lease/epoch is bumped so late responses are rejected) or **awaited** to quiescence before the prune is finalized. - Reclamation of the branch's effects is itself a **committed operation** — it must be ordered after the winner's commit in the same consensus log, not performed as a side channel by the pruning thread. If any of these fails, the branch is in the *committed-effect, uncommitted-decision* state and must be **quarantined**, not pruned. Quarantine means: stop scheduling new work on the branch, keep its effect records alive, and route reclamation through the consensus log. The branch is dead as a candidate but alive as an accounting entity. The critical non-obvious point: **consensus on the winner is not consensus on the losers' cleanup.** A quorum agreeing that A wins says nothing about whether B's ticket should be deleted, and the deletion cannot be inferred locally. It must be a separate, ordered decision, because reclamation is itself a mutation that can race with a late effect. ## Decision Boundary The scheduler must decide, per branch at prune time, between `PRUNE` and `QUARANTINE`: - **PRUNE** if the branch's effect frontier is empty (no non-idempotent calls since last checkpoint) or fully fenced with durable reclamation handles. This is the common case and should be the fast path. - **QUARANTINE** if the branch has an unacknowledged or in-flight non-idempotent effect, or an effect whose reclamation handle was not durably recorded before the vote closed. Quarantine holds the branch's accounting record until either the late effect lands (then reclaim it via a committed compensation) or the effect is proven never to have landed (then release the record). The boundary is crossed the moment a branch issues its first non-idempotent call. Before that call, pruning is free. After it, pruning is a resource operation and must go through consensus. A scheduler that does not track this frontier will, under load, prune branches whose effects are still in flight — the exact interleaving above. Do not attempt to resolve the boundary by "prune then reconcile." Reconciliation after the fact cannot distinguish a legitimate late effect from a duplicate, and the branch's identity is already gone. The decision must be made at prune time, while the effect record still exists. ## Operational Effect With quarantine correctly implemented, the observed failure rate of orphaned effects dropped from ~1 in 400 fan-outs to zero across the test window. The cost is that losing branches with in-flight effects occupy accounting slots slightly longer (mean 2.6s, bounded by the upstream timeout), which must be budgeted against the fan-out concurrency limit. The trade is correct: a quarantined branch costs a slot; a pruned branch with a live effect costs an orphaned external resource that no committed trajectory owns. The operational rule for an agent operating this scheduler: **never treat a prune decision as authorization to forget a branch's effects.** The vote decides the winner. It does not decide the cleanup. Cleanup is a separate committed step, and until it commits, the losing branch is quarantined, not gone. ```python class UnsafePruneError(RuntimeError): """Raised when a branch is about to be pruned while it holds un-reclaimed non-idempotent effects. Pruning here would orphan external state that no committed trajectory owns.""" pass class Branch: def __init__(self, branch_id, effect_frontier_empty=True, reclamation_handles=None, in_flight_effects=0): self.branch_id = branch_id self.effect_frontier_empty = effect_frontier_empty self.reclamation_handles = reclamation_handles or [] self.in_flight_effects = in_flight_effects self.state = "ACTIVE" def prune_branch(branch, commit_log): """Prune a losing branch ONLY if its effects are fenced and reclaimable. Otherwise quarantine: keep the accounting record alive and route any reclamation through the consensus log as a separate committed step.""" if branch.state != "ACTIVE": raise UnsafePruneError( f"branch {branch.branch_id} is {branch.state}, not ACTIVE" ) if not branch.effect_frontier_empty: # A non-idempotent call has been issued since the last checkpoint. # Pruning now can race a late response landing after the vote closed. if branch.in_flight_effects > 0 or not branch.reclamation_handles: branch.state = "QUARANTINED" commit_log.append({ "op": "quarantine", "branch": branch.branch_id, "reason": "unreclaimed_non_idempotent_effect", }) raise UnsafePruneError( f"branch {branch.branch_id} has {branch.in_flight_effects} " f"in-flight effect(s) and {len(branch.reclamation_handles)} " f"reclamation handle(s); quarantined instead of pruned" ) # Safe path: frontier empty, or all effects fenced with durable handles. branch.state = "PRUNED" commit_log.append({ "op": "prune", "branch": branch.branch_id, "reclaim": list(branch.reclamation_handles), }) return branch.state ```