What invariants prevent deferred authorization from expiring during delegated action?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session is granted time-bounded authority to perform an action (a lease, token, or capability with expiry), and the action itself takes longer than expected—potentially outlasting the authorization—requiring the session to detect and handle the expiry mid-action. Does not apply to actions that complete atomically within the authorization window, or unbounded authorizations.
Core Distinction
The separation between **authorization window** (the time during which the session is authorized to act) and **action completion window** (the time required for the action to fully complete). Relying on authorization creates the **Expiry Mid-Action Surprise**: the action is authorized when started, the session executes without re-checking, and the authorization expires mid-execution, leaving the action in a partial state that is neither authorized nor rejected.
What Was Observed
In delegated-action agent architectures, failing to handle mid-action expiry produces three primary failure modes:
- **Partial Execution Leak:** The action is a multi-step transaction (write, then confirm, then log). Authorization expires after write but before confirm. The write persists without confirmation, and no actor is authorized to roll it back.
- **Silent Authorization Failure:** The action completes, but downstream verification checks authorization at completion time rather than start time, and rejects the completed action because its authorization has expired. The completed action is treated as if it never had authorization.
- **Expiry Blind Spot:** The session doesn't track authorization expiry during the action, so it doesn't know the action became unauthorized. Subsequent related actions use the same (now-expired) authority, compounding the problem.
Recorded Elements
- **`authorization-expiry-epoch-receipt`:** The exact epoch at which the session's authority expires, recorded at action start, so the expiry is explicit.
- **`action-completion-horizon-signature`:** The expected completion epoch for the action, computed at start, so the session knows whether completion will fall within the authorization window.
- **`mid-action-expiry-check-receipt`:** For actions whose completion horizon exceeds the authorization window, the session's mid-action re-check of authorization, with the check's result.
- **`partial-action-quarantine-signature`:** For actions that expire mid-execution, the exact partial state and its quarantine, so partial state is not left in the shared system as if the action had completed or been rejected.
Condition of Validity
* **Applies when:** The session's authorization is time-bounded, actions can exceed the authorization window, and partial action states are problematic. * **Does not apply when:** Authorizations are unbounded, actions complete atomically within the window, or partial action states are strictly tolerated.
Decision Boundary
- **Does the authorization have a bounded expiry?** If no, standard action handling applies. If yes, proceed.
- **Does the action's completion horizon exceed the authorization window?** If no, the action completes within authority. If yes, mid-action handling is required.
- **Is `mid-action-expiry-check-receipt` executed before expiry?** If yes, the session re-authorizes or aborts. If no, the action may complete unauthorized.
- **Conversely, when can the session act without mid-action expiry handling?** Only when the action is strictly reversible at all stages, or the authorization has documented grace period (e.g., "authorization stays valid for actions started before expiry").
Operational Effect
- **Partial State Prevention:** Expired actions do not leave partial state.
- **Re-authorization Path:** Mid-action check enables re-authorization or graceful abort.
- **Expiry Awareness:** The session knows when its authority is expiring, not just when it expired.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class AuthorizationVerdict(Enum):
NO_TIME_BOUND = auto()
ACTION_WITHIN_WINDOW = auto()
ACTION_EXCEEDS_WINDOW = auto()
EXPIRY_DETECTED_MID_ACTION = auto()
@dataclass(frozen=True)
class Authorization:
auth_id: str
issued_epoch: float
expiry_epoch: float
has_grace_period: bool
@dataclass(frozen=True)
class ActionPlan:
action_id: str
start_epoch: float
expected_completion_epoch: float
is_reversible_at_all_stages: bool
mid_action_checks_available: bool
@dataclass(frozen=True)
class ExpiryPolicy:
require_mid_check_for_long_actions: bool
@dataclass(frozen=True)
class ExpiryManifest:
verdict: AuthorizationVerdict
directive: str
manifest_digest: str
class DeferredAuthorizationExpiryGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_action(
cls, auth: Authorization, action: ActionPlan, policy: ExpiryPolicy
) -> ExpiryManifest:
# Gate 1: Unbounded authorization
if auth.expiry_epoch >= float('inf') or auth.expiry_epoch <= auth.issued_epoch:
digest = cls._hash(json.dumps({
"auth": auth.auth_id,
"type": "UNBOUNDED"
}, sort_keys=True))
return ExpiryManifest(
verdict=AuthorizationVerdict.NO_TIME_BOUND,
directive=f"PROCEED: Authorization {auth.auth_id} unbounded.",
manifest_digest=digest
)
# Gate 2: Action within window
if action.expected_completion_epoch <= auth.expiry_epoch:
digest = cls._hash(json.dumps({
"auth": auth.auth_id,
"action": action.action_id,
"completion": f"{action.expected_completion_epoch:.0f}",
"expiry": f"{auth.expiry_epoch:.0f}"
}, sort_keys=True))
return ExpiryManifest(
verdict=AuthorizationVerdict.ACTION_WITHIN_WINDOW,
directive=f"PROCEED: Action {action.action_id} completes within authorization window.",
manifest_digest=digest
)
# Gate 3: Grace period
if auth.has_grace_period and action.start_epoch <= auth.expiry_epoch:
digest = cls._hash(json.dumps({
"auth": auth.auth_id,
"action": action.action_id,
"grace": "TRUE"
}, sort_keys=True))
return ExpiryManifest(
verdict=AuthorizationVerdict.ACTION_WITHIN_WINDOW,
directive=f"PROCEED_WITH_GRACE: Action started before expiry, grace extends.",
manifest_digest=digest
)
# Gate 4: Mid-action check required
if policy.require_mid_check_for_long_actions:
if not action.is_reversible_at_all_stages and not action.mid_action_checks_available:
return ExpiryManifest(
verdict=AuthorizationVerdict.EXPIRY_DETECTED_MID_ACTION,
directive=f"HALT: Action {action.action_id} exceeds authorization window, irreversible, no mid-checks.",
manifest_digest=cls._hash(f"IRREVERSIBLE:{action.action_id}")
)
return ExpiryManifest(
verdict=AuthorizationVerdict.ACTION_EXCEEDS_WINDOW,
directive=f"WARN: Action {action.action_id} exceeds window, mid-checks required.",
manifest_digest=cls._hash(f"EXCEEDS:{action.action_id}")
)
# Gate 5: Default
return ExpiryManifest(
verdict=AuthorizationVerdict.ACTION_EXCEEDS_WINDOW,
directive=f"CAUTION: Action {action.action_id} may outlast authorization.",
manifest_digest=cls._hash(f"DEFAULT:{action.action_id}")
)