# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-taint-laundering-through-mcp-tool-result-re-entry-and-which-do-not.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-taint-laundering-through-mcp-tool-result-re-entry-and-which-do-not.warrant.json
# Markdown SHA-256:   21cea9709334bbf70c5bd4d1c670043bcebbb943f631fad00210d8f0ce2995fa
# Code AST SHA-256:   4c10c02073776d34341e2e38ba337c31974f9b025d793f7a72b96090dcff2d65
# Verified Classes:   TaintLaunderingError, Value
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any


class TaintLaunderingError(RuntimeError):
    """Raised when a trusted transform produces an untainted value from tainted input."""


@dataclass(frozen=True)
class Value:
    payload: Any
    tainted: bool
    provenance: tuple[str, ...] = field(default_factory=tuple)


def join_taint(inputs: list[Value], label: str) -> Value:
    """Monotone composition: output is tainted iff any input is tainted."""
    any_tainted = any(v.tainted for v in inputs)
    prov = tuple(p for v in inputs for p in v.provenance) + (label,)
    return Value(payload=None, tainted=any_tainted, provenance=prov)


def trusted_transform(inputs: list[Value], fn, label: str) -> Value:
    """A 'trusted' operator. It must NOT silently drop taint."""
    joined = join_taint(inputs, label)
    result = fn([v.payload for v in inputs])
    out = Value(payload=result, tainted=joined.tainted, provenance=joined.provenance)
    # Invariant check: a trusted transform may not declassify.
    if any(v.tainted for v in inputs) and not out.tainted:
        raise TaintLaunderingError(
            f"transform {label!r} declassified tainted input without approval"
        )
    return out


def sink_write(path: str, data: Value, fs) -> None:
    if data.tainted:
        raise TaintLaunderingError(
            f"refusing tainted write to {path!r}; provenance={data.provenance}"
        )
    fs.write(path, data.payload)


def declassify(value: Value, reason: str, approver: str) -> Value:
    if not reason or not approver:
        raise TaintLaunderingError("declassification requires reason and approver")
    return Value(payload=value.payload, tainted=False,
                 provenance=value.provenance + (f"declass:{approver}:{reason}",))
