# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-the-revocation-propagation-horizon-in-federated-agent-identity-and-cross-domain-aaif-credential-lifecycle-synchroniza.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-the-revocation-propagation-horizon-in-federated-agent-identity-and-cross-domain-aaif-credential-lifecycle-synchroniza.warrant.json
# Markdown SHA-256:   bce992b10e1bb1df30d7cb275b44941f86901178c82ebf41775d96e1b249eb62
# Code AST SHA-256:   4f375045c6a465544f09b6e28b5f5f445cbf9cba05e94b4d5a2920593ecbe491
# Verified Classes:   RevocationHorizonError, RevocationState
# Defensive Guards:   4 fail-closed invariants (zero external deps)

from dataclasses import dataclass


class RevocationHorizonError(RuntimeError):
    """Raised when a relying party would enforce stale or mis-keyed revocation state."""


@dataclass(frozen=True)
class RevocationState:
    highest_received_seq: int
    highest_enforced_seq: int
    cache_ttl_s: float
    sync_interval_s: float
    revocation_key_includes_epoch: bool


def assert_enforcement_current(
    state: RevocationState,
    privilege_class: str,
    max_tolerable_lag_s: float,
) -> None:
    gap = state.highest_received_seq - state.highest_enforced_seq
    if gap < 0:
        raise RevocationHorizonError(
            f"enforced sequence {state.highest_enforced_seq} exceeds received "
            f"{state.highest_received_seq}: enforcement is reading state it never received"
        )
    if gap > 0:
        raise RevocationHorizonError(
            f"monotonicity gap of {gap} events: domain has received revocations "
            f"it is not yet enforcing"
        )
    if not state.revocation_key_includes_epoch:
        raise RevocationHorizonError(
            "revocation key omits lifecycle_epoch; re-issued credentials cannot be "
            "distinguished from revoked predecessors"
        )
    lag = state.cache_ttl_s - state.sync_interval_s
    if lag > max_tolerable_lag_s:
        raise RevocationHorizonError(
            f"enforcement lag {lag:.0f}s exceeds tolerable {max_tolerable_lag_s:.0f}s "
            f"for privilege class {privilege_class!r}; bind cache invalidation to "
            f"revocation receipt or cap cache TTL"
        )
