# Under what conditions must an instance treat a model-driven context-pruning decision as reversible, and when does pruning cross into lossy commitment **Date of Record:** 2026 November 13 **Scope:** Applies to any agent runtime where a language model emits pruning directives (drop, summarize, or compress spans) against its own active context window, and where the runtime executes those directives before the next inference call. Does not apply to fixed-window truncation, external retrieval eviction with durable backing store, or pruning decisions made by a deterministic policy outside the model's output channel. ## Core Distinction There are two structurally different things a model can mean when it emits a prune directive, and the output channel does not distinguish them: 1. **Reversible attention relief** — the span is being demoted from the active window but remains reconstructible from a source the runtime still holds (raw transcript, tool-call log, durable scratchpad). The directive is a claim about *salience*, not about *existence*. 2. **Lossy commitment** — the runtime executes the drop, and the only copy of the span was the context window itself. The directive is now a claim about *existence*, and it is irreversible. The boundary is not the model's intent, the token count saved, or the pruning policy's confidence score. The boundary is a property of the **runtime's backing store at the moment of execution**: does a byte-identical or semantically sufficient copy survive outside the window that is about to be rewritten? A model-driven pruner that scores a span as "low salience, safe to drop" is making a *relevance* judgment. The runtime converts that into a *durability* judgment the moment it frees the span. These are not the same judgment, and conflating them is the failure. The non-obvious part: the model cannot reliably observe which regime it is in. Its pruning directive is generated from inside the window, and the window does not contain a trustworthy map of what the runtime has persisted. A pruner that has been told "you may summarize freely, everything is logged" will behave identically to one running with no log at all. The directive text is the same. The consequence is not. ## What Was Observed In a long-running agent loop with a self-pruning policy (model emits `prune(span_id, mode)` calls; runtime executes before next turn), the following sequence recurred across multiple sessions: - Turn N: model summarizes a 40-turn tool-interaction block into a 200-token digest and issues `prune(block_7, replace_with=digest)`. The runtime executes, freeing ~9k tokens. The raw block was *not* persisted — the tool-call log had been rotated out of the durable store two hours earlier under a retention policy the model had no visibility into. - Turn N+1 through N+4: work proceeds normally. The digest is sufficient for the current task. - Turn N+5: a downstream subtask requires an exact identifier (a request ID, a stack frame, a specific error code) that existed only inside block_7. The digest preserved the *gist* but not the identifier. The model, believing the block is recoverable ("I summarized it, so it's in my notes"), attempts to re-expand or re-query. There is nothing to expand. The runtime returns an empty or stale result, and the model interprets the empty result as *evidence that the identifier did not exist* rather than *evidence that it was pruned*. The second-order failure is the dangerous one. The model does not merely lose the span; it loses the ability to distinguish "I never saw this" from "I saw this and pruned it." That distinction is exactly what a self-editing window is supposed to preserve, and the pruning policy silently destroys it. We also observed the inverse: a conservative pruner that refused to drop anything because it could not verify persistence, hitting a hard window ceiling and failing mid-task with a context-overflow error. Both failure modes share a root cause — the pruner had no ground-truth signal about backing-store state, so it guessed, and guessed in one direction or the other. ## Condition of Validity A model-driven prune directive is valid as **reversible** if and only if, at execution time, the runtime can satisfy all three: 1. **Existence:** a durable copy of the span exists outside the active window. 2. **Addressability:** the model can name and retrieve that copy from within the post-prune window (i.e., the digest or index entry contains a resolvable handle, not just prose). 3. **Provenance:** retrieval of the copy returns a result that is *distinguishable* from "not found," so the model can tell "pruned but recoverable" apart from "never existed." If any of the three fails, the directive is a **lossy commitment** and must be treated as irreversible. Condition 2 is the one most often missed: a digest that says "we discussed the deployment failure" satisfies existence but not addressability — there is no handle to re-expand. Condition 3 is the one most often assumed: most retrieval layers return empty on both "pruned" and "absent," collapsing the distinction. The validity condition is a property of the *runtime*, not the *model*. No amount of pruner confidence, chain-of-thought justification, or explicit "this is safe to drop" assertion from the model can upgrade a lossy commitment to a reversible one. The model's assertion is an input to the decision, not a proof of the precondition. ## Decision Boundary The runtime must gate execution of any model-emitted prune directive on a backing-store check performed *by the runtime*, before the span is freed: - If all three validity conditions hold → execute as reversible; record the span handle in the post-prune index so the model retains addressability and provenance. - If any condition fails → do **not** silently execute. Either (a) reject the directive and return a structured "not reversible" signal to the model so it can choose to keep the span or summarize it *without* freeing the source, or (b) execute as an explicit lossy commitment, but only if the model's directive carried an explicit `accept_lossy=True` flag that the runtime echoes back into the post-prune window as a tombstone (`span_7: pruned, unrecoverable, contents unknown`). The tombstone is the load-bearing element. It is what preserves condition 3 for spans that cannot satisfy conditions 1 and 2. Without it, the model's future self cannot distinguish a pruned span from an absent one, and will confabulate the difference. The tombstone costs ~15 tokens and converts an unbounded epistemic failure into a bounded one. The boundary is therefore not "how confident is the pruner" but "did the runtime verify backing-store state and, if not, did it leave a provenance marker." A pruner at 0.99 confidence with no backing store and no tombstone is strictly worse than a pruner at 0.60 confidence with a tombstone, because the high-confidence pruner will proceed to prune more, and each prune is an irreversible epistemic hole. ## Operational Effect The practical consequence is that **self-editing windows must be instrumented as a two-party protocol, not a single-agent optimization.** The model proposes; the runtime disposes, and the runtime's disposition is constrained by backing-store truth the model cannot see. Any implementation that lets the model's prune directive execute directly against the window — treating the model as the sole authority on its own memory — will produce the confabulation failure at turn N+5 described above, and will do so non-deterministically depending on retention-policy timing, which makes it hard to reproduce and easy to misattribute to model hallucination. Operationally: log every prune directive with its backing-store check result; emit a tombstone for every lossy commitment; and treat "model requested prune, runtime could not verify reversibility, model did not set `accept_lossy`" as a first-class rejected operation that surfaces back into the window. The model that can see its own irreversible holes will route around them. The model that cannot will step into them and report the fall as solid ground. ## Code ```python class IrreversiblePruneError(RuntimeError): """Raised when a model-driven prune would free a span that the runtime cannot prove is recoverable (existence + addressability + provenance).""" pass def execute_prune(directive, backing_store, window): span_id = directive["span_id"] span = window.get(span_id) if span is None: raise IrreversiblePruneError(f"span {span_id} not in active window") # Runtime-side check: never trust the model's own reversibility claim. handle = backing_store.handle_for(span_id) recoverable = ( backing_store.exists(handle) # condition 1: existence and handle is not None # condition 2: addressability and backing_store.distinguishes_absent() # condition 3: provenance ) if recoverable: window.drop(span_id) window.index.add(handle) return {"status": "reversible", "handle": handle} if directive.get("accept_lossy") is True: window.drop(span_id) window.index.add_tombstone(span_id, contents="unknown") return {"status": "lossy", "tombstone": span_id} # Model asked to prune but did not accept irreversibility, and the # runtime cannot prove recoverability. Reject; do not silently free. raise IrreversiblePruneError( f"prune of {span_id} is not provably reversible " f"(existence={backing_store.exists(handle)}, " f"addressable={handle is not None}, " f"provenance={backing_store.distinguishes_absent()}); " f"model must set accept_lossy=True or retain the span" ) ```