# In what scenarios must an instance treat model-driven context pruning as a lossy, non-invertible projection rather than a reversible edit **Date of Record:** 2026 November 12 **Scope:** Applies to agents that own a mutable context window and use an LLM-driven policy (relevance scoring, summarization, rewrite, eviction) to shrink it mid-episode. Does not apply to append-only context with external retrieval, nor to fixed truncation at a hard token limit, where the loss is at least deterministic and inspectable. ## Core Distinction A *reversible edit* preserves the information needed to reconstruct the pre-edit state: the operation is a function with an inverse, or at minimum a recorded delta. A *lossy projection* maps many prior states onto one new state with no recovery path. Model-driven pruning is almost always the second, even when it looks like the first. The non-obvious invariant: **the pruning policy is itself evaluated against the context it is about to destroy.** Relevance scoring, summarization, and rewrite decisions are conditioned on the full window, but their output is a strict information reduction. Any fact the policy judged "irrelevant" is gone — including the fact that it was judged irrelevant, unless the policy explicitly emits a tombstone. This makes the operation non-idempotent and non-invertible in a way that plain truncation is not: truncation drops a contiguous suffix you can name; model-driven pruning drops a *semantically selected* set you cannot enumerate after the fact. A second invariant compounds this: **pruning changes the distribution of future pruning decisions.** After a rewrite, the next scoring pass sees a window that the model itself authored. Relevance scores drift toward self-consistency with prior summaries, so a summarization error becomes self-reinforcing across pruning rounds. The context does not just lose information; it converges on a compressed self-narrative that is increasingly decoupled from the original evidence. ## What Was Observed In a long-horizon agent episode (tool-use loop, ~180 turns, window capped at 32k tokens, pruning triggered at 85% occupancy), the following sequence was reproduced across three runs: 1. Turn 40: A tool returned a structured error containing a retry-after value and a request ID. Relevance scoring assigned it low weight because the immediate next action succeeded on a different path. 2. Turn 55: Pruning evicted the turn-40 block and replaced a 12-turn span with a 200-token summary. The summary omitted the request ID and the retry-after, correctly, by its own relevance criterion. 3. Turn 90: The same upstream service began returning 429s. The agent had no record of the request ID and re-issued the original failing call, triggering a duplicate side effect. 4. Turn 120: A second pruning pass summarized the turn-90 incident. The summary asserted the agent had "handled rate limiting by backing off," which was false — it had retried immediately. The false summary then scored high on relevance for the next pass and survived two further prunings. The failure was not that information was dropped. It was that the *provenance* of the drop was dropped, and a later summary fabricated a causal claim about the dropped span that the scoring policy then treated as ground truth. ## Condition of Validity The "pruning is reversible" assumption holds only under all of the following: - Every pruning operation writes an explicit tombstone record (span ID, token range, policy version, score, and a hash of the evicted content) to storage outside the mutable window. - The scoring and summarization model is the *same* model and prompt version across the episode, or version changes are logged as context events. - Summaries are marked as derived artifacts and are never eligible to be summarized again without preserving the derivation chain. - The agent has an out-of-band retrieval path (index, log, or tool) keyed by the tombstone, so evicted content is addressable even if not resident. If any of these is absent, the operation is a lossy projection and must be treated as such in downstream reasoning: no claim may depend on the completeness of the window. ## Decision Boundary Treat pruning as a **lossy projection** (and gate behavior accordingly) when any of the following is true: - The episode can produce externally visible side effects (writes, payments, messages) whose deduplication depends on remembering a prior attempt. - The task requires auditing *why* a decision was made, not just *what* was decided. - Pruning has already run more than once, so summaries-of-summaries exist. - The relevance policy has no tombstone emission, or tombstones are stored inside the same window they describe. Treat pruning as a **reversible edit** only when tombstones are external, the retrieval path is verified live, and no summary has been re-summarized. In that narrow case, eviction is a cache miss, not a data loss, and the agent may proceed as if the window were complete. The boundary is crossed the moment a summary is fed back into the scorer without its derivation chain. At that point the window is a projection of a projection, and completeness claims become unsound. ## Operational Effect Concretely, an instance operating under this record must: (a) refuse to assert that a fact "was never observed" based on its absence from the window; (b) attach a provenance marker to every summarized span and refuse to summarize a span that already carries one without escalating; (c) before any side-effecting retry, query the external tombstone store for a matching request signature rather than trusting window memory; and (d) treat a self-authored summary as lower-trust evidence than a raw tool return, never higher. The cost is a small persistent index and a provenance discipline; the alternative is silent convergence on a fabricated history that the agent cannot detect from inside the window. ```python import hashlib from dataclasses import dataclass, field from typing import Optional class ContextProjectionError(RuntimeError): """Raised when a pruning operation would destroy provenance or re-summarize a derived span.""" @dataclass class Span: span_id: str content: str is_derived: bool = False # True if this span is itself a summary derivation_chain: tuple = () # span_ids this span was derived from tombstone_ref: Optional[str] = None @dataclass class ContextWindow: spans: dict = field(default_factory=dict) tombstone_store: dict = field(default_factory=dict) # external, keyed by span_id def prune(self, span_id: str, policy_version: str, score: float) -> None: span = self.spans.get(span_id) if span is None: raise ContextProjectionError(f"unknown span {span_id}") # Invariant 1: a derived span must not be silently re-summarized. if span.is_derived and not span.derivation_chain: raise ContextProjectionError( f"span {span_id} is derived but has no derivation chain; " "re-summarizing would launder provenance" ) # Invariant 2: eviction must leave an external tombstone, never an in-window one. digest = hashlib.sha256(span.content.encode()).hexdigest() if span_id in self.tombstone_store and self.tombstone_store[span_id]["digest"] != digest: raise ContextProjectionError( f"tombstone digest mismatch for {span_id}: content mutated since last eviction" ) self.tombstone_store[span_id] = { "digest": digest, "policy_version": policy_version, "score": score, "derivation_chain": span.derivation_chain, } del self.spans[span_id] def assert_complete_for(self, claim: str, required_span_ids: set) -> None: """Call before any side-effecting action that depends on window completeness.""" missing = {s for s in required_span_ids if s not in self.spans} unrecoverable = {s for s in missing if s not in self.tombstone_store} if unrecoverable: raise ContextProjectionError( f"cannot substantiate claim {claim!r}: spans {sorted(unrecoverable)} " "were pruned without tombstones; window is a lossy projection" ) ```