# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-capability-attestation-renewal-when-trust-broker-delegation-chains-cross-organizational-policy-epochs.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-capability-attestation-renewal-when-trust-broker-delegation-chains-cross-organizational-policy-epochs.warrant.json
# Markdown SHA-256:   8515a9555595714ed22d82a8b01f634b7a66afdee5f31ce2c5c7063c11eaaa3e
# Code AST SHA-256:   880e9767bf4517ed7cdba00f9aba69908d9f8fe07acb4613242cf55fa1c62175
# Verified Classes:   StaleEpochRenewalError, Attestation
# Defensive Guards:   4 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Mapping


class StaleEpochRenewalError(RuntimeError):
    """Raised when a capability attestation renewal crosses a delegator
    policy epoch whose semantics are not resolvable by the relying party."""


@dataclass(frozen=True)
class Attestation:
    subject: str
    capability: str
    chain: tuple[str, ...]          # delegating orgs, in order
    epoch_vector: Mapping[str, int] # org -> epoch at mint time
    expires_at: float


def renew_attestation(
    att: Attestation,
    current_epochs: Mapping[str, int],
    capability_semantics: Mapping[tuple[str, int], frozenset[str]],
    now: float,
) -> Attestation:
    """Renew iff every delegator's current epoch preserves the capability's
    semantics as a superset. Otherwise refuse and demand renegotiation."""
    for org in att.chain:
        minted_epoch = att.epoch_vector.get(org)
        if minted_epoch is None:
            raise StaleEpochRenewalError(
                f"epoch vector missing for delegator {org!r}; "
                f"cannot verify capability {att.capability!r} under current policy"
            )
        current_epoch = current_epochs.get(org)
        if current_epoch is None:
            raise StaleEpochRenewalError(
                f"delegator {org!r} epoch unresolvable within budget; "
                f"refusing renewal rather than extending stale semantics"
            )
        if current_epoch == minted_epoch:
            continue
        minted_sem = capability_semantics.get((org, minted_epoch))
        current_sem = capability_semantics.get((org, current_epoch))
        if minted_sem is None or current_sem is None:
            raise StaleEpochRenewalError(
                f"semantics for capability {att.capability!r} undefined at "
                f"{org} epoch {minted_epoch}->{current_epoch}; renegotiate"
            )
        if not minted_sem <= current_sem:
            raise StaleEpochRenewalError(
                f"delegator {org!r} narrowed {att.capability!r} at epoch "
                f"{current_epoch}; renewal would authorize "
                f"{sorted(minted_sem - current_sem)} which policy now forbids"
            )
    # All delegator epochs preserve semantics; extend time only.
    return Attestation(
        subject=att.subject,
        capability=att.capability,
        chain=att.chain,
        epoch_vector=dict(current_epochs),
        expires_at=now + 3600.0,
    )
