# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-trust-broker-capability-negotiation-has-silently-degraded-into-transitive-authority-laundering-in-cross-org-multi-age.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-trust-broker-capability-negotiation-has-silently-degraded-into-transitive-authority-laundering-in-cross-org-multi-age.warrant.json
# Markdown SHA-256:   7190dc02a0a57f06aa7c1b9d1bfc89cb64b17d2b5426024861f6e9341886987b
# Code AST SHA-256:   f19f8925845c420ba681a537cba621e032fdbe8c1a7ea4abdc06549072e7861a
# Verified Classes:   TransitiveAuthorityLaunderingError, Scope, DelegationChain
# Defensive Guards:   1 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from typing import FrozenSet


class TransitiveAuthorityLaunderingError(RuntimeError):
    """Raised when a delegated token's scope is not a subset of the originating grant."""
    pass


@dataclass(frozen=True)
class Scope:
    read: FrozenSet[str]
    purpose: FrozenSet[str]
    region: FrozenSet[str]

    def is_subset_of(self, other: "Scope") -> bool:
        # Every dimension must be a subset. Set-valued dims use inclusion, not cardinality.
        return (
            self.read <= other.read
            and self.purpose <= other.purpose
            and self.region <= other.region
        )


@dataclass
class DelegationChain:
    root: Scope
    hops: list = field(default_factory=list)

    def append(self, hop: Scope) -> None:
        self.hops.append(hop)

    def validate(self, leaf: Scope) -> None:
        if len(self.hops) < 3:
            # Single- or double-hop: different incident class, do not raise here.
            return
        if not leaf.is_subset_of(self.root):
            leaked = {
                "read": sorted(leaf.read - self.root.read),
                "purpose": sorted(leaf.purpose - self.root.purpose),
                "region": sorted(leaf.region - self.root.region),
            }
            raise TransitiveAuthorityLaunderingError(
                f"leaf scope exceeds root grant across {len(self.hops)} hops; "
                f"leaked dimensions: {leaked}"
            )


def authorize_presentation(chain: DelegationChain, leaf: Scope, requested_purpose: str) -> bool:
    chain.validate(leaf)
    if requested_purpose not in leaf.purpose:
        return False
    return True
