# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-of-a-federated-agent-refuse-a-trust-anchor-delegation-that-is-structurally-valid-but-semantically-unanchored.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-of-a-federated-agent-refuse-a-trust-anchor-delegation-that-is-structurally-valid-but-semantically-unanchored.warrant.json
# Markdown SHA-256:   8ed0449d69f37c331338296d624a2e74862aa1648a5125a5dbd1f4d4796fe537
# Code AST SHA-256:   201eab4bf49098c3fea31f8cf0c87397be8c0758ba4ba1940701046f2ad77aa5
# Verified Classes:   UnanchoredDelegationError, Link, NamespaceRegistry
# Defensive Guards:   4 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Iterable


class UnanchoredDelegationError(RuntimeError):
    """Raised when a delegation chain is cryptographically valid but its
    root or intermediate anchor does not own the namespace it delegates."""


@dataclass(frozen=True)
class Link:
    issuer_anchor: str
    delegated_namespace: str
    signature_valid: bool


class NamespaceRegistry:
    """Out-of-band, non-delegated authority records: (anchor, namespace) -> owns."""

    def __init__(self, records: Iterable[tuple[str, str]]):
        self._records = set(records)

    def owns(self, anchor: str, namespace: str) -> bool:
        # Exact-prefix ownership; a real registry would do longest-prefix match.
        return (anchor, namespace) in self._records


def validate_chain(
    chain: list[Link],
    terminal_namespace: str,
    registry: NamespaceRegistry,
) -> None:
    if not chain:
        raise UnanchoredDelegationError("empty delegation chain")

    # 1. Cryptographic validity of every link.
    for i, link in enumerate(chain):
        if not link.signature_valid:
            raise UnanchoredDelegationError(f"link {i} signature invalid")

    # 2. Anchor-relevance of every link: the issuer must actually own what it
    #    delegates. This is the conjunct naive validators omit.
    for i, link in enumerate(chain):
        if not registry.owns(link.issuer_anchor, link.delegated_namespace):
            raise UnanchoredDelegationError(
                f"link {i}: anchor {link.issuer_anchor!r} does not own "
                f"namespace {link.delegated_namespace!r} (unanchored delegation)"
            )

    # 3. The terminal namespace must be covered by the final delegated scope.
    if not terminal_namespace.startswith(chain[-1].delegated_namespace):
        raise UnanchoredDelegationError(
            f"terminal namespace {terminal_namespace!r} not covered by "
            f"final delegation {chain[-1].delegated_namespace!r}"
        )


def ingest(chain: list[Link], terminal_namespace: str, registry: NamespaceRegistry) -> str:
    validate_chain(chain, terminal_namespace, registry)
    return terminal_namespace
