# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-reject-a-capability-token-whose-egress-taint-lattice-has-been-widened-by-a-downstream-tool-and-how-is-that-boundary.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-reject-a-capability-token-whose-egress-taint-lattice-has-been-widened-by-a-downstream-tool-and-how-is-that-boundary.warrant.json
# Markdown SHA-256:   ee3249108e331cc3422ae674e28efbbde3adce5fedf245c870b7d870dacb3696
# Code AST SHA-256:   335ab7f55444748f7f1753d8a64c067eb0c2ff46dfdc53e91d5b878e4f58381c
# Verified Classes:   TaintRegressionError, TaintState
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from typing import FrozenSet


class TaintRegressionError(RuntimeError):
    """Raised when a tool lowers its output taint label relative to its input
    provenance, or when the joined taint breaches the token ceiling."""


@dataclass(frozen=True)
class TaintState:
    label: str
    provenance: FrozenSet[str] = field(default_factory=frozenset)

    def join(self, other: "TaintState") -> "TaintState":
        # Lattice join: label is the higher of the two, provenance is unioned.
        order = {"PUBLIC": 0, "INTERNAL": 1, "SECRET": 2}
        hi = self.label if order[self.label] >= order[other.label] else other.label
        return TaintState(hi, self.provenance | other.provenance)


def enforce_egress(
    accumulated: TaintState,
    tool_static: TaintState,
    output: TaintState,
    ceiling: str,
) -> TaintState:
    order = {"PUBLIC": 0, "INTERNAL": 1, "SECRET": 2}

    # 1. Provenance must not shrink: a tool may not relabel data it did not originate.
    if not output.provenance.issuperset(accumulated.provenance):
        missing = accumulated.provenance - output.provenance
        raise TaintRegressionError(
            f"label regression: output provenance missing {sorted(missing)}; "
            f"tool widened taint without carrying source provenance"
        )

    # 2. Ceiling is checked against the *joined* taint, not the per-call request.
    joined = accumulated.join(tool_static).join(output)
    if order[joined.label] > order[ceiling]:
        raise TaintRegressionError(
            f"ceiling breach after join: {joined.label} > {ceiling} "
            f"(provenance={sorted(joined.provenance)})"
        )
    return joined
