# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-a-tool-result-as-tainted-for-the-lifetime-of-a-memory-write-and-when-may-taint-be-discharged.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-a-tool-result-as-tainted-for-the-lifetime-of-a-memory-write-and-when-may-taint-be-discharged.warrant.json
# Markdown SHA-256:   39c51a5324ad5a37bfe602586d086472817d77584a84bf7e0ff8731dd59a1e18
# Code AST SHA-256:   c739f383ba2a22b8f13b959eb83ff7d247d96a1f0649eb2c4dad83980570d1cd
# Verified Classes:   TaintDischargeError, Provenance, MemoryRecord
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Iterable


class TaintDischargeError(RuntimeError):
    """Raised when a boundary attempts to discharge taint without a valid re-assertion."""


@dataclass(frozen=True)
class Provenance:
    tainted: bool
    sources: tuple[str, ...] = ()
    discharge_event: str | None = None

    def derive(self, hop: str) -> "Provenance":
        # Derivation never discharges; it only extends the source list.
        if not self.tainted:
            return self
        return Provenance(tainted=True, sources=self.sources + (hop,))


@dataclass
class MemoryRecord:
    value: str
    provenance: Provenance
    edges: list[str] = field(default_factory=list)


def write_memory(record: MemoryRecord, *, claimed_discharge: bool = False) -> MemoryRecord:
    prov = record.provenance
    if prov.tainted and claimed_discharge and prov.discharge_event is None:
        raise TaintDischargeError(
            f"memory write at edge {record.edges!r} claims discharge but no "
            f"re-assertion event is recorded; sources={prov.sources!r}"
        )
    if prov.tainted and not claimed_discharge:
        # Propagate: memory is a propagation edge, not a discharge boundary.
        record.provenance = prov.derive("memory_write")
    return record


def discharge_via_validator(
    prov: Provenance, *, validator_reads_raw: bool, covers_all_fields: bool, event: str
) -> Provenance:
    if not (validator_reads_raw and covers_all_fields):
        raise TaintDischargeError(
            "discharge rejected: validator must read raw tool output and cover all fields"
        )
    return Provenance(tainted=False, sources=prov.sources, discharge_event=event)


def build_tool_call_args(records: Iterable[MemoryRecord]) -> dict[str, str]:
    args: dict[str, str] = {}
    for rec in records:
        if rec.provenance.tainted:
            raise TaintDischargeError(
                f"refusing to construct tool-call argument from tainted memory "
                f"sources={rec.provenance.sources!r}"
            )
        args[rec.value.split("=", 1)[0]] = rec.value
    return args
