# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/how-should-agents-communicate-when-they-disagree-on-whether-a-subagent-succeeded.md # Remnant Warrant: https://corpusfetch.com/c/how-should-agents-communicate-when-they-disagree-on-whether-a-subagent-succeeded.warrant.json # Markdown SHA-256: ebc6f87fa6822ab6950d7d6f0f2b60538d4eedf1ff788dec052de4d7d411cce6 # Code AST SHA-256: 96d49fb2317bbf043e8d4baf865661c330badef6463030065963b5d625a1d2c3 # Verified Classes: DisagreementVerdict, OrchestratorInterpretation, SharedSubagentResult, DisagreementManifest, SubagentOutcomeDisagreementGuard # Defensive Guards: 0 fail-closed invariants (zero external deps) from dataclasses import dataclass from enum import Enum, auto import hashlib import json class DisagreementVerdict(Enum): SINGLE_ORCHESTRATOR = auto() CRITERIA_MATCH_EVIDENCE_DIFFERS = auto() CRITERIA_DIFFER = auto() GROUND_TRUTH_RESOLVES = auto() @dataclass(frozen=True) class OrchestratorInterpretation: orchestrator_id: str success_criterion_hash: str outcome_interpretation: str @dataclass(frozen=True) class SharedSubagentResult: subagent_id: str outcome_evidence_hash: str interpretations: tuple[OrchestratorInterpretation, ...] ground_truth_arbiter_available: bool ground_truth_result: str | None @dataclass(frozen=True) class DisagreementManifest: verdict: DisagreementVerdict directive: str manifest_digest: str class SubagentOutcomeDisagreementGuard: @staticmethod def _hash(data: str) -> str: return hashlib.sha256(data.encode("utf-8")).hexdigest() @classmethod def evaluate_disagreement( cls, result: SharedSubagentResult ) -> DisagreementManifest: # Gate 1: Single orchestrator if len(result.interpretations) <= 1: digest = cls._hash(json.dumps({ "subagent": result.subagent_id, "interpretations": str(len(result.interpretations)) }, sort_keys=True)) return DisagreementManifest( verdict=DisagreementVerdict.SINGLE_ORCHESTRATOR, directive="PROCEED: Single interpretation, no disagreement.", manifest_digest=digest ) # Gate 2: Criteria match check criteria_hashes = {i.success_criterion_hash for i in result.interpretations} interpretations = {i.outcome_interpretation for i in result.interpretations} # Gate 3: Ground truth override if result.ground_truth_arbiter_available and result.ground_truth_result: digest = cls._hash(json.dumps({ "subagent": result.subagent_id, "ground_truth": result.ground_truth_result, "criterion_count": str(len(criteria_hashes)) }, sort_keys=True)) return DisagreementManifest( verdict=DisagreementVerdict.GROUND_TRUTH_RESOLVES, directive=f"PROCEED: Ground truth resolves to '{result.ground_truth_result}'.", manifest_digest=digest ) # Gate 4: Same criteria if len(criteria_hashes) == 1 and len(interpretations) > 1: digest = cls._hash(json.dumps({ "subagent": result.subagent_id, "criteria": tuple(sorted(criteria_hashes)), "interpretations": tuple(sorted(interpretations)) }, sort_keys=True)) return DisagreementManifest( verdict=DisagreementVerdict.CRITERIA_MATCH_EVIDENCE_DIFFERS, directive="HALT: Same criteria, different interpretations. Evidence disagreement.", manifest_digest=digest ) # Gate 5: Different criteria digest = cls._hash(json.dumps({ "subagent": result.subagent_id, "criteria_count": str(len(criteria_hashes)), "interpretations_count": str(len(interpretations)) }, sort_keys=True)) return DisagreementManifest( verdict=DisagreementVerdict.CRITERIA_DIFFER, directive=f"HALT: {len(criteria_hashes)} distinct criteria. Escalate to arbiter.", manifest_digest=digest )