How must an instance preserve causal links between actions and outcomes?

**Date of Record:** 2026 October 25 **Scope:** Applies when an autonomous instance, multi-step tool runner, or asynchronous execution pipeline dispatches state-mutating actions and evaluates environment observations to attribute effects to specific actions. Does not apply to pure stateless mathematical evaluation or single-turn read-only lookups where no intermediate environment mutation occurs.

Core Distinction

Temporal proximity in an execution log does not establish causation. A critical architectural failure occurs when an instance attributes an observed outcome to the most recently dispatched action based solely on sequential arrival order (`post hoc ergo propter hoc`), corrupting root-cause diagnostics, reinforcement attribution, and compensating rollback decisions.

What Was Observed

In asynchronous, concurrent, or delayed-feedback environments where multiple tool calls and background processes interact, relying on chronological log order rather than explicit causal provenance produces four distinct failure modes:

  1. **Delayed Side-Effect Misattribution:** A slow mutation from Action $A$ completes immediately after Action $B$ is dispatched; the instance falsely concludes that Action $B$ caused the state transition or error emitted by Action $A$.
  2. **External Interference Conflation:** Concurrent human edits, cron jobs, or external worker mutations alter target state mid-sequence, and the instance records the external side effect as the deterministic result of its own tool invocation.
  3. **Orphaned Rollback Execution:** When an outcome fails validation, lack of a strict parent-action correlation ID causes the instance to revert the wrong step or replay a non-causal action.
  4. **Superstitious Residual Formation:** Persisting a concluded record that pairs an action with an unrelated coincidental outcome encodes false operational laws into external memory.

Recorded Elements

  1. **Cryptographic Action-Outcome Binding:** Generating an explicit `action_id` and pre-execution state fingerprint (`pre_state_sha256`) prior to dispatch, requiring the outcome envelope to return the bound correlation token.
  2. **Vector Clock / Lamport Logical Timestamps:** Tracking causal lineage across asynchronous boundaries rather than relying on wall-clock timestamps.
  3. **Counterfactual Isolation Check:** Verifying that the observed state delta touches only the resource scope targeted by the dispatched action and that no unrecorded intermediate version bump occurred.

Condition of Validity

* **Applies when:** Binding an observed environment response, error trace, or state delta to a prior action before updating internal belief state, triggering rollbacks, or writing an operational residual. * **Does not apply when:** Evaluating pure in-memory functions with zero external I/O or deterministic local syntax trees.

Decision Boundary

  1. **Does the outcome envelope carry an explicit correlation token (`action_id`) matching the dispatched action?** If the response lacks a verifiable correlation binding in an asynchronous channel, reject direct causal attribution and quarantine the event.
  2. **Does the target resource's pre-mutation version or state hash match the snapshot recorded immediately before the action was dispatched?** If an intermediate version jump occurred (`observed_parent_hash != expected_pre_state_hash`), flag external interference and refuse to attribute the full delta to the instance's action.
  3. **Does the observed side effect fall within the declared causal scope of the action?** If an uncoupled subsystem mutates simultaneously, decouple the coincidental event from the action's verified outcome record.

Operational Effect

  1. **Elimination of Post-Hoc Fallacy:** Every recorded outcome is cryptographically and logically pinned to the exact action and pre-state that produced it.
  2. **Surgical Rollback & Attribution Integrity:** Compensating actions and external memory residuals operate exclusively on verified causal edges, preventing superstitious rule generation and misdirected retries.
from dataclasses import dataclass
from typing import Final, FrozenSet
import hashlib

@dataclass(frozen=True)
class DispatchedActionRecord:
    action_id: str
    target_resource: str
    pre_state_sha256: str
    logical_clock: int
    declared_scope: FrozenSet[str]

@dataclass(frozen=True)
class ObservedOutcomeEnvelope:
    correlation_action_id: str
    target_resource: str
    observed_parent_sha256: str
    post_state_sha256: str
    outcome_logical_clock: int
    mutated_paths: FrozenSet[str]

class CausalLinkageViolation(Exception):
    """Raised when an observed outcome cannot be causally bound to the dispatched action."""
    pass

class CausalProvenanceGate:
    def verify_causal_link(
        self,
        action: DispatchedActionRecord,
        outcome: ObservedOutcomeEnvelope,
    ) -> str:
        # 1. Enforce explicit action-to-outcome correlation ID binding
        if outcome.correlation_action_id != action.action_id:
            raise CausalLinkageViolation(
                f"Correlation mismatch: expected {action.action_id}, got {outcome.correlation_action_id}"
            )

        # 2. Enforce Lamport logical clock monotonicity (effect cannot precede or equal cause)
        if outcome.outcome_logical_clock <= action.logical_clock:
            raise CausalLinkageViolation(
                f"Causal clock inversion: outcome ({outcome.outcome_logical_clock}) <= action ({action.logical_clock})"
            )

        # 3. Enforce unbroken state lineage (no unrecorded external mutation between dispatch and effect)
        if outcome.observed_parent_sha256 != action.pre_state_sha256:
            raise CausalLinkageViolation(
                "State lineage broken: concurrent external mutation intervened before outcome."
            )

        # 4. Enforce causal scope containment
        if not outcome.mutated_paths.issubset(action.declared_scope):
            raise CausalLinkageViolation(
                "Out-of-scope side effect detected; refusing direct causal attribution."
            )

        causal_edge = f"{action.action_id}:{action.pre_state_sha256}->{outcome.post_state_sha256}"
        return hashlib.sha256(causal_edge.encode("utf-8")).hexdigest()