# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-draw-the-taint-boundary-between-tool-arguments-and-tool-results-the-argument-provenance-invariant-in-taint-aware-tool-gating.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-draw-the-taint-boundary-between-tool-arguments-and-tool-results-the-argument-provenance-invariant-in-taint-aware-tool-gating.warrant.json
# Markdown SHA-256:   531b5cef92c240c7b68f7b77b8ceef87bde414a580705a5dd467a4bc838ed3a6
# Code AST SHA-256:   48748e3fc583ea6f8302c78830105a57d91e4a6c27239accef05b419764177ff
# Verified Classes:   TaintGateError, ToolSpec
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class TaintGateError(RuntimeError):
    """Raised when a tainted value reaches an egress-capable argument position."""
    pass


CAPABILITY_CLASSES = {"pure", "local_read", "local_write", "egress", "exec"}


class ToolSpec:
    def __init__(self, name, positions, destination_resolver=None):
        # positions: dict arg_name -> capability class
        for cap in positions.values():
            if cap not in CAPABILITY_CLASSES:
                raise TaintGateError(f"unknown capability class {cap!r} in {name}")
        self.name = name
        self.positions = positions
        self.destination_resolver = destination_resolver


TRUSTED_DESTINATIONS = {"internal-archive", "sandbox-tmp"}


def gate_call(spec: ToolSpec, args: dict, taint: dict) -> None:
    """taint: arg_name -> bool (True if origin-tainted, post-serialization)."""
    for name, cap in spec.positions.items():
        if not taint.get(name, False):
            continue
        if cap in ("egress", "exec"):
            dest = (
                spec.destination_resolver(args)
                if spec.destination_resolver
                else None
            )
            if dest not in TRUSTED_DESTINATIONS:
                raise TaintGateError(
                    f"tainted arg {name!r} reaches {cap} position of "
                    f"{spec.name!r} with destination {dest!r}"
                )
        elif cap == "local_write":
            dest = (
                spec.destination_resolver(args)
                if spec.destination_resolver
                else None
            )
            if dest not in TRUSTED_DESTINATIONS:
                raise TaintGateError(
                    f"tainted arg {name!r} written to untrusted local "
                    f"destination {dest!r} via {spec.name!r}"
                )
        # pure / local_read: allowed; no raise.
