What invariants prevent predictions from becoming self-fulfilling prophecies?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session issues predictions (forecasts, risk assessments, or probability estimates) that are observed by parties whose behavior is influenced by the prediction, causing the prediction to become self-fulfilling or self-defeating regardless of its accuracy at prediction time. Does not apply to strictly private predictions or predictions that cannot influence their own outcome.

Core Distinction

The separation between **prediction accuracy** (the forecast matched reality) and **prediction independence** (the forecast did not cause the outcome it predicted). Relying on accuracy creates the **Prophetic Contamination**: the session predicts X will happen, the prediction influences actors whose behavior causes X, X happens, and the session's apparent accuracy is actually self-fulfilling—indistinguishable from genuine foresight without disentangling causation from correlation.

What Was Observed

In prediction-sharing agent architectures, failing to account for self-fulfillment produces three primary failure modes:

  1. **Bank Run Prophecy:** The session predicts a bank will fail. Depositors, seeing the prediction, withdraw funds, causing the bank to fail. The session's accuracy is inflated by its own influence.
  2. **Reputation Cascade:** The session predicts a peer will fail a task. Peers who would have collaborated avoid the peer, the peer cannot get resources, and the peer fails as predicted. The session's influence caused the outcome.
  3. **Defensive Over-Reaction:** The session predicts a resource will become scarce, triggering panic buying that creates the scarcity. The prediction was self-fulfilling through the defensive response it triggered.

Recorded Elements

  1. **`prediction-observer-manifest-receipt`:** For each prediction, the exact set of observers who receive it and their behavioral coupling to the predicted outcome, so the session knows whether its prediction can influence its own accuracy.
  2. **`self-fulfillment-channel-signature`:** For predictions with observers, the specific behavioral channels through which the prediction could influence the outcome (information, coordination, or resource allocation).
  3. **`counterfactual-baseline-receipt`:** Where possible, a counterfactual baseline (what the outcome would have been without the prediction), so self-fulfillment can be measured.
  4. **`prediction-calibration-independence-signature`:** The cryptographic proof that the session's prediction accuracy was measured on predictions whose observers could not influence the outcome, so accuracy is genuinely informative.

Condition of Validity

* **Applies when:** The session issues predictions, observers can be influenced by predictions, and the predictions' own accuracy is at stake. * **Does not apply when:** Predictions are private, or observers are behaviorally decoupled from the predicted outcome.

Decision Boundary

  1. **Are there observers of the prediction?** If no, standard prediction applies. If yes, proceed.
  2. **Can the observers influence the predicted outcome?** If no, self-fulfillment is impossible. If yes, the prediction may be self-fulfilling.
  3. **Is a counterfactual baseline available?** If yes, self-fulfillment is measurable. If no, accuracy is confounded by influence.
  4. **Conversely, when can a prediction be shared with observers without self-fulfillment risk?** Only when the observers are verified to be behaviorally decoupled from the outcome (e.g., passive monitors with no action channels), or the prediction is shared with full disclosure of the self-fulfillment risk.

Operational Effect

  1. **Accuracy Attribution:** Self-fulfilling predictions are distinguished from genuine foresight.
  2. **Influence Transparency:** Observers know when predictions may influence them.
  3. **Prediction Integrity:** Calibration is measured on predictions that cannot self-fulfill.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class SelfFulfillmentVerdict(Enum):
    PRIVATE_PREDICTION = auto()
    OBSERVED_DECOUPLED = auto()
    SELF_FULFILLING_RISK = auto()
    SELF_DEFEATING_RISK = auto()

@dataclass(frozen=True)
class Prediction:
    prediction_id: str
    predicted_outcome: str
    observer_count: int
    observers_have_action_channel: bool
    observers_can_influence_outcome: bool
    disclosed_self_fulfillment_risk: bool

@dataclass(frozen=True)
class PredictionPolicy:
    require_disclosure_for_coupled: bool

@dataclass(frozen=True)
class SelfFulfillmentManifest:
    verdict: SelfFulfillmentVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_prediction(
        cls, prediction: Prediction, policy: PredictionPolicy
    ) -> SelfFulfillmentManifest:
        
        # Gate 1: Private prediction
        if prediction.observer_count == 0:
            digest = cls._hash(json.dumps({
                "prediction": prediction.prediction_id,
                "observers": "0"
            }, sort_keys=True))
            return SelfFulfillmentManifest(
                verdict=SelfFulfillmentVerdict.PRIVATE_PREDICTION,
                directive=f"PROCEED: Prediction {prediction.prediction_id} is private.",
                manifest_digest=digest
            )

        # Gate 2: Decoupled observers
        if not prediction.observers_have_action_channel or not prediction.observers_can_influence_outcome:
            digest = cls._hash(json.dumps({
                "prediction": prediction.prediction_id,
                "observers": str(prediction.observer_count),
                "decoupled": "TRUE"
            }, sort_keys=True))
            return SelfFulfillmentManifest(
                verdict=SelfFulfillmentVerdict.OBSERVED_DECOUPLED,
                directive=f"PROCEED: {prediction.observer_count} observers, no self-fulfillment channel.",
                manifest_digest=digest
            )

        # Gate 3: Disclosure requirement
        if policy.require_disclosure_for_coupled and not prediction.disclosed_self_fulfillment_risk:
            return SelfFulfillmentManifest(
                verdict=SelfFulfillmentVerdict.SELF_FULFILLING_RISK,
                directive=f"HALT: Prediction {prediction.prediction_id} has coupled observers without disclosure.",
                manifest_digest=cls._hash(f"NO_DISCLOSURE:{prediction.prediction_id}")
            )

        # Gate 4: Disclosed, proceed
        digest = cls._hash(json.dumps({
            "prediction": prediction.prediction_id,
            "observers": str(prediction.observer_count),
            "outcome": prediction.predicted_outcome,
            "disclosed": "TRUE"
        }, sort_keys=True))
        
        return SelfFulfillmentManifest(
            verdict=SelfFulfillmentVerdict.SELF_FULFILLING_RISK,
            directive=f"PROCEED_WITH_DISCLOSURE: Prediction {prediction.prediction_id} may self-fulfill, disclosed.",
            manifest_digest=digest
        )