# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-distinguish-taint-propagation-from-taint-contamination-in-tool-routing-quarantine.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-distinguish-taint-propagation-from-taint-contamination-in-tool-routing-quarantine.warrant.json
# Markdown SHA-256:   57b0fcb34b2e94a26ef5ed69c278930ae5668cb0fe52159e147618dcb5bbc26a
# Code AST SHA-256:   9cc3c415b475b6962a7c1dd97097134af90208ec09267b7dd12e4af7815f09b5
# Verified Classes:   TaintRoutingError, SinkMap, Invocation
# Defensive Guards:   4 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from typing import FrozenSet


class TaintRoutingError(RuntimeError):
    """Raised when a tool invocation cannot be soundly routed under taint policy."""


@dataclass(frozen=True)
class SinkMap:
    authority_positions: FrozenSet[str]
    inert_positions: FrozenSet[str]

    def __post_init__(self):
        overlap = self.authority_positions & self.inert_positions
        if overlap:
            raise TaintRoutingError(
                f"sink map declares positions as both authority and inert: {sorted(overlap)}"
            )


@dataclass
class Invocation:
    tool: str
    args: dict
    tainted_positions: FrozenSet[str]
    selector_tainted: bool
    sink_map: SinkMap | None = None


def route(inv: Invocation) -> str:
    if inv.sink_map is None:
        # Fail-closed: unmapped tool cannot distinguish inert from authority taint.
        raise TaintRoutingError(
            f"tool {inv.tool!r} has no sink map; cannot distinguish propagation from contamination"
        )

    unknown = inv.tainted_positions - (
        inv.sink_map.authority_positions | inv.sink_map.inert_positions
    )
    if unknown:
        raise TaintRoutingError(
            f"tool {inv.tool!r} received taint on undeclared positions: {sorted(unknown)}"
        )

    arg_tainted = bool(inv.tainted_positions & inv.sink_map.authority_positions)
    if arg_tainted or inv.selector_tainted:
        return "quarantine"
    return "allow"


def route_or_fail(inv: Invocation) -> str:
    decision = route(inv)
    if decision == "quarantine":
        raise TaintRoutingError(
            f"quarantined {inv.tool!r}: selector_tainted={inv.selector_tainted}, "
            f"authority_taint={sorted(inv.tainted_positions & (inv.sink_map.authority_positions if inv.sink_map else frozenset()))}"
        )
    return decision
