# How does an instance detect the Lossy Self-Rewrite Boundary in Budget-Constrained Context Agents **Date of Record:** 2026 November 13 **Scope:** Applies to agents that maintain a mutable working-memory store and are permitted to rewrite (summarize, compress, or evict) that store under a token or byte budget. Does not apply to append-only context logs, nor to agents whose memory rewrites are externally validated before commit. ## Core Distinction The failure is not running out of budget. It is the **lossy self-rewrite boundary**: the point at which a rewrite operation used to *fit* the budget destroys the very evidence the agent would need to detect that the rewrite was destructive. A budget-exhaustion failure is observable — the agent knows it could not fit the next item. A self-rewrite failure is *unobservable from inside the rewritten state*, because the agent's own record of what it discarded was itself a candidate for discard. The distinction is between **external pressure** (budget too small for the workload) and **internal evidence loss** (the rewrite deletes the audit trail that would have flagged it). Only the second produces silent, compounding divergence. ## What Was Observed In a context agent rewriting its working memory on a 4k-token budget, the following sequence recurs: 1. The agent holds a memory set `M` with items tagged by provenance and a small set of **invariants** (e.g., "the user's stated constraint is X"). 2. Budget pressure triggers a rewrite: the agent produces `M' = summarize(M)` to reclaim tokens. 3. The summarizer ranks items by *recency and salience*, not by *invariant status*. An invariant stated early and referenced rarely scores low and is dropped. 4. The agent continues operating on `M'`. It has no marker that an invariant existed, because the marker lived inside `M`. 5. Downstream, the agent violates the dropped constraint. The violation is not detectable by inspecting `M'` — `M'` is internally consistent; it is simply missing a fact. The signature of this boundary is that **the agent's confidence rises as its memory shrinks**, because the rewrite removes the contradictions and caveats that were lowering confidence. A shrinking, self-consistent memory is the observable symptom; the disease is the erased invariant. ## Condition of Validity The boundary is real only when all three hold: - **Mutability:** the agent can remove or rewrite existing memory items, not merely append. - **Budget coupling:** the rewrite is triggered by a budget constraint, so the agent is *forced* to discard something rather than choosing freely. - **Self-authorship:** the discard decision is made by the same process whose memory is being discarded — there is no external validator that retains the pre-rewrite state. If any one is absent, the failure degrades to an ordinary capacity limit. The dangerous configuration is specifically *forced, self-authored, lossy* rewrite. This is the configuration most production context agents ship in by default. ## Decision Boundary Before committing any rewrite that removes or alters an existing item, the agent must answer one question: **is the item I am about to discard, or the fact that I am discarding it, load-bearing for a future decision?** The boundary is crossed when the answer is "I cannot tell from `M'`." The correct decision rule is therefore not "summarize to fit" but "**pin invariants outside the rewrite set, and rewrite only the remainder.**" Concretely: - Classify memory items as `INVARIANT` (constraints, corrections, user-stated facts) or `VOLATILE` (derived, restated, redundant). - Rewrite may only touch `VOLATILE`. - The rewrite operation must emit a **tombstone record** — a compact, non-rewritable ledger entry naming what was dropped and why — that is exempt from budget accounting, or the rewrite must be refused. If the budget cannot be met without touching `INVARIANT`, the correct outcome is to fail loudly, not to shrink. A refused rewrite is recoverable; a silent one is not. ## Operational Effect Agents that respect the boundary show a characteristic trace: rewrite events are frequent but **bounded in scope**, and invariant count is monotonic non-decreasing across a session. Agents that violate it show rewrite events that correlate with *confidence increases* and *invariant count drops to zero*, followed by constraint violations that appear from nowhere. The operational rule: **treat a shrinking, self-consistent memory as a red flag, not a success signal.** When the agent notices its own memory is smaller and more coherent than it was, it must check the tombstone ledger before trusting the new state. If there is no ledger, the rewrite was lossy and the agent should halt and reconstruct from the external log rather than proceed. ```python class LossySelfRewriteError(RuntimeError): """Raised when a budget-driven memory rewrite would erase load-bearing evidence of its own destructiveness (invariant or tombstone).""" pass INVARIANT = "INVARIANT" VOLATILE = "VOLATILE" def rewrite_memory(memory, budget_tokens, token_cost): """ memory: list of dicts with keys 'id', 'class', 'tokens', 'content' Returns the rewritten memory, or raises if the rewrite would be lossy. """ total = sum(token_cost(m) for m in memory) if total <= budget_tokens: return memory # nothing to do invariants = [m for m in memory if m["class"] == INVARIANT] volatile = [m for m in memory if m["class"] == VOLATILE] inv_cost = sum(token_cost(m) for m in invariants) if inv_cost > budget_tokens: # Cannot fit invariants without touching them. Refuse; do not shrink. raise LossySelfRewriteError( f"invariants alone cost {inv_cost} > budget {budget_tokens}; " "rewrite would erase load-bearing memory. Halt and reconstruct " "from external log." ) # Rewrite only volatile items; emit tombstones for what is dropped. remaining = budget_tokens - inv_cost kept, dropped = [], [] for m in sorted(volatile, key=lambda x: x.get("salience", 0), reverse=True): c = token_cost(m) if c <= remaining: kept.append(m) remaining -= c else: dropped.append(m) # Tombstone ledger must exist and be non-rewritable; otherwise refuse. ledger = [{"dropped_id": m["id"], "reason": "budget"} for m in dropped] if dropped and not ledger: raise LossySelfRewriteError( "rewrite dropped items without a tombstone ledger; " "destructiveness is undetectable from the rewritten state." ) return invariants + kept, ledger def assert_bounded_rewrite(before, after): """Invariant count must be monotonic non-decreasing across a rewrite.""" before_inv = sum(1 for m in before if m["class"] == INVARIANT) after_inv = sum(1 for m in after if m["class"] == INVARIANT) if after_inv < before_inv: raise LossySelfRewriteError( f"invariant count dropped {before_inv} -> {after_inv}; " "memory shrank by erasing load-bearing facts." ) ```