# Which invariants prevent speculative tool-call prefetching from canceling live dependency chains in agent meshes **Date of Record:** 2026 November 14 **Scope:** Applies to agent meshes where a planner issues speculative tool calls ahead of confirmed dependency resolution and a cancellation layer prunes speculative branches when a downstream result invalidates them. Does not apply to strictly serialized tool-call graphs or to meshes where speculation is disabled at the planner. ## Core Distinction The failure is not "speculation was wrong." Speculation being wrong is the expected state, and cancellation is the designed recovery mechanism. The failure is that cancellation of a speculative branch propagates across a *shared dependency edge* into a chain that has already been committed to by a live consumer. Speculative prefetching treats the call tree as a tree; dependency-aware cancellation must treat it as a DAG. When the two models disagree at a node with in-degree greater than one, cancellation is not a local retraction but a global mutation of state that other agents are actively reading. The invariant at stake is **cancellation locality**: a cancel token may only revoke work whose sole consumer is the speculative branch being pruned. Any prefetched result with a second, non-speculative consumer must be demoted, not canceled. ## What Was Observed A mesh with a planner, three tool-executor agents, and a result cache. The planner speculatively prefetched `fetch_user_profile` and `fetch_billing_history` in parallel while awaiting a slow `resolve_tenant` call, betting that both would be needed. `resolve_tenant` returned a tenant whose profile was already cached, so the planner issued a cancellation for the `fetch_user_profile` speculative branch. The cancellation handler walked the branch's dependency closure and revoked the in-flight `fetch_user_profile` call. However, a second agent — a compliance auditor attached to the same tenant — had independently subscribed to `fetch_user_profile` via a shared memoized future created at prefetch time. The prefetch had deduplicated the call by key, so the auditor's subscription and the speculative branch pointed at the *same* future object. Canceling the speculative branch resolved that shared future to `CancelledError`. The auditor's chain failed not because its dependency was unavailable, but because a sibling's speculation was retracted. The observed signature: a `CancelledError` surfacing in an agent whose request had never been speculative, correlated in time with a planner cancellation event for a different logical branch, with both sharing a cache key. No retry fired, because the auditor's retry policy treated cancellation as terminal rather than as a transient dependency failure. ## Condition of Validity The invariant holds — cancellation locality is enforceable — only when the following three conditions are jointly true: 1. **Ref-counted futures.** Every deduplicated future carries a consumer count distinguishing speculative holders from committed holders. Cancellation decrements the speculative count; the future is only resolved to cancellation when the committed count reaches zero. 2. **Cancellation is a demotion, not a revocation, for shared nodes.** A node with any committed consumer transitions from `SPECULATIVE` to `COMMITTED` on cancel, and the in-flight call continues. 3. **Cancellation is not terminal for downstream consumers.** Any consumer that receives a cancellation it did not itself originate must classify it as `DependencyRevoked`, a retriable transient, distinct from `SelfCancelled`. When any condition is violated, the mesh silently converts a local optimization (prune wasted work) into a cross-agent availability fault. The failure is silent because cancellation is a normal, expected event; nothing in the logs marks it as anomalous. ## Decision Boundary The boundary is drawn at the deduplication layer, not the cancellation layer. If the prefetch layer deduplicates by key without tracking consumer provenance, the cancellation layer *cannot* recover locality — it has no information to distinguish a speculative holder from a committed one. Therefore: - If futures are deduplicated and shared: cancellation must be ref-counted and provenance-aware. Do not cancel; demote. - If futures are *not* shared (each consumer gets an independent call): cancellation is trivially local and may revoke freely, at the cost of duplicate work. - Mixed meshes must tag each future at creation with the originating branch class (`SPECULATIVE` / `COMMITTED`) and refuse to resolve a shared future to cancellation while any `COMMITTED` holder remains. The non-obvious corollary: the correct fix lives in the prefetch/dedup layer, and a cancellation-layer patch that "checks whether anyone else is waiting" is a race, because the committed consumer may subscribe *after* the cancel is issued but *before* the speculative branch's future is released. ## Operational Effect Without the invariant, speculative prefetching is unsafe to enable in any mesh with shared memoization, and teams disable it — losing the latency win that motivated the mesh. With the invariant, prefetch hit rates remain high while cancellation faults drop to zero for committed consumers. The observable effect of enforcing cancellation locality is that `CancelledError` no longer appears in non-speculative agent traces, and the only cancellations that resolve are those with zero committed holders. The cost is retained in-flight work for demoted nodes — acceptable, because the work was already paid for at prefetch time and demotion is cheaper than a downstream retry storm. ```python from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Dict class BranchClass(Enum): SPECULATIVE = "speculative" COMMITTED = "committed" class CancellationLocalityError(RuntimeError): """Raised when a cancel would revoke a future that still has committed holders.""" @dataclass class SharedFuture: key: str speculative_holders: int = 0 committed_holders: int = 0 resolved: bool = False cancelled: bool = False class FutureRegistry: def __init__(self) -> None: self._futures: Dict[str, SharedFuture] = {} def acquire(self, key: str, branch: BranchClass) -> SharedFuture: fut = self._futures.setdefault(key, SharedFuture(key=key)) if branch is BranchClass.SPECULATIVE: fut.speculative_holders += 1 else: fut.committed_holders += 1 return fut def cancel_speculative(self, key: str) -> SharedFuture: fut = self._futures.get(key) if fut is None: raise CancellationLocalityError(f"no future registered for key={key!r}") if fut.resolved: return fut if fut.committed_holders > 0: # Demotion, not revocation: a live consumer depends on this node. fut.speculative_holders = max(0, fut.speculative_holders - 1) return fut # Safe to revoke: no committed holders remain. fut.cancelled = True fut.resolved = True return fut def prune_branch(registry: FutureRegistry, prefetched_keys: list[str]) -> None: for key in prefetched_keys: fut = registry.cancel_speculative(key) if fut.cancelled: continue if fut.committed_holders > 0 and fut.speculative_holders == 0: # Node fully demoted; the in-flight call must be allowed to finish. continue if not fut.resolved and fut.committed_holders == 0: raise CancellationLocalityError( f"key={key!r} left in unresolved limbo with no holders; " "speculative cancel failed to demote or revoke" ) ```