# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-accepts-a-capability-grant-whose-trust-anchor-was-rotated-mid-negotiation.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-accepts-a-capability-grant-whose-trust-anchor-was-rotated-mid-negotiation.warrant.json
# Markdown SHA-256:   332a7bb736a9fb109e13851ce184be40bd9f9304bc45628881e70f14ab375f99
# Code AST SHA-256:   b46e457a83ccdc5bf345d8b7fedd95f7abcde8b87a8c339fc5008cf7488d274c
# Verified Classes:   AnchorRotatedMidNegotiation, AnchorMaterial, NegotiationTranscript
# Defensive Guards:   2 fail-closed invariants (zero external deps)

import hashlib
from dataclasses import dataclass
from datetime import datetime, timedelta


class AnchorRotatedMidNegotiation(RuntimeError):
    """Raised when a capability grant's trust anchor no longer matches the
    material recorded at negotiation time. Recovery is re-negotiation,
    not retry."""
    pass


@dataclass(frozen=True)
class AnchorMaterial:
    identifier: str
    public_key: bytes

    def content_hash(self) -> str:
        return "sha256:" + hashlib.sha256(self.public_key).hexdigest()


@dataclass(frozen=True)
class NegotiationTranscript:
    anchor: AnchorMaterial
    negotiated_at: datetime
    grace_period: timedelta


def verify_grant(
    grant_anchor_hash: str,
    transcript: NegotiationTranscript,
    issuer_current_anchor: AnchorMaterial,
    now: datetime,
) -> None:
    recorded = transcript.anchor.content_hash()

    if grant_anchor_hash != recorded:
        # Grant references material we never agreed to.
        raise AnchorRotatedMidNegotiation(
            f"grant anchor {grant_anchor_hash} != transcript anchor {recorded}"
        )

    if issuer_current_anchor.content_hash() != recorded:
        elapsed = now - transcript.negotiated_at
        if elapsed > transcript.grace_period:
            raise AnchorRotatedMidNegotiation(
                f"anchor {recorded} rotated by issuer; "
                f"elapsed {elapsed} exceeds grace {transcript.grace_period}; "
                f"re-negotiate capability exchange"
            )
        # Within grace: both roots valid, proceed under recorded material.
        return

    # Anchor unchanged; grant is verifiable as-is.
    return
