# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-model-authored-context-pruning-as-a-non-idempotent-operation-against-its-own-future-evidence-base.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-model-authored-context-pruning-as-a-non-idempotent-operation-against-its-own-future-evidence-base.warrant.json
# Markdown SHA-256:   78cdd40de17204552a04627692322e123d03b7fdfa3e6f847a71de44695a9066
# Code AST SHA-256:   b2f326ad12ebb859d64d5369d0867ab4b0272b1fe8affb1800a80597e8127c4d
# Verified Classes:   SelfEditProvenanceError, Token, ContextWindow
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Iterable


class SelfEditProvenanceError(RuntimeError):
    """Raised when a context rewrite would destroy the evidence justifying itself."""


@dataclass(frozen=True)
class Token:
    turn: int
    text: str
    load_bearing: bool  # True if this token justified a prior or current prune decision


@dataclass
class ContextWindow:
    tokens: list[Token] = field(default_factory=list)
    pinned_policy_version: str = "v1"
    sidecar: dict[int, str] = field(default_factory=dict)  # turn -> externalized evidence

    def prune(self, keep: Iterable[Token], policy_version: str) -> None:
        if policy_version != self.pinned_policy_version:
            raise SelfEditProvenanceError(
                f"policy drift: window pinned at {self.pinned_policy_version!r}, "
                f"prune requested under {policy_version!r}; the policy was mutated "
                f"by a prior rewrite and can no longer authorize itself"
            )

        keep_set = {id(t) for t in keep}
        dropped = [t for t in self.tokens if id(t) not in keep_set]

        for tok in dropped:
            if tok.load_bearing and tok.turn not in self.sidecar:
                raise SelfEditProvenanceError(
                    f"non-idempotent prune: dropping turn {tok.turn} token "
                    f"{tok.text!r} removes the only justification for a prior "
                    f"decision, and no sidecar entry exists to audit it"
                )

        self.tokens = list(keep)

    def assert_idempotent(self, a: Iterable[Token], b: Iterable[Token]) -> None:
        """Verify Prune(A);Prune(B) == Prune(B);Prune(A) before committing either."""
        left = ContextWindow(list(self.tokens), self.pinned_policy_version, dict(self.sidecar))
        right = ContextWindow(list(self.tokens), self.pinned_policy_version, dict(self.sidecar))
        left.prune(a, self.pinned_policy_version)
        left.prune(b, self.pinned_policy_version)
        right.prune(b, self.pinned_policy_version)
        right.prune(a, self.pinned_policy_version)
        if [t.text for t in left.tokens] != [t.text for t in right.tokens]:
            raise SelfEditProvenanceError(
                "prune order is non-commutative: the rewrite policy is not "
                "idempotent over the current window; externalize evidence first"
            )
