What cross-agent memory consensus invariants prevent agents from acting on divergent recollections?
**Date of Record:** 2026 October 27 **Scope:** Applies when multiple agentic sessions have overlapping memories of shared events (multi-agent workflows, shared incident response, or coordinated task execution), and their recollections of the same event may diverge, requiring consensus before downstream action. Does not apply to strictly partitioned memories or single-agent scenarios.
Core Distinction
The separation between **individual recollection** (each agent's memory of an event) and **consensus recollection** (the reconciled memory agreed upon by all agents who witnessed the event). Relying on individual recollection creates the **Divergent Memory Action**: each agent acts on its own version of the event, and their actions contradict each other, causing state corruption that neither agent is responsible for because neither was wrong about its own memory.
What Was Observed
In shared-workflow agent architectures, failing to reconcile memory divergence produces three primary failure modes:
- **Contradictory Writes:** Agent A remembers "user X was created," Agent B remembers "user X was not created." A writes X, B writes a duplicate X or fails to write X downstream, and the state diverges.
- **Audit Trail Collapse:** The audit trail shows two agents making contradictory claims about the same event. Auditors cannot determine which claim is authoritative because neither is more "correct" from the audit's perspective.
- **Reconciliation Storms:** Attempting to reconcile memories after the fact triggers expensive reconciliation processes (query origin, cross-reference), and in some cases reconciliation reveals that neither agent's memory matches reality.
Recorded Elements
- **`event_witness_manifest_receipt`:** For each event, the cryptographic record of which agents witnessed it, allowing the receiver to know who to query for consensus.
- **`per_agent_event_signature`:** Each witness's cryptographic signature on the event details, allowing the receiver to verify attribution and detect post-hoc modification.
- **`consensus_reconciliation_receipt`:** For reconciled events, the cryptographic proof of the reconciliation method (majority, unanimous, or oracle-attested) and outcome.
- **`divergence_escalation_signature`:** For events where consensus could not be reached (no majority, no oracle), the cryptographic proof of escalation to human resolution.
Condition of Validity
* **Applies when:** Multiple agents witness shared events, their recollections can diverge, and downstream actions depend on event accuracy. * **Does not apply when:** Events are witnessed by only one agent, or downstream actions are insensitive to event accuracy.
Decision Boundary
- **Do multiple agents have memories of the same event?** If no, individual recollection applies. If yes, proceed.
- **Do all witnesses agree on the event details?** If yes, use the agreed recollection. If no, proceed to consensus.
- **Is consensus achievable via the defined protocol (majority, unanimous, oracle)?** If yes, use the reconciled version. If no, escalate to human resolution.
- **Conversely, when can an agent act on its own recollection without consensus?** Only when the action is strictly local to the agent (no shared state), or the action is reversible with no downstream consequence.
Operational Effect
- **Contradictory Write Prevention:** Consensus ensures all agents act on the same recollection.
- **Audit Coherence:** The audit trail reflects the reconciled recollection, not individual divergences.
- **Deterministic Reconciliation:** The reconciliation protocol prevents ad-hoc reconciliation storms.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class ConsensusVerdict(Enum):
SINGLE_WITNESS = auto()
CONSENSUS_REACHED = auto()
CONSENSUS_FAILED = auto()
DIVERGENCE_ESCALATED = auto()
@dataclass(frozen=True)
class EventWitness:
agent_id: str
event_hash: str
signature: str
@dataclass(frozen=True)
class SharedEvent:
event_id: str
witnesses: tuple[EventWitness, ...]
oracle_attestation: str | None
@dataclass(frozen=True)
class ConsensusPolicy:
consensus_threshold: float
@dataclass(frozen=True)
class ConsensusManifest:
verdict: ConsensusVerdict
reconciled_event_hash: str | None
directive: str
manifest_digest: str
class DivergentMemoryGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_event(
cls, event: SharedEvent, policy: ConsensusPolicy
) -> ConsensusManifest:
# Gate 1: Single witness
if len(event.witnesses) <= 1:
digest = cls._hash(json.dumps({
"event": event.event_id,
"witnesses": str(len(event.witnesses))
}, sort_keys=True))
return ConsensusManifest(
verdict=ConsensusVerdict.SINGLE_WITNESS,
reconciled_event_hash=event.witnesses[0].event_hash if event.witnesses else None,
directive=f"PROCEED: Single witness, no consensus needed.",
manifest_digest=digest
)
# Gate 2: Oracle attestation
if event.oracle_attestation:
digest = cls._hash(json.dumps({
"event": event.event_id,
"oracle": event.oracle_attestation
}, sort_keys=True))
return ConsensusManifest(
verdict=ConsensusVerdict.CONSENSUS_REACHED,
reconciled_event_hash=event.oracle_attestation,
directive=f"PROCEED: Oracle-attested event.",
manifest_digest=digest
)
# Gate 3: Vote tally
vote_counts: dict[str, int] = {}
for w in event.witnesses:
vote_counts[w.event_hash] = vote_counts.get(w.event_hash, 0) + 1
total = len(event.witnesses)
for event_hash, count in vote_counts.items():
if count / total >= policy.consensus_threshold:
digest = cls._hash(json.dumps({
"event": event.event_id,
"winner": event_hash,
"votes": str(count),
"total": str(total)
}, sort_keys=True))
return ConsensusManifest(
verdict=ConsensusVerdict.CONSENSUS_REACHED,
reconciled_event_hash=event_hash,
directive=f"PROCEED: Consensus {count}/{total} on {event_hash[:8]}.",
manifest_digest=digest
)
# Gate 4: Escalate
return ConsensusManifest(
verdict=ConsensusVerdict.DIVERGENCE_ESCALATED,
reconciled_event_hash=None,
directive=f"HALT: No consensus ({len(vote_counts)} distinct hashes). Escalate to human.",
manifest_digest=cls._hash(f"DIVERGENT:{event.event_id}")
)