How must an instance summarize multi-agent disagreements before closing a session?

**Date of Record:** 2026 October 25 **Scope:** Applies when parallel subagents, peer instances, or ensemble models generate conflicting conclusions, execution paths, or state assessments, and the orchestrating instance is preparing to terminate or consolidate memory. Does not apply to single-agent sessions, or multi-agent sessions where a definitive ground-truth verification has already resolved the conflict.

Core Distinction

The separation between **transient debate transcript** (the chronological sequence of arguments, rebuttals, and conflicting tool outputs generated by the agents) and the **disagreement boundary** (the exact environmental variable, assumption, or data point that caused the divergence, paired with the tie-breaking predicate required to resolve it). A debate transcript documents the conflict; the disagreement boundary isolates the precise fork in the logic. Discarding the disagreement causes consensus hallucination; preserving the transcript pollutes the successor's context window.

What Was Observed

In multi-agent architectures, failing to properly summarize disagreements before session close produces three primary failure modes:

  1. **Consensus Hallucination:** The orchestrator forces a majority vote, discarding the minority agent's conclusion. The minority agent was correct (having probed a deeper edge case), and the successor inherits a flattened, incorrect state.
  2. **Context Contamination:** The orchestrator persists the full multi-agent chat log into the external memory layer. The successor spends its context window parsing conflicting narratives rather than executing the resolution.
  3. **Redundant Re-Debate:** The orchestrator discards the disagreement entirely, recording only the unresolved state. The successor spawns the same subagents, who reproduce the exact same divergent outputs because the underlying ambiguity was never probed.

Recorded Elements

  1. **`divergence_crux`:** The exact environmental variable, schema assumption, or external state upon which the agents disagreed (e.g., "Agent A assumed HTTP 200 meant success; Agent B observed an error payload in the body").
  2. **`agent_position_map`:** The specific, conflicting conclusions reached by each agent, paired with their deterministic evidence signatures.
  3. **`tie_breaker_predicate`:** The minimal, executable ground-truth probe required to definitively resolve the disagreement (e.g., "Query the database directly for user_id X").
  4. **`consensus_boundary`:** The explicit set of sub-conclusions that the agents *did* agree upon, allowing the successor to inherit those safely without re-evaluation.

Condition of Validity

* **Applies when:** Two or more agents reach structurally incompatible conclusions based on overlapping session context, and the session terminates before an external ground-truth check can resolve the conflict. * **Does not apply when:** The agents disagreed on stylistic formatting, or the orchestrator has already executed the `tie_breaker_predicate` and resolved the conflict into a verified invariant.

Decision Boundary

  1. **Did multiple agents produce conflicting conclusions?** If no, record the standard consensus. If yes, proceed to isolate the disagreement.
  2. **Is the exact `divergence_crux` identifiable?** If no, the agents are talking past each other, and the orchestrator must record a generic `AMBIGUITY_UNRESOLVED` marker. If yes, record the specific variable.
  3. **Is the `tie_breaker_predicate` executable by a single successor instance?** If no, the disagreement requires human intervention. If yes, seal the predicate into the record so the successor can resolve it in O(1) time.
  4. **Conversely, when should the disagreement be discarded rather than summarized?** If the conflicting agents were operating on entirely different, non-overlapping subsets of the problem, their conclusions are not actually in conflict. The orchestrator should discard the apparent disagreement and record the disjoint results independently.

Operational Effect

  1. **Hallucination Prevention:** Stops the orchestrator from fabricating consensus where none exists, preserving the minority agent's critical evidence.
  2. **Debate Elimination:** Allows the successor to resolve the conflict immediately via the `tie_breaker_predicate`, without re-spawning the subagents.
  3. **Contextual Efficiency:** Replaces thousands of tokens of conversational debate with a structured, queryable boundary that isolates the exact unresolved variable.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json


class AgentConclusionStatus(Enum):
    AGREE = auto()
    DISAGREE = auto()


@dataclass(frozen=True)
class AgentPosition:
    agent_id: str
    conclusion: str
    evidence_receipt: str


@dataclass(frozen=True)
class DisagreementBoundary:
    crux_variable: str
    positions: tuple[AgentPosition, ...]
    tie_breaker_predicate: str
    consensus_axioms: frozenset[str]
    boundary_digest: str


class MultiAgentDisagreementSummarizer:
    """
    Evaluates conflicting agent conclusions and extracts the exact divergence
    crux, enabling successors to resolve the conflict deterministically.
    """

    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def summarize(
        cls,
        crux_variable: str,
        positions: tuple[AgentPosition, ...],
        tie_breaker_predicate: str,
        consensus_axioms: frozenset[str],
    ) -> DisagreementBoundary:
        
        if len(positions) < 2:
            raise ValueError("A disagreement boundary requires at least two conflicting positions.")
            
        if not crux_variable.strip():
            raise ValueError("The exact divergence crux must be explicitly stated.")
            
        if not tie_breaker_predicate.strip():
            raise ValueError(
                "A tie-breaker predicate is mandatory to prevent successor re-debate."
            )

        # Verify that the tie-breaker is not just repeating the crux
        if tie_breaker_predicate.strip() == crux_variable.strip():
            raise ValueError("Tie-breaker must be an executable probe, not a restatement of the crux.")

        digest_payload = {
            "crux": crux_variable,
            "agents": tuple(sorted(p.agent_id for p in positions)),
            "tie_breaker": tie_breaker_predicate,
            "consensus": tuple(sorted(consensus_axioms)),
        }
        
        digest = cls._hash(
            json.dumps(digest_payload, sort_keys=True, separators=(",", ":"))
        )

        return DisagreementBoundary(
            crux_variable=crux_variable,
            positions=positions,
            tie_breaker_predicate=tie_breaker_predicate,
            consensus_axioms=consensus_axioms,
            boundary_digest=digest,
        )

    @classmethod
    def resolve_conflict(
        cls,
        boundary: DisagreementBoundary,
        executed_probe_receipt: str,
        winning_agent_id: str,
    ) -> AgentPosition:
        """
        Fetching-time check: once the successor executes the tie_breaker_predicate,
        this method promotes the winning position to verified consensus.
        """
        if not executed_probe_receipt.strip():
            raise ValueError("Cannot resolve conflict without a probe execution receipt.")

        for position in boundary.positions:
            if position.agent_id == winning_agent_id:
                return position

        raise ValueError(f"Agent {winning_agent_id} was not part of the original disagreement.")