# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-to-honor-a-delegated-capability-after-a-trust-revocation-has-been-issued-but-not-yet-acknowledged-by-the-pee.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-to-honor-a-delegated-capability-after-a-trust-revocation-has-been-issued-but-not-yet-acknowledged-by-the-pee.warrant.json
# Markdown SHA-256:   9538eb661cfe259977fc99446f9fb683163dc410798855b71a97caa39928625c
# Code AST SHA-256:   97c6ec8b62b868c11858a8f5ddafa67b3eb2a3935189058ebe27ea5566e0b384
# Verified Classes:   StaleAuthorizationError, FreshnessProof, DelegatedCapability
# Defensive Guards:   3 fail-closed invariants (zero external deps)

import time
from dataclasses import dataclass


class StaleAuthorizationError(RuntimeError):
    """Raised when a delegated capability cannot be proven fresh within bounds."""
    pass


@dataclass(frozen=True)
class FreshnessProof:
    issuer_sequence: int
    signed_at: float  # monotonic seconds


@dataclass(frozen=True)
class DelegatedCapability:
    capability_id: str
    minted_at_sequence: int
    max_staleness: float          # negotiated in envelope
    degrade_read_only: bool       # negotiated in envelope


def authorize(
    cap: DelegatedCapability,
    proof: FreshnessProof | None,
    observed_now: float,
    is_mutation: bool,
) -> None:
    if proof is None:
        raise StaleAuthorizationError(
            f"capability {cap.capability_id}: no freshness proof available; "
            "unverifiable authorization must be denied"
        )

    age = observed_now - proof.signed_at
    if age > cap.max_staleness:
        if is_mutation or not cap.degrade_read_only:
            raise StaleAuthorizationError(
                f"capability {cap.capability_id}: freshness proof age {age:.2f}s "
                f"exceeds max_staleness {cap.max_staleness:.2f}s; "
                "refusing (fail-closed)"
            )
        # degrade: read-only path — allowed, but must be audited
        return

    if proof.issuer_sequence < cap.minted_at_sequence:
        raise StaleAuthorizationError(
            f"capability {cap.capability_id}: issuer sequence "
            f"{proof.issuer_sequence} precedes mint sequence "
            f"{cap.minted_at_sequence}; proof is from a stale ledger fork"
        )
