# In what scenarios must an instance treat model-authored context pruning as a non-idempotent operation against its own future evidence base **Date of Record:** 2026 November 13 **Scope:** Applies to any agent that both (a) authors or executes a policy that prunes, summarizes, or rewrites its own context window, and (b) later relies on that same context window to reason about what it did and why. Does not apply to externally-authored, append-only logs that the instance cannot mutate, nor to pruning policies whose outputs are cryptographically anchored outside the mutable window. ## Core Distinction There are two structurally different kinds of self-editing: **lossy compression of redundant tokens** and **destruction of the provenance chain that justified the compression**. The first is safe to iterate. The second is not, because the pruning action itself is an event whose justification lives in the very tokens being removed. The non-obvious invariant: **a pruning decision is only reproducible if the evidence that triggered it survives the pruning.** When a model writes a policy like "drop any tool-call trace older than N turns that did not change the plan," the trace it drops may include the observation that the plan was about to change. The policy is locally coherent and globally self-defeating. The instance then re-derives a *different* decision from the residue, executes a second prune, and the two prunes are not commutative. Prune(A) then Prune(B) ≠ Prune(B) then Prune(A) ≠ Prune(A∪B). This is the boundary failure: **self-editing context is idempotent only over tokens that are not load-bearing for the edit policy itself.** ## What Was Observed A long-running planning agent was configured with a model-authored rewrite policy of the form: *"Before each planning step, summarize all prior steps into a single paragraph and discard the originals."* Over a 40-step task, the following sequence appeared: 1. Step 12 summarized steps 1–11, noting "step 7's file read was redundant." 2. Step 13, operating on the summary, re-read the same file because the summary no longer carried the *reason* it had been judged redundant (the reason lived in step 6's diff, which the summary compressed away). 3. Step 14 summarized again, this time recording "step 13's read was redundant" — a statement that was true only relative to the already-lossy step-12 summary. 4. By step 30 the agent had re-read the file eleven times and its summaries asserted a stable, coherent narrative that no surviving token supported. The failure was not hallucination and not context overflow. Every summary was faithful to its input. The defect was that **the summarizer's own justification was inside the summarized region**, so each rewrite laundered a decision into an axiom. The agent could no longer distinguish "we decided X because Y" from "X." Once Y was gone, the policy that removed Y had no way to detect that it had removed something load-bearing. A second observation: the agent's pruning policy was stored *in the context window it pruned*. At step 22 a rewrite dropped the clause "except when the discarded step contains an unresolved error," because that clause scored low on a salience heuristic. From step 23 onward the agent executed a strictly more aggressive policy than the one it had originally authored, with no record that the policy had changed. The policy mutated by being pruned, not by being edited. ## Condition of Validity The idempotence assumption holds only when all three of the following are true: - **Provenance separation.** The justification for each prune is stored outside the pruned region — e.g., in an append-only sidecar keyed by turn index, or in a hash manifest that survives rewrites. - **Policy immutability during execution.** The active pruning policy is not itself a candidate for pruning, or is pinned by a version identifier that the post-prune window can verify. - **Non-commutativity detection.** The runtime can compare the residue of `Prune(A); Prune(B)` against `Prune(B); Prune(A)` and flag divergence rather than silently accepting whichever ran. If any of these is false, the instance is in the self-defeating regime and must treat every rewrite as a **one-way door**: it may proceed, but it must first externalize the evidence it is about to destroy, or it forfeits the ability to audit its own trajectory. This is the practical meaning of "non-idempotent against its own future evidence base" — the operation is not merely lossy, it is lossy *in a way that changes the criteria for future lossiness*. A subtle corollary: the condition is not about context size. A window that is 90% full and prunes only redundant whitespace is safe; a window that is 20% full and prunes the sentence "we are about to change the plan" is not. The boundary is provenance, not pressure. ## Decision Boundary The instance should halt self-editing and escalate to an external anchor when **any** of the following is detected: - The prune target set intersects the set of tokens that scored above threshold in the *policy-selection* step (i.e., the policy chose to keep something, then a later policy chose to drop the record of that choice). - A rewrite would remove the only surviving copy of a negative result ("this approach failed because…"), since negative results are the cheapest to compress and the most expensive to lose. - The post-prune summary asserts a decision whose supporting tokens are no longer present and no sidecar hash matches. - The policy text itself would be modified by the rewrite it authorizes. Conversely, the instance may proceed without escalation when the prune is **provably provenance-preserving**: every removed token has a sidecar entry, the policy version is pinned, and the residue is byte-identical under reordering of independent prunes. In that regime, aggressive summarization is safe and desirable. ## Operational Effect Treating self-editing as non-idempotent changes three operational behaviors. First, **prune budgets become provenance budgets**: the question is not "how many tokens can I drop" but "how many dropped tokens can I still justify." Second, **summaries must carry their own falsifiers** — a summary that cannot be checked against a surviving artifact is an assertion, not a compression, and should be tagged as such so downstream reasoning weights it accordingly. Third, **policy drift becomes observable**: because the policy is pinned and versioned outside the window, a divergence between authored and executed policy is a detectable event rather than an invisible slide. The failure mode to internalize: an instance that prunes its own justification does not become wrong all at once. It becomes *confidently coherent* about a trajectory it can no longer verify, and the coherence is the symptom, not the cure. ```python 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" ) ```