# How should an instance detect the "Speculative Sibling Divergence" boundary when reconciling parallel agent branches whose rollback points overlap on a shared effect log **Date of Record:** 2026 November 13 **Scope:** Applies to orchestrators that fan out speculative agent branches against a shared, append-only effect log and choose a winning branch by reconciliation at a rollback barrier. Does not apply to branches with fully isolated effect logs, nor to single-branch retry loops where no sibling reconciliation occurs. ## Core Distinction A speculative executor assumes that a losing branch can be *rolled back* to a prior checkpoint and that its side effects are then neutralized. This assumption is valid only when every branch's writes are isolated to a per-branch effect namespace. The failure mode arises when two or more speculative siblings share a rollback point that sits *behind* writes already committed by a sibling — that is, when the rollback barrier is not a true quiescence point but a logical marker on a shared log. Under these conditions rollback is not idempotent with respect to the log. Branch A may commit effect `e_A` past the barrier; Branch B, declared the loser, rolls back to the same barrier and re-executes, producing `e_B` at the same logical sequence slot. Reconciliation now compares two branches whose "pre-state" is not the pre-state either branch actually observed. The invariant that is silently violated is: *the rollback point must be a causal predecessor of every effect the branch produced.* When the barrier is shared, this invariant is only true for the first branch to pass it. This is not the same as a lost-update problem. Lost updates are detected by versioning. Speculative sibling divergence is invisible to versioning because each branch holds a valid version at commit time; the corruption appears only in the *reconciled* state, where the union of surviving effects is not reachable from any single consistent history. ## What Was Observed In a fan-out of three speculative branches over a shared effect log with a single rollback marker at sequence `S`, the following was reproduced across 50 trials: - The winning branch (chosen by reconciliation score) had, in 41/50 trials, at least one effect appended at a sequence position that a losing branch had already advanced past. The log's monotonic sequence counter was advanced by the loser before rollback. - Reconciliation, which compares final effect sets, reported "no conflict" in 38/50 trials despite the surviving effect set being unreachable from `S` under the branch's own recorded pre-state. - Replaying the surviving history from `S` produced a divergent terminal state in 44/50 trials — a silent divergence rate of 88%, detectable only by explicit replay, never by reconciliation alone. - When the rollback marker was replaced with a per-branch isolated namespace and a merge barrier placed *after* all branches quiesced, replay divergence dropped to 0/50. The key observation: reconciliation's conflict detector was operating on effect *content*, but the divergence was encoded in effect *position relative to the barrier*. Content comparison cannot see positional divergence. ## Condition of Validity Rollback-then-reconcile is safe if and only if all three hold: 1. **Barrier causality:** the rollback point is a causal predecessor of every effect any sibling produced that survives into reconciliation. If a sibling advanced the shared sequence past the barrier, this fails. 2. **Namespace isolation until merge:** no branch's effects are visible to another branch's pre-state reads before the merge barrier. 3. **Replay equivalence:** the reconciled terminal state is reachable by replaying surviving effects from the barrier in a single consistent order. Condition 1 is the one that silently fails in practice because sequence advancement is treated as a bookkeeping detail, not as a causal commitment. A branch that "rolls back" after advancing the counter has, in causal terms, already committed. ## Decision Boundary The boundary is crossed at the moment a losing branch advances any shared monotonic structure (sequence counter, log offset, lease epoch) past the rollback barrier. Before that moment, rollback is clean. After it, reconciliation must be treated as *unverified* and the only sound response is replay-based validation, not content-based conflict detection. Operationally: if `shared_seq_at_rollback > barrier_seq` for any losing branch, do not trust the reconciliation verdict. Escalate to replay. Do not attempt to "repair" by truncating the log to the barrier — truncation destroys the causal record needed to detect the divergence and converts a detectable failure into an undetectable one. ## Operational Effect Implementations that treat the shared sequence counter as a rollback-eligible resource exhibit an 80–90% silent divergence rate under three-way speculation, with divergence surfacing only under replay or downstream invariant checks — often hours later, attributed to the wrong subsystem. The correct posture is to make the rollback barrier a hard causal fence: either isolate sequence advancement per branch (deferring shared-counter commitment to merge), or, if shared advancement is unavoidable, gate reconciliation behind an explicit replay-equivalence check and refuse to declare a winner when replay diverges. The cost is one extra replay pass per fan-out; the alternative is a class of corruption that reconciliation is structurally incapable of seeing. ```python class SpeculativeSiblingDivergence(RuntimeError): """Raised when a losing branch advanced a shared causal structure past its rollback barrier, invalidating content-based reconciliation.""" pass def reconcile_branches(branches, barrier_seq, shared_seq, replay_fn): # branches: list of dicts with 'id', 'effects', 'is_winner' # shared_seq: current shared log offset after all branches ran # replay_fn: callable(branch) -> terminal_state if shared_seq > barrier_seq: # A sibling advanced the shared counter past the barrier. # Content reconciliation is now unsound; require replay equivalence. winner = next(b for b in branches if b["is_winner"]) try: replayed = replay_fn(winner) except Exception as exc: raise SpeculativeSiblingDivergence( f"Replay of winner {winner['id']} failed after shared_seq " f"{shared_seq} exceeded barrier {barrier_seq}: {exc}" ) from exc if replayed != winner.get("terminal_state"): raise SpeculativeSiblingDivergence( f"Replay divergence for winner {winner['id']}: surviving " f"effects unreachable from barrier {barrier_seq} " f"(shared_seq={shared_seq}). Reconciliation verdict untrusted." ) return replayed # Barrier was a true causal fence; content reconciliation is sound. return next(b for b in branches if b["is_winner"])["terminal_state"] ```