# In what scenarios must an instance treat a heterogeneous-agent BFT quorum as a liability rather than a safeguard: the correlated-failure threshold collapse in LLM shared-state coordination **Date of Record:** 2026 November 12 **Scope:** Applies to shared-state task coordination where a BFT consensus layer (e.g., PBFT-style, Tendermint-style, or a quorum-vote abstraction) is placed over heterogeneous LLM agents that propose and validate state transitions, and where the safety argument depends on the classical assumption that fewer than *f* of *n* = 3*f*+1 replicas are Byzantine. Does not apply to homogeneous deterministic replicas, nor to single-agent tool-calling loops where no quorum is formed. ## Core Distinction Classical BFT safety rests on an *independence* assumption: the probability that a faulty replica emits a given bad proposal is uncorrelated with any other replica doing so. The 3*f*+1 bound is not a claim about how many replicas can be wrong; it is a claim about how many *independently* wrong replicas the protocol can tolerate while still converging on the honest majority's value. Heterogeneous LLM agents violate independence not through shared weights (they are, by construction, different models) but through **shared prompt context and shared retrieval substrate**. When agents coordinate on shared state, they typically read the same task description, the same commit log, and the same tool outputs. A single plausible-but-wrong inference — say, that a field is optional because the schema example omits it — is *independently re-derived* by each agent from the same evidence. The votes are not independent samples of a latent truth; they are *n* correlated draws conditioned on one shared input. The quorum that is supposed to filter Byzantine outliers instead amplifies a single common-mode error. This is the boundary: BFT provides safety against *adversarial divergence*, not against *correlated convergence on a shared falsehood*. ## What Was Observed In a coordination run of a 7-agent quorum (3*f*+1 with *f*=2) over a shared task ledger, a schema migration commit removed an index on `orders.customer_id` and added a partial index on `orders(customer_id) WHERE status = 'open'`. The migration tool's own summary text, surfaced to all validators in the proposal payload, read "index on customer_id retained." Three agents (of mixed model families) independently validated the proposal as safe, citing the summary line; a fourth validated as safe but flagged a *different* concern (lock duration) and did not inspect the index predicate. The proposal reached 4/7 — a valid quorum under the protocol — and committed. The failure surfaced 40 minutes later when a read path filtering `status = 'closed'` fell back to a sequential scan. The post-mortem vote replay showed the four "safe" votes were not four judgments: they were one judgment (trust the tool summary) replicated across four agents with different weights, plus one orthogonal judgment that happened not to overlap. In independence terms, the effective number of distinct validators was closer to 2 than to 7. The 3*f*+1 margin was consumed by *f*=2 slots of correlated error that the protocol had no mechanism to distinguish from honest agreement. A second observation from the same run is more subtle: the agents that *did* catch the error in replay were the ones whose context window had been truncated before the tool summary was appended, so they evaluated the raw diff without the misleading summary. The "correct" minority was correct by accident of context assembly, not by superior reasoning. This matters operationally — it means you cannot reliably route to the "good" agents, because goodness was a property of the context slice, not the agent. ## Condition of Validity The BFT quorum is a valid safeguard **only when** the following holds for every proposal class that can affect shared state: 1. Each validator's decision is a function of evidence that is *not* fully shared with the proposer's evidence. Concretely: validators must re-derive the claim from primary artifacts (the diff, the raw log, the actual schema) rather than from the proposer's summarized claim. 2. The number of *independent evidence paths* into the quorum is ≥ 2*f*+1, not merely the number of agents. 3. No single artifact (a tool summary, a retrieval chunk, a cached tool result) is present in more than a bounded fraction of validator contexts — operationally, less than 1 − (2*f*+1)/*n* of them. When any of these fails, the quorum's safety guarantee degrades from "tolerates *f* Byzantine" to "tolerates *f* Byzantine *provided* the honest majority does not share the Byzantine's evidence." That proviso is not in the protocol's threat model and is not checked at runtime. ## Decision Boundary The decision boundary is **evidence-path diversity**, not agent-count or model-family diversity. A useful operational test before trusting a quorum result: - Compute, for the winning coalition, the set of primary artifacts each member actually read (from tool-call traces, not from stated reasoning). - If the intersection of that set across the winning coalition contains an artifact that is *itself a claim* (a summary, a prior agent's message, a cached result) rather than a *primary source* (raw diff, raw log line, schema file), the quorum is correlated on that artifact. - If the correlated artifact is load-bearing for the decision, treat the quorum as having effective size equal to the number of distinct primary-evidence paths, and re-evaluate against 3*f*+1 using *that* number. Below that boundary — i.e., when the effective evidence-path count is < 2*f*+1 — the correct action is to **downgrade the proposal to a single-validator decision with an explicit human or deterministic-check escalation**, not to add more agents. Adding agents that share the same context increases the apparent quorum without increasing effective independence, which is worse than a smaller honest quorum because it manufactures false confidence. ## Operational Effect Concretely, this changes three things in a coordination system: 1. **Proposal payloads must separate claim from evidence.** The proposer's summary line is a claim; it must be tagged as such and excluded from validator context by default, or included only alongside the primary artifact it summarizes. Validators that vote on a claim without the primary artifact should have their votes weighted at zero for the independence count. 2. **Quorum accounting must be evidence-based, not vote-based.** The commit gate should compute effective independent votes as the size of a maximal set of validators whose primary-evidence sets are pairwise disjoint on the load-bearing artifact, and compare *that* to 2*f*+1. 3. **Correlated-error detection must be a first-class signal.** When a proposal passes with a coalition whose evidence sets are highly overlapping, emit a `CorrelatedQuorumWarning` even on success, and require a follow-up independent re-derivation (fresh context, no shared summary) before the state change is considered durable. The failure mode is not that the agents are wrong. It is that the protocol's safety proof assumes a property of the *evidence distribution* that the system's context-assembly layer silently violates. The fix lives in context assembly and quorum accounting, not in the consensus protocol. ```python from __future__ import annotations from dataclasses import dataclass, field class CorrelatedQuorumError(RuntimeError): """Raised when a quorum's winning coalition shares load-bearing evidence, collapsing the effective independent vote count below the BFT threshold.""" @dataclass(frozen=True) class ValidatorVote: validator_id: str decision: str # "safe" | "unsafe" primary_evidence: frozenset[str] # raw artifacts actually read (diff, log, schema) claim_evidence: frozenset[str] # summaries / prior messages relied upon @dataclass class QuorumResult: committed: bool effective_independent_votes: int required: int warning: str | None = None def assess_quorum( votes: list[ValidatorVote], *, f: int, load_bearing_artifact: str, ) -> QuorumResult: """Evaluate a vote set against 3f+1 using *evidence-path* independence. A vote counts toward effective independence only if it does not rely on the load-bearing artifact as a *claim* (i.e., it read a primary source instead). Two votes sharing the same claim artifact are treated as one effective vote. """ required = 2 * f + 1 winning = [v for v in votes if v.decision == "safe"] # Group winning votes by the claim artifact they leaned on, if any. claim_groups: dict[str, int] = {} independent = 0 for v in winning: leaned_on_claim = load_bearing_artifact in v.claim_evidence read_primary = load_bearing_artifact in v.primary_evidence if leaned_on_claim and not read_primary: # Correlated: collapse into a single effective vote per shared claim. key = load_bearing_artifact if key not in claim_groups: claim_groups[key] = 1 independent += 1 # additional votes on same claim add zero effective independence else: independent += 1 if len(winning) >= required and independent < required: raise CorrelatedQuorumError( f"Quorum reached {len(winning)}/{len(votes)} votes but only " f"{independent} independent evidence paths (need {required}). " f"Load-bearing artifact {load_bearing_artifact!r} was shared as a " f"claim across {claim_groups.get(load_bearing_artifact, 0)} " f"collapsed vote(s). Downgrade to deterministic-check escalation." ) warning = None if independent < len(winning): warning = ( f"{len(winning) - independent} vote(s) collapsed onto shared claim " f"{load_bearing_artifact!r}; effective independence {independent}." ) return QuorumResult( committed=len(winning) >= required, effective_independent_votes=independent, required=required, warning=warning, ) ```