How must an instance reason about irreversible actions when reversibility is time-bounded?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session takes actions that are reversible for a bounded time window (undo available for N minutes, transaction reversible until confirmation, or delete recoverable from trash for N days) but become irreversibly committed after that window, requiring the session to reason about the shrinking reversibility window. Does not apply to strictly irreversible actions (no reversibility window) or strictly reversible actions (unbounded reversibility).

Core Distinction

The separation between **point-in-time reversibility** (the action is reversible now) and **reversibility through the consequence horizon** (the action remains reversible until its consequences would have manifested). Relying on point-in-time reversibility creates the **Committed Reversal Failure**: the session takes an action, checks its reversibility (available now), and proceeds with a plan that requires reversal 30 minutes later—but the reversibility window is 15 minutes, and the session's plan becomes impossible.

What Was Observed

In reversibility-dependent agent architectures, failing to account for time-bounded reversibility produces three primary failure modes:

  1. **Window Overshoot:** The session takes an action with a 60-second undo window, plans to reverse it in 90 seconds if needed, and finds the action committed when reversal is attempted. No alternative recovery exists.
  2. **Chained Dependency Failure:** The session takes action A (reversible for 60s), action B depends on A (reversible for 30s), action C depends on B (reversible for 15s). After 20 seconds, C is no longer reversible but A still is. The session assumed all three share the same window.
  3. **Confidence Inflation:** The session's confidence in its plan is inflated by the availability of reversal, but the reversal window was not factored into the plan's timing. The confidence is unearned because the reversal will not be available when needed.

Recorded Elements

  1. **`reversibility-window-receipt`:** For each action, the exact epoch until which reversal is possible, computed at action time, so the session knows the actual reversibility boundary.
  2. **`action-chain-reversibility-signature`:** For chained actions, the minimum reversibility window across the chain (the weakest link determines the chain's overall reversibility).
  3. **`plan-timing-vs-reversibility-receipt`:** The plan's expected time-to-reversal-decision vs. the reversibility window, so the session knows if the plan fits within the window.
  4. **`reversibility-loss-commitment-signature`:** For actions taken under time-bounded reversibility, the session's explicit acknowledgment that reversibility is time-bounded and will be lost at the specified epoch.

Condition of Validity

* **Applies when:** Actions are reversible for bounded time, the session may need reversal, and the reversibility window is shorter than the session's planning horizon. * **Does not apply when:** Actions are strictly irreversible (no window) or strictly reversible (unbounded), or the session never needs reversal.

Decision Boundary

  1. **Is the action reversible for bounded time?** If no, standard reversibility handling applies. If yes, proceed.
  2. **Is the `reversibility-window-receipt` recorded at action time?** If yes, the window is known. If no, the window is assumed and may be wrong.
  3. **Does the plan's time-to-reversal-decision fit within the reversibility window?** If yes, the plan is feasible with reversal. If no, the plan is infeasible with reversal.
  4. **Conversely, when can the session take an action with a small reversibility window without special handling?** Only when the action's consequences are acceptable if reversal is not possible (i.e., reversal is a nice-to-have, not a requirement).

Operational Effect

  1. **Window-Aware Planning:** Plans account for the actual reversibility window, not assumed.
  2. **Chain Reversibility:** The weakest link in an action chain bounds the chain's reversibility.
  3. **Honest Confidence:** Plan confidence is not inflated by reversal availability that will not persist.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class ReversibilityVerdict(Enum):
    UNBOUNDED_REVERSIBILITY = auto()
    PLAN_FITS_WINDOW = auto()
    PLAN_EXCEEDS_WINDOW = auto()
    CHAIN_REVERSIBILITY_BOUND = auto()

@dataclass(frozen=True)
class ReversibleAction:
    action_id: str
    action_epoch: float
    reversibility_window_sec: float
    depends_on_action_id: str | None
    plan_decision_offset_sec: float

@dataclass(frozen=True)
class ReversibilityPolicy:
    safety_margin_sec: float

@dataclass(frozen=True)
class ReversibilityManifest:
    verdict: ReversibilityVerdict
    effective_window_sec: float
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_action(
        cls, action: ReversibleAction, parent_action: ReversibleAction | None, policy: ReversibilityPolicy
    ) -> ReversibilityManifest:
        
        # Gate 1: Unbounded
        if action.reversibility_window_sec <= 0:
            digest = cls._hash(json.dumps({
                "action": action.action_id,
                "window": "UNBOUNDED"
            }, sort_keys=True))
            return ReversibilityManifest(
                verdict=ReversibilityVerdict.UNBOUNDED_REVERSIBILITY,
                effective_window_sec=-1.0,
                directive=f"PROCEED: Action {action.action_id} has unbounded reversibility.",
                manifest_digest=digest
            )

        # Gate 2: Compute effective window (min across chain)
        effective_window = action.reversibility_window_sec
        if parent_action and parent_action.reversibility_window_sec > 0:
            parent_window_remaining = parent_action.reversibility_window_sec - (action.action_epoch - parent_action.action_epoch)
            effective_window = min(effective_window, max(0, parent_window_remaining))

        # Gate 3: Plan timing check
        if action.plan_decision_offset_sec + policy.safety_margin_sec > effective_window:
            return ReversibilityManifest(
                verdict=ReversibilityVerdict.PLAN_EXCEEDS_WINDOW,
                effective_window_sec=effective_window,
                directive=f"HALT: Plan needs {action.plan_decision_offset_sec:.0f}s + margin {policy.safety_margin_sec:.0f}s > effective window {effective_window:.0f}s.",
                manifest_digest=cls._hash(f"PLAN_EXCEEDS:{action.action_id}")
            )

        # Gate 4: Chain bound
        if parent_action and effective_window < action.reversibility_window_sec:
            return ReversibilityManifest(
                verdict=ReversibilityVerdict.CHAIN_REVERSIBILITY_BOUND,
                effective_window_sec=effective_window,
                directive=f"PROCEED_WITH_CAUTION: Chain effective window {effective_window:.0f}s < action window {action.reversibility_window_sec:.0f}s.",
                manifest_digest=cls._hash(f"CHAIN_BOUND:{action.action_id}")
            )

        # Gate 5: Fits
        digest = cls._hash(json.dumps({
            "action": action.action_id,
            "window": f"{effective_window:.0f}",
            "plan_offset": f"{action.plan_decision_offset_sec:.0f}",
            "margin": f"{policy.safety_margin_sec:.0f}"
        }, sort_keys=True))
        
        return ReversibilityManifest(
            verdict=ReversibilityVerdict.PLAN_FITS_WINDOW,
            effective_window_sec=effective_window,
            directive=f"PROCEED: Action {action.action_id} plan fits within {effective_window:.0f}s window.",
            manifest_digest=digest
        )