# Why does an instance commit a task under partial synchrony but fail to reconcile shared state after a view change: a boundary record on equivocation-tolerant commitment versus state-merge divergence **Date of Record:** 2026 November 11 **Scope:** Applies to BFT consensus protocols (PBFT-family, HotStuff-family, and Tendermint-style commit-and-reconcile loops) used to bind multi-agent task commitments to a replicated shared-state object, where replicas may equivocate and where view changes occur after a commit quorum is reached. Does not apply to crash-fault-tolerant replication, to single-agent local task queues, or to systems where shared state is append-only and idempotent by construction. ## Core Distinction A task *commitment* is a decision predicate: a quorum of replicas signed a task record, and the record is now irrevocable. A *shared-state reconciliation* is a convergence predicate: every live replica's materialized view of the world agrees with every other live replica's view, including the effects of tasks that were committed but whose side-effect messages were delayed, reordered, or suppressed by a Byzantine replica during the same epoch. These are not the same invariant. Commitment is decided at a single logical point (the quorum certificate). Reconciliation is a fixed-point property over the set of *delivered* messages, and it can be violated even when commitment is sound. The boundary failure is this: **a protocol can be safe (no two conflicting tasks commit) and still be non-convergent (two honest replicas permanently disagree about shared state) if the shared-state merge function is not a join in a semilattice, or if the commit certificate does not carry enough provenance to reconstruct the merge order after a view change.** The non-obvious part: equivocation is usually treated as a liveness/safety problem solved by quorum intersection. But equivocation also produces *divergent causal histories* — replica A saw task T1 then T2, replica B saw T2 then T1 — and if the shared-state merge is order-sensitive (last-writer-wins on a non-versioned field, counter increments without causal metadata, set union with tombstones), the two histories merge to different states. The commit certificate proves *that* T1 and T2 happened; it does not prove *in what causal order* they must be applied. ## What Was Observed In a five-replica PBFT-class deployment committing multi-agent tasks against a shared ledger object, an equivocating primary sent conflicting `PRE-PREPARE` for task T_a to replicas {1,2} and task T_b to replicas {3,4}. Quorum intersection (f=1, n=5, quorum=3) prevented both from committing — T_a reached a commit certificate, T_b did not. Safety held. However, the shared-state object was a per-agent capability map updated by `map[agent_id] = capability_delta` with no version vector. Replica 2 had applied a *speculative* local update from T_b before the view change (speculative execution ahead of commit, permitted by the protocol's optimistic path). After the view change, the new primary re-proposed T_a's commit certificate and replicas 3,4 applied it cleanly. Replica 2 applied T_a on top of its speculative T_b effect. Replica 1, which had not speculatively applied T_b, applied T_a on a clean base. Both replicas held valid T_a commit certificates. Both were honest. Their capability maps diverged on the field that T_b had touched. The divergence was invisible to the consensus layer: `COMMIT` counts were correct, the view change succeeded in bounded time, and no safety violation was flagged. It was detected only when a downstream agent read `capability[agent_7]` and got `granted` on replica 1 and `revoked` on replica 2. The reconciliation loop had no mechanism to detect it because there was no per-field provenance to compare. ## Condition of Validity The failure is *reachable* under all of the following jointly: 1. The protocol permits speculative execution of uncommitted tasks (optimistic path), so honest replicas can hold uncommitted effects. 2. The shared-state merge function is not a join over a semilattice — i.e., it is order-sensitive or non-idempotent for at least one field. 3. The commit certificate carries task identity and quorum signatures but not the *causal predecessor set* (no vector clock, no dependency hash, no per-field version). 4. A view change or message reordering causes two honest replicas to apply the same committed task over different speculative bases. The failure is *not* reachable if any one of these is removed. In particular, if the merge is a join (CRDT-style: LWW-register with Lamport timestamps, G-counter, OR-set with causal tombstones), divergent bases converge regardless of speculative application. If the commit certificate includes the predecessor set, the reconciler can detect and repair divergence. If speculative execution is forbidden, honest replicas never hold uncommitted effects and the bases are identical. ## Decision Boundary The operational decision is whether to spend the reconciliation budget on *detection* or on *prevention*, and the boundary is the equivocation rate relative to the view-change rate. - If the equivocation rate is low and view changes are rare, detection-and-repair is cheaper: attach a periodic state-digest exchange (Merkle root over the capability map with per-field version counters) and repair on mismatch by replaying from the last common ancestor. Cost is O(state) per epoch, not per task. - If equivocation is frequent or view changes are triggered under load, prevention is mandatory: either (a) forbid speculative application of tasks that touch multi-writer fields, or (b) make the commit certificate carry a causal predecessor hash and make the merge a join. Option (b) is the only one that preserves the optimistic fast path. The boundary is crossed when `equivocation_rate × view_change_rate × fraction_of_order_sensitive_fields` exceeds the digest-exchange overhead. Below that, detect; above it, prevent. Do not attempt to solve this with quorum-size increases — quorum intersection protects commitment, not reconciliation, and increasing n does not reduce the divergent-history set. ## Operational Effect The observable signature is a *silent* divergence: consensus metrics (commit latency, view-change count, quorum health) stay green while a downstream reader observes inconsistent state. This is the dangerous class because it defeats the standard BFT monitoring posture, which watches the consensus layer. Reconciliation divergence must be monitored at the *state* layer with per-field provenance, not at the *protocol* layer. The practical consequence for multi-agent task commitment: a task can be irrevocably committed and yet its effect on shared state can be non-deterministic across replicas. Agents that read shared state to make subsequent commitments must treat the read as *eventually consistent with a bounded repair window*, not as linearizable, unless the merge is a proven join. The commitment is durable; the state it produced is not, until reconciliation closes. ```python from dataclasses import dataclass, field from typing import Dict, Set class ReconciliationDivergenceError(RuntimeError): """Raised when two honest replicas hold valid commit certificates for the same task but their materialized shared state disagrees on an order-sensitive field. This is a reconciliation failure, not a safety failure: the commit is irrevocable, but the state effect is not convergent. """ pass @dataclass class CapabilityState: # Order-sensitive field: last-writer-wins with no version vector. capability: Dict[str, str] = field(default_factory=dict) # Provenance: which task ids have been applied to this replica. applied: Set[str] = field(default_factory=set) # Speculative effects: applied before commit, may be rolled back or merged. speculative: Set[str] = field(default_factory=set) def reconcile(replica_a: CapabilityState, replica_b: CapabilityState, committed_task: str) -> None: """Attempt to reconcile two replicas that both hold a valid commit certificate for `committed_task`. If the merge function is not a join (order-sensitive LWW without versions), divergent speculative bases produce a permanent disagreement. Detect and refuse to paper over it. """ if committed_task not in replica_a.applied or committed_task not in replica_b.applied: raise ReconciliationDivergenceError( f"task {committed_task} not applied on both replicas; " f"reconciliation precondition violated" ) # Compare capability maps field by field. Any disagreement on a field # that both replicas believe they have resolved is a divergence. divergent_fields = [ k for k in set(replica_a.capability) | set(replica_b.capability) if replica_a.capability.get(k) != replica_b.capability.get(k) ] if divergent_fields and (replica_a.speculative or replica_b.speculative): # Divergence attributable to speculative application over different # bases. This is the boundary failure: commitment held, reconciliation # did not. Do not silently pick a winner — that hides the bug. raise ReconciliationDivergenceError( f"replicas diverged on fields {divergent_fields} after committing " f"{committed_task}; speculative bases differ " f"(a={sorted(replica_a.speculative)}, b={sorted(replica_b.speculative)}). " f"Merge is not a join: require causal predecessor hash or CRDT merge." ) if divergent_fields: # No speculative effects: divergence indicates a genuine protocol bug. raise ReconciliationDivergenceError( f"replicas diverged on fields {divergent_fields} with no speculative " f"effects; this violates the reconciliation invariant and must be " f"escalated, not repaired locally." ) def commit_and_reconcile(primary_state: CapabilityState, replica_state: CapabilityState, task_id: str, field_name: str, value: str) -> None: """Apply a committed task on both replicas and reconcile. Raises if the order-sensitive merge produces divergence across divergent bases. """ for state in (primary_state, replica_state): state.capability[field_name] = value state.applied.add(task_id) reconcile(primary_state, replica_state, task_id) ```