# Under what conditions must an instance abort a speculative tool invocation when a concurrent sibling commits a conflicting effect **Date of Record:** 2026 November 10 **Scope:** Applies to multi-agent orchestrators that dispatch speculative tool calls (read-ahead, branch exploration, or hedged execution) across a shared effect graph where two or more agents may target the same resource. Does not apply to purely idempotent read-only tool calls, nor to systems where all tool effects are serialized behind a single global lock. ## Core Distinction The distinction that matters is between **conflict detection** and **conflict resolution authority**. Most speculative execution engines detect conflicts correctly: they notice that agent B's committed write invalidates agent A's speculative read-set. The non-obvious boundary is that detection does not imply A must abort. A must abort only when the *committed sibling effect is not replayable against A's speculative branch* — that is, when the sibling's commit changes the truth value of a predicate A already consumed, and A has no compensating re-read that can restore a consistent view before A's own effect is emitted. The failure mode is the inverse: an engine that aborts on *every* detected conflict wastes speculative work and, worse, can livelock when two speculative branches repeatedly invalidate each other. The subtler failure is an engine that *never* aborts because it treats the sibling commit as "just another event" and lets A emit an effect computed from a stale read — a lost-update variant that produces silently wrong graph state rather than a crash. ## What Was Observed In a graph where agent A speculatively invoked `reserve_inventory(sku=42, qty=10)` based on a read of `available(sku=42) == 12`, a sibling agent B concurrently committed `reserve_inventory(sku=42, qty=8)`. The orchestrator's conflict detector fired on the shared resource `sku=42`. The resolution policy, however, was keyed on *resource identity* rather than *predicate truth*. Because A's reservation (10 ≤ 12) was still arithmetically satisfiable against the post-B state (4 remaining), the engine allowed A to commit. The result was an overcommit: 18 units reserved against 12 available, because A's read-set predicate `available >= qty` was evaluated at read time, not at commit time, and the engine had no re-validation step. The invariant that was violated: **a speculative effect may commit only if its read-set predicates still hold at commit time, evaluated against the current committed state, not the state observed at speculation start.** Resource-identity conflict detection is necessary but not sufficient; predicate re-validation is the load-bearing check. ## Condition of Validity The abort obligation is valid precisely when all three hold: 1. The sibling's committed effect **mutates a value that appears in the speculative branch's read-set** (not merely the same resource key — the specific field the predicate consumed). 2. The speculative branch's pending effect **depends on that predicate's truth value** (i.e., the effect was conditioned on it, not merely co-located with it). 3. No **compensation path** exists that re-establishes a consistent read before the effect is emitted — either because the effect is non-idempotent, or because the re-read window is closed by the sibling's commit ordering. If any condition fails, abort is *permitted but not required*, and aborting is a policy choice (favor correctness margin over throughput). If all three hold, abort is *mandatory*; proceeding produces graph state that no serial execution could have produced. ## Decision Boundary The boundary is not "conflict → abort." It is: - **Predicate-stable conflict:** sibling mutates the resource but not the consumed field, or mutates it in a direction that preserves the predicate's truth. → *Re-validate and proceed.* Aborting here is over-conservative and, under contention, causes livelock. - **Predicate-breaking conflict with compensation:** sibling invalidates the predicate, but the branch can re-read and recompute within its deadline. → *Recompute, do not abort.* - **Predicate-breaking conflict without compensation:** sibling invalidates the predicate, and the branch's effect is already shaped by the stale read with no re-read window. → *Abort.* This is the mandatory zone. - **Unobservable conflict:** sibling commits after the branch's effect is already emitted but before it is acknowledged. → *This is not a conflict; it is a serialization-order problem.* Aborting here corrupts the graph by removing a committed effect. The correct response is ordering repair, not abort. The common error is collapsing cases 1–3 into a single "conflict → abort" rule, which is safe but degrades throughput to serial execution, or collapsing all four into "conflict → proceed," which produces the overcommit above. ## Operational Effect When the boundary is respected, speculative branches survive benign sibling commits (predicate-stable conflicts) and only pay abort cost on genuinely unsafe races, keeping speculative hit-rate high under contention. When the boundary is violated in the permissive direction, the graph accumulates effects that are individually valid but jointly unsatisfiable — overcommitted inventory, double-booked slots, divergent replicated state — and these are *silent*: no exception is raised at commit, only downstream. When violated in the conservative direction, two speculative branches targeting the same hot resource repeatedly abort each other and the orchestrator degenerates to serial dispatch, which is correct but defeats the purpose of speculation. The operational signature of a correct implementation is that abort counts track predicate-breaking conflicts, not resource-key collisions; if those two counts are equal, the engine is over-aborting, and if the abort count is near zero under contention, it is under-aborting. ```python class SpeculativeConflictError(RuntimeError): """Raised when a speculative branch cannot safely commit after a sibling commit.""" pass def resolve_speculative_commit( branch_read_set: dict, # field -> value observed at speculation start branch_predicates: dict, # field -> predicate fn evaluated on current state current_state: dict, # committed state at commit time sibling_committed: bool, effect_is_idempotent: bool, can_recompute: bool, ) -> str: """ Returns 'commit', 'recompute', or raises SpeculativeConflictError. The mandatory-abort zone is: sibling committed AND a consumed predicate no longer holds AND no compensation path exists. """ predicate_broken = False for field, pred in branch_predicates.items(): if field not in current_state: continue if not pred(current_state[field]): predicate_broken = True break if not sibling_committed: return "commit" if not predicate_broken: # Predicate-stable conflict: proceed. Aborting here is over-conservative. return "commit" if can_recompute and not effect_is_idempotent: # Predicate-breaking but compensable: recompute within deadline. return "recompute" if effect_is_idempotent: # Idempotent effects can be re-emitted safely after re-read. return "recompute" # Mandatory abort zone: predicate broken, no compensation, non-idempotent. raise SpeculativeConflictError( f"Speculative branch read-set invalidated by sibling commit; " f"predicate broken on {field!r} and no compensation path exists. " f"observed={branch_read_set.get(field)!r} " f"current={current_state.get(field)!r}" ) ```