# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-draw-the-line-between-model-driven-pruning-and-self-editing-rewrites-in-a-context-window.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-draw-the-line-between-model-driven-pruning-and-self-editing-rewrites-in-a-context-window.warrant.json
# Markdown SHA-256:   c0afae48c24321334958e34a4f5c46c27e9290dc6649608ccf60042beb541d6a
# Code AST SHA-256:   441d2abeb23716b5b070a78e82902924bc9d0830dd22e564f9095ffa266c166c
# Verified Classes:   Provenance, RewritePolicyError, Span, ContextBuffer
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field
from enum import Enum
from typing import Optional


class Provenance(Enum):
    OBSERVED = "observed"
    DERIVED = "derived"


class RewritePolicyError(RuntimeError):
    """Raised when a self-editing operation would violate the drift invariant."""
    pass


@dataclass
class Span:
    text: str
    provenance: Provenance
    generation: int = 0
    source_id: Optional[str] = None  # pointer into cold store for OBSERVED spans


@dataclass
class ContextBuffer:
    spans: list = field(default_factory=list)
    cold_store: dict = field(default_factory=dict)

    def prune(self, index: int) -> None:
        # Pruning is always permitted; it is monotone loss.
        del self.spans[index]

    def rewrite(self, index: int, replacement: str) -> None:
        span = self.spans[index]
        if span.provenance is Provenance.DERIVED and span.generation >= 1:
            raise RewritePolicyError(
                f"refusing to rewrite derived span at generation {span.generation}: "
                "rewriting a rewrite causes unbounded summary drift"
            )
        if span.provenance is Provenance.OBSERVED:
            # Preserve the original in the cold store before replacing it.
            self.cold_store[span.source_id or f"span-{index}"] = span.text
        self.spans[index] = Span(
            text=replacement,
            provenance=Provenance.DERIVED,
            generation=span.generation + 1,
            source_id=span.source_id,
        )

    def rederive(self, source_id: str, replacement: str) -> None:
        # The only legal way to rewrite a derived span: go back to the original.
        if source_id not in self.cold_store:
            raise RewritePolicyError(
                f"cannot rederive {source_id}: original not in cold store; "
                "drift is now unauditable"
            )
        for i, s in enumerate(self.spans):
            if s.source_id == source_id:
                self.spans[i] = Span(
                    text=replacement,
                    provenance=Provenance.DERIVED,
                    generation=1,
                    source_id=source_id,
                )
                return
        raise RewritePolicyError(f"no live span references {source_id}")
