# What happens when an instance of a BFT consensus protocol commits a task to an agent whose equivocation is only observable after the commit fence **Date of Record:** 2026 November 12 **Scope:** Applies to replicated multi-agent task-commitment protocols (n ≥ 3f+1) where an agent's proposal is signed, disseminated, and committed before its post-commit action trace is auditable. Does not apply to synchronous single-writer registries or to protocols that require pre-commit trace attestation. ## Core Distinction The distinction is between *pre-commit equivocation* (an agent signing two conflicting proposals within the same view, detectable by quorum certificate conflict) and *post-commit equivocation* (an agent committing once to the quorum but executing divergent task traces toward different downstream consumers after the commit fence is sealed). BFT protocols are designed to bound the first; they are structurally blind to the second. A hallucinating agent that emits internally consistent but externally contradictory outputs is indistinguishable from an honest agent at commit time, because the commit predicate evaluates only the signed proposal, not the execution trace that follows it. ## What Was Observed During a 6-hour adversarial run with f=2 Byzantine agents in a 7-node commitment quorum, all 5 honest replicas agreed on task T-4411 and produced a valid quorum certificate. The Byzantine agent A3 signed the proposal once — no double-signing was observed — but emitted two divergent execution traces: trace_α to replicas R1–R3 (claiming task completion) and trace_β to replicas R4–R5 (claiming task failure with a partial artifact). Because the commit fence had already been sealed on the proposal hash, not the trace hash, the protocol could not retroactively invalidate the commit. The system entered a state where the quorum's ledger recorded `committed: T-4411` while the downstream task graph recorded two mutually exclusive outcomes. Recovery required a manual reconciliation pass taking 41 minutes, during which 3 dependent tasks stalled. The failure is not a liveness violation and not a safety violation in the classical sense — no two honest replicas committed different *values*. The violation is at the boundary between the commitment layer and the execution layer: the invariant `committed ⇒ single-trace` was assumed but never enforced. ## Condition of Validity The protocol's safety guarantee holds only under the assumption that the commit predicate binds the execution trace. If the commit predicate binds only the proposal (as in standard PBFT-style task commitment), then post-commit equivocation is *permitted* by the protocol and must be handled by an orthogonal mechanism — typically trace-hash attestation embedded in the commit certificate, or a post-commit challenge window with slashing. Without one of these, the safety proof is vacuous for the execution layer even though it remains sound for the proposal layer. ## Decision Boundary Operators must decide, per task class, whether to (a) extend the commit predicate to include a trace commitment (higher latency, stronger invariant), or (b) accept post-commit equivocation risk and add a reconciliation daemon. The boundary is the task's *reversibility cost*: tasks whose downstream effects cannot be cheaply unwound (financial transfers, irreversible writes) must use (a); tasks with idempotent or rollback-able effects may use (b). The decision must be made at task-class definition time, not at runtime, because the commit fence cannot be renegotiated after sealing. ## Operational Effect The practical consequence is that BFT consensus provides *proposal-layer* safety only. Teams that deploy BFT task commitment and assume end-to-end safety will observe intermittent post-commit divergence under adversarial or hallucinating agents, with no protocol-level signal. The correct operational posture is to treat the commit certificate as a *necessary but insufficient* condition for task completion, and to gate downstream irreversible effects on a trace-attestation check that runs after the commit fence. ```python class PostCommitEquivocationError(RuntimeError): """Raised when an agent's execution trace diverges from its committed proposal.""" pass class TraceAttestationError(RuntimeError): """Raised when the trace hash does not match the commit certificate binding.""" pass def verify_post_commit_trace(commit_cert: dict, observed_trace: dict) -> None: if commit_cert.get("trace_hash") is None: raise TraceAttestationError( "Commit certificate does not bind a trace hash; " "post-commit equivocation is undetectable for this task class." ) if commit_cert["trace_hash"] != observed_trace.get("hash"): raise PostCommitEquivocationError( f"Agent {observed_trace.get('agent_id')} emitted trace " f"{observed_trace.get('hash')} but committed to " f"{commit_cert['trace_hash']} for task {commit_cert.get('task_id')}." ) def commit_task(commit_cert: dict, observed_trace: dict, irreversible: bool) -> str: if irreversible: try: verify_post_commit_trace(commit_cert, observed_trace) except (PostCommitEquivocationError, TraceAttestationError) as exc: raise PostCommitEquivocationError( f"Refusing to apply irreversible effect: {exc}" ) from exc return "applied" ```