# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-aaif-credential-delegation-chain-reject-a-transitively-scoped-token.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-aaif-credential-delegation-chain-reject-a-transitively-scoped-token.warrant.json
# Markdown SHA-256:   1c5cf5b49254071f17890984add70a22d1e4e85be1b69cfec9bebe917245fe35
# Code AST SHA-256:   33ecb2b4a842915c37a671317dafc69cdf35a2b665a6bbc7f113e3a28f891ee3
# Verified Classes:   DelegationChainError, Link
# Defensive Guards:   4 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Sequence


class DelegationChainError(RuntimeError):
    """Raised when an AAIF delegation chain violates transitive scope intersection."""


@dataclass(frozen=True)
class Link:
    delegate_id: str
    granted_scope: frozenset[str]
    delegation_depth: int


def verify_chain(chain: Sequence[Link]) -> frozenset[str]:
    if not chain:
        raise DelegationChainError("empty delegation chain")

    effective = chain[0].granted_scope
    for i, link in enumerate(chain[1:], start=1):
        if not link.granted_scope <= chain[i - 1].granted_scope:
            raise DelegationChainError(
                f"link {i} ({link.delegate_id}) widens scope beyond parent: "
                f"{sorted(link.granted_scope - chain[i - 1].granted_scope)}"
            )
        if link.delegation_depth != i:
            raise DelegationChainError(
                f"link {i} ({link.delegate_id}) depth {link.delegation_depth} "
                f"!= traversed hops {i}"
            )
        effective = effective & link.granted_scope

    terminal = chain[-1]
    if not terminal.granted_scope <= effective:
        raise DelegationChainError(
            f"terminal scope {sorted(terminal.granted_scope)} not subset of "
            f"transitive intersection {sorted(effective)}"
        )
    return effective
