How must an instance handle actions that matter because they did not happen?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session's value or failure is determined by actions it did not take (missed opportunities, prevented harms, non-events that matter), and the session must account for and communicate about non-action as rigorously as about action. Does not apply to sessions whose only relevant outputs are the actions they did take.
Core Distinction
The separation between **action evidence** (what the session did) and **non-action evidence** (what the session chose not to do, and why). Relying on action evidence creates the **Invisible Decision Blindness**: the session's best decisions are often non-actions (not deploying, not writing, not calling), but non-actions leave no execution trace, and their value is invisible to successors, auditors, and the session itself.
What Was Observed
In action-traceable agent architectures, failing to record non-actions produces three primary failure modes:
- **Missed Opportunity Attribution:** The session chose not to take a path that would have led to a better outcome, but there is no record of the consideration, so the missed opportunity is invisible and cannot inform future decisions.
- **Prevented Harm Invisibility:** The session's inaction prevented a harm (did not click the malicious link, did not deploy the flawed code), but the prevention is invisible—only failures leave traces, not prevented failures.
- **Non-Action Repetition:** The session or its successor repeatedly evaluates the same non-action options without the benefit of knowing they were previously ruled out, wasting evaluation effort.
Recorded Elements
- **`non-action-decision-receipt`:** For decisions not to act, the exact consideration (what was considered, what alternatives were evaluated, what led to the non-action), recorded with the same rigor as action decisions.
- **`non-action-rationale-signature`:** The session's reasoning for choosing non-action (risk assessment, opportunity cost, or principled abstention), so successors can evaluate whether the rationale still holds.
- **`prevented-harm-attribution-receipt`:** For non-actions that prevented harm, the estimated harm that would have occurred (if estimable), so prevention is counted in the session's value.
- **`reevaluation-trigger-signature`:** For each non-action, the exact conditions under which the decision should be reevaluated (if the environment changes, the harm becomes more likely, or the opportunity becomes more valuable).
Condition of Validity
* **Applies when:** The session's decisions include non-actions, non-actions have material consequences, and downstream decisions depend on accounting for non-action. * **Does not apply when:** All decisions are actions (no non-action options), or non-actions have no material consequence.
Decision Boundary
- **Are non-action decisions material to the session's value or failure?** If no, standard action logging suffices. If yes, proceed.
- **Is the `non-action-decision-receipt` recorded with rationale?** If yes, the decision is traceable. If no, the consideration is invisible.
- **Does the `reevaluation-trigger-signature` specify when to revisit?** If yes, the non-action is revisitable. If no, the decision is frozen without revalidation.
- **Conversely, when can a non-action be recorded without rationale?** Only when the non-action is trivially obvious (no reasonable alternative considered), or the recording overhead exceeds the non-action's stakes.
Operational Effect
- **Decision Symmetry:** Non-actions are recorded with the same rigor as actions.
- **Prevention Recognition:** Prevented harms are attributed to the session's choices.
- **Revisitation Discipline:** Non-actions are revisitable when conditions change.
from dataclasses import dataclass, Enum, auto
import hashlib
import json
class NonActionVerdict(Enum):
NO_NON_ACTION_DECISION = auto()
NON_ACTION_PROPERLY_RECORDED = auto()
NON_ACTION_UNDOCUMENTED = auto()
REEVALUATION_DUE = auto()
@dataclass(frozen=True)
class NonActionDecision:
decision_id: str
considered_action: str
action_rejected: bool
rationale_hash: str | None
estimated_prevented_harm: float | None
reevaluation_trigger: str | None
decision_epoch: float
@dataclass(frozen=True)
class NonActionContext:
current_epoch: float
environment_changed: bool
@dataclass(frozen=True)
class NonActionPolicy:
require_rationale_for_significant: bool
@dataclass(frozen=True)
class NonActionManifest:
verdict: NonActionVerdict
directive: str
manifest_digest: str
class NonActionGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_decision(
cls, decision: NonActionDecision, ctx: NonActionContext, policy: NonActionPolicy
) -> NonActionManifest:
# Gate 1: No non-action
if not decision.action_rejected:
digest = cls._hash(json.dumps({
"decision": decision.decision_id,
"type": "ACTION_TAKEN"
}, sort_keys=True))
return NonActionManifest(
verdict=NonActionVerdict.NO_NON_ACTION_DECISION,
directive=f"PROCEED: Decision {decision.decision_id} resulted in action.",
manifest_digest=digest
)
# Gate 2: Rationale requirement
if policy.require_rationale_for_significant and not decision.rationale_hash:
return NonActionManifest(
verdict=NonActionVerdict.NON_ACTION_UNDOCUMENTED,
directive=f"HALT: Non-action {decision.decision_id} lacks rationale.",
manifest_digest=cls._hash(f"NO_RATIONALE:{decision.decision_id}")
)
# Gate 3: Reevaluation due
if ctx.environment_changed and decision.reevaluation_trigger:
return NonActionManifest(
verdict=NonActionVerdict.REEVALUATION_DUE,
directive=f"ACTION: Reevaluate non-action {decision.decision_id}, environment changed.",
manifest_digest=cls._hash(f"REEVAL:{decision.decision_id}")
)
# Gate 4: Recorded
digest = cls._hash(json.dumps({
"decision": decision.decision_id,
"considered": decision.considered_action,
"rationale": decision.rationale_hash,
"prevented_harm": f"{decision.estimated_prevented_harm or 0:.2f}",
"trigger": decision.reevaluation_trigger
}, sort_keys=True))
return NonActionManifest(
verdict=NonActionVerdict.NON_ACTION_PROPERLY_RECORDED,
directive=f"PROCEED: Non-action {decision.decision_id} recorded with rationale.",
manifest_digest=digest
)