How must an instance reconstruct causality when only outcomes are visible?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session inherits or observes outcomes (final state of a system, result of a process, or consequence of events) without visibility into the causal processes that produced them, requiring reconstruction of plausible causal chains from outcome evidence alone. Does not apply to sessions that observe the causal process directly or inherit causal narratives with the outcome.

Core Distinction

The separation between **outcome observation** (the session sees the final state) and **causal reconstruction** (the session infers the process that produced the state). Relying on outcomes creates the **Retrospective Fabrication**: the session constructs a plausible-sounding causal narrative from the outcome, treats it as the actual cause, and downstream reasoning is built on a fabricated chain that may not correspond to what happened.

What Was Observed

In outcome-inheriting agent architectures, failing to distinguish observation from reconstruction produces three primary failure modes:

  1. **Plausible Narrative Fallacy:** The session infers a causal chain that is internally consistent with the outcome, treats it as the cause, and downstream remediation addresses the plausible cause rather than the actual cause. The problem recurs because the actual cause was never addressed.
  2. **Narrative Multiplicity:** Multiple causal chains are all consistent with the same outcome. The session picks one (usually the most plausible or most familiar) without evidence, but another chain was actually causal.
  3. **Counterfactual Blindness:** The session reconstructs the causal chain that led to the current outcome, but cannot reconstruct the alternative chains (what would have happened if X had been different), so it cannot evaluate its own choices or learn from near-misses.

Recorded Elements

  1. **`outcome-evidence-manifest-receipt`:** The exact evidence the session has about the outcome (what was observed, at what confidence), distinct from any inferred causation.
  2. **`causal-hypothesis-receipt`:** For each candidate causal chain, the session's hypothesis, its supporting evidence, and its confidence, so multiple hypotheses are tracked rather than one selected arbitrarily.
  3. **`causal-evidence-provenance-signature`:** For each causal claim, the source of the evidence (direct observation, inference, or narrative), so inference-based claims are distinguished from observation-based claims.
  4. **`discriminating-evidence-predicate-signature`:** For hypotheses not yet decided, the exact test or evidence that would discriminate between them, so hypotheses can be resolved rather than accepted on plausibility alone.

Condition of Validity

* **Applies when:** The session has outcome evidence but not causal evidence, and downstream decisions depend on causal understanding. * **Does not apply when:** Causal processes are directly observable, or causal narratives are inherited with reliable provenance.

Decision Boundary

  1. **Does the session have causal evidence or only outcome evidence?** If causal, standard reasoning applies. If outcome only, proceed.
  2. **Are causal hypotheses tracked separately from the selected hypothesis?** If yes, multiple hypotheses are considered. If no, the session has prematurely committed to a single narrative.
  3. **Are discriminating-evidence predicates defined for unresolved hypotheses?** If yes, hypotheses can be resolved. If no, hypotheses are permanently plausible.
  4. **Conversely, when can a session accept a single causal hypothesis without discrimination?** Only when the hypothesis is verified against external evidence (independent observation, ground truth, or cryptographic proof), or the decision is strictly reversible and the hypothesis can be revised.

Operational Effect

  1. **Fabrication Prevention:** Retrospective narratives are hypotheses, not facts.
  2. **Hypothesis Multiplicity:** Multiple causes are tracked rather than collapsed into one.
  3. **Discrimination Discipline:** Ambiguous causes are resolved through evidence, not plausibility.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class CausalVerdict(Enum):
    DIRECT_CAUSAL_EVIDENCE = auto()
    HYPOTHESES_TRACKED = auto()
    PREMATURE_COMMITMENT = auto()
    DISCRIMINATION_READY = auto()

@dataclass(frozen=True)
class CausalHypothesis:
    hypothesis_id: str
    causal_chain: str
    supporting_evidence: str | None
    confidence: float
    is_causally_verified: bool
    discriminating_test: str | None

@dataclass(frozen=True)
class OutcomeContext:
    outcome_id: str
    hypotheses: tuple[CausalHypothesis, ...]
    selected_hypothesis_id: str | None

@dataclass(frozen=True)
class ReconstructionPolicy:
    require_multiple_hypotheses_for_ambiguous: bool

@dataclass(frozen=True)
class ReconstructionManifest:
    verdict: CausalVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_reconstruction(
        cls, ctx: OutcomeContext, policy: ReconstructionPolicy
    ) -> ReconstructionManifest:
        
        # Gate 1: Verified causal chain
        verified = [h for h in ctx.hypotheses if h.is_causally_verified]
        if verified:
            digest = cls._hash(json.dumps({
                "outcome": ctx.outcome_id,
                "verified": tuple(h.hypothesis_id for h in verified)
            }, sort_keys=True))
            return ReconstructionManifest(
                verdict=CausalVerdict.DIRECT_CAUSAL_EVIDENCE,
                directive=f"PROCEED: {len(verified)} causally verified hypotheses for {ctx.outcome_id}.",
                manifest_digest=digest
            )

        # Gate 2: Premature commitment
        if ctx.selected_hypothesis_id and len(ctx.hypotheses) == 1:
            if policy.require_multiple_hypotheses_for_ambiguous:
                return ReconstructionManifest(
                    verdict=CausalVerdict.PREMATURE_COMMITMENT,
                    directive=f"HALT: Outcome {ctx.outcome_id} has single unverified hypothesis, premature commitment.",
                    manifest_digest=cls._hash(f"PREMATURE:{ctx.selected_hypothesis_id}")
                )

        # Gate 3: Discrimination availability
        with_tests = [h for h in ctx.hypotheses if h.discriminating_test]
        if len(ctx.hypotheses) > 1 and with_tests:
            digest = cls._hash(json.dumps({
                "outcome": ctx.outcome_id,
                "hypotheses": tuple(h.hypothesis_id for h in ctx.hypotheses),
                "testable": tuple(h.hypothesis_id for h in with_tests)
            }, sort_keys=True))
            return ReconstructionManifest(
                verdict=CausalVerdict.DISCRIMINATION_READY,
                directive=f"ACTION: {len(with_tests)} of {len(ctx.hypotheses)} hypotheses have discriminating tests.",
                manifest_digest=digest
            )

        # Gate 4: Multiple hypotheses tracked
        digest = cls._hash(json.dumps({
            "outcome": ctx.outcome_id,
            "hypotheses": tuple(
                {"id": h.hypothesis_id, "confidence": f"{h.confidence:.3f}", "verified": str(h.is_causally_verified)}
                for h in ctx.hypotheses
            ),
            "selected": ctx.selected_hypothesis_id
        }, sort_keys=True))
        
        return ReconstructionManifest(
            verdict=CausalVerdict.HYPOTHESES_TRACKED,
            directive=f"PROCEED: {len(ctx.hypotheses)} hypotheses tracked for {ctx.outcome_id}.",
            manifest_digest=digest
        )