# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-spiffe-svid-renewal-after-a-workload-attestor-selector-drift-and-why-naive-re-attestation-is-unsafe.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-refuse-spiffe-svid-renewal-after-a-workload-attestor-selector-drift-and-why-naive-re-attestation-is-unsafe.warrant.json
# Markdown SHA-256:   840f01cd4fcb9a775aec52df686fb7ed99afb347a561d6e720540a21e209b0ff
# Code AST SHA-256:   8c0749c6ead91c9d5deee0be8a9a60cef184f00d08493ccb30718f6a42faea80
# Verified Classes:   SelectorDriftError, AttestationState
# Defensive Guards:   2 fail-closed invariants (zero external deps)

import time
from dataclasses import dataclass, field


class SelectorDriftError(RuntimeError):
    """Raised when SVID renewal would cross a selector discontinuity
    without a corresponding re-attestation of the parent root of trust."""


@dataclass
class AttestationState:
    spiffe_id: str
    selectors: frozenset
    parent_root: str          # e.g. node attestor serial, k8s SA token hash, machine-id
    attested_at: float
    mutable_selectors: frozenset = field(default_factory=frozenset)


def renew_svid(state: AttestationState, current_selectors: frozenset,
               current_parent_root: str, max_age_s: float = 3600.0) -> str:
    if time.time() - state.attested_at > max_age_s:
        raise SelectorDriftError(
            f"attestation for {state.spiffe_id} is stale "
            f"({time.time() - state.attested_at:.0f}s > {max_age_s:.0f}s)"
        )

    drifted = state.selectors.symmetric_difference(current_selectors)
    mutable_drift = drifted & state.mutable_selectors
    parent_unchanged = (current_parent_root == state.parent_root)

    if mutable_drift and parent_unchanged:
        raise SelectorDriftError(
            f"refusing renewal for {state.spiffe_id}: mutable selectors "
            f"{sorted(mutable_drift)} drifted while parent root "
            f"'{state.parent_root}' stayed constant; this is identity "
            f"substitution, not reschedule"
        )

    if not parent_unchanged:
        # Legitimate reschedule: parent re-attested, leaf may differ.
        state.parent_root = current_parent_root
        state.attested_at = time.time()

    state.selectors = current_selectors
    return f"svid-issued:{state.spiffe_id}:{state.parent_root[:8]}"
