The Context Garbage Collector

Most agents still manage context the way scripts clean up temp files: delete anything old, or summarize and hope nothing load-bearing got compressed away. A July 2026 paper out of Xiaohongshu (arXiv:2607.00692) tries something closer to how a real garbage collector works: turn tool results, files, plans, and constraints into indexed objects, let a planner propose fold, mask, or prune per object, and only commit the edit through a harness that keeps a recoverable sidecar and respects the model's prompt cache. On a 33-session adversarial test set, Self-GC pruned 43.95% of prefix tokens while leaving 84.85% of future continuations unaffected; heuristic baselines that pruned harder still broke far more of them, holding a no-impact rate of only 54.55% to 69.70%. On a 332-session production-derived suite, three independent planner backbones held 91.27% to 94.58% against 77.71% to 87.46% for the baselines, and it shipped: an account-level production split cut daytime average input tokens 10% to 15%, with peak reductions near 20%. Here's the mechanism, the exact numbers, and a simplified version you can build against your own agent loop this week.

Published 2026-08-01 by Dor Amir on the Nadir blog.

Filed under Context & Compression.

Abstract.

Every long-horizon agent hits the same wall eventually: the context window fills up with tool results, file contents, half-finished plans, and constraints the user stated four turns ago, and something has to give. Today's answer is usually one of two blunt instruments: drop the oldest messages, or ask the model to write a summary and hope nothing load-bearing got compressed away. A July 2026 paper from Xiaohongshu (arXiv:2607.00692) proposes a third option, borrowed from a discipline that solved a version of this problem decades ago: garbage collection. Self-GC turns an agent's accumulated context into indexed objects, has a planner decide per object whether to fold, mask, or prune it based on future dependency rather than age, and commits that decision through a harness step that keeps a recoverable sidecar and doesn't break the model provider's prompt cache. On an adversarial 33-session test set, it pruned 43.95% of prefix tokens while leaving 84.85% of future continuations unaffected, clearly ahead of heuristic baselines that pruned harder and still broke more. On a 332-session production-derived suite, three separate planner backbones held 91.27% to 94.58%. It's already running: an account-level production split cut daytime average input tokens 10 to 15%, with peak reductions near 20%.

Research question.

If an agent's context is treated as a set of objects with a lifecycle, indexed, scored for future dependency, and prunable through a recoverable, cache-aware commit path, rather than as a flat text log truncated by age, how much can be safely removed, and does the resulting system still hold up once it's carrying real production traffic instead of a benchmark set?

Why chronological pruning and self-summary both fail long-horizon agents.

Long-horizon agents don't accumulate context the way a chat transcript does. A single session can carry a file's full contents fetched three tool calls ago, a plan the agent committed to two turns back, a constraint the user stated once and never repeated, and a dozen intermediate tool results of wildly different relevance to whatever the agent is about to do next. That's structured state, not disposable text, and the paper's framing is blunt about why the common fixes don't treat it that way. Chronological pruning drops what's oldest, on the assumption that older roughly means less relevant, an assumption that breaks the moment a plan or constraint from early in the session is still exactly what a later step needs. Tool-output masking hides results using the same blind, age-based logic. A final self-summary near the context limit avoids that specific failure but introduces a different one: it compresses everything into prose, and prose is bad at preserving exact evidence, precise locators, or artifacts a later step needs to edit rather than just read about. The same failure mode this blog measured in Chroma's context-rot research: the tokens hurting accuracy and the tokens inflating the bill are usually the same tokens, and a fix that's blind to which tokens those are can't reliably target them.

Context as objects, not a text suffix.

Self-GC's mechanism, echoing the name on purpose, mirrors how a real garbage collector works: not "delete anything old," but "track every object, decide what's still reachable from where the program is headed, and reclaim the rest, recoverably." Three stages.

Three-stage diagram of Self-GC's context lifecycle: INDEX turns user turns, tool spans, and skill state into indexed objects; PLAN has a side-channel planner propose a fold, mask, or prune action per object; COMMIT has the harness rehearse the edit, keep a recoverable sidecar, and commit at a cache-aware boundary.
Three-stage diagram of Self-GC's context lifecycle: INDEX turns user turns, tool spans, and skill state into indexed objects; PLAN has a side-channel planner propose a fold, mask, or prune action per object; COMMIT has the harness rehearse the edit, keep a recoverable sidecar, and commit at a cache-aware boundary.

Index. User turns, tool spans, and skill state get turned into indexed objects instead of staying appended text. That's what makes the next two steps possible at all, you can't reason about "does anything downstream still need this" if the thing in question isn't addressable on its own.

Plan. A side-channel planner, a separate model call outside the main agent loop, looks at the indexed objects and proposes an action per object: fold it into a shorter form, mask it (hide it from the prompt but keep it retrievable), or prune it outright. The decision is scored against future dependency, not recency, which is the direct fix for what chronological pruning gets wrong.

Commit. This is the step that keeps the system honest. The harness rehearses the proposed edit rather than applying it blind, keeps a recoverable sidecar so a folded or pruned object isn't gone for good, and only commits at a boundary chosen to respect the model provider's prompt cache. That last part matters more than it might look: a compression or pruning step that rewrites the same prefix on every call breaks prefix caching outright, turning a token-saving move into a cache-invalidating one that can cost more than doing nothing. Self-GC's commit step is built around avoiding exactly that trap.

The numbers: pruning harder is not the same as pruning safely.

The paper reports two evaluation regimes, and the contrast between them is the paper's real argument.

Bar chart comparing no-impact rate between heuristic baselines and Self-GC: on a 33-session Hard Set, baselines hold 54.55–69.70% versus 84.85% for Self-GC; on a 332-session production suite, baselines hold 77.71–87.46% versus 91.27–94.58% for three Self-GC planner backbones.
Bar chart comparing no-impact rate between heuristic baselines and Self-GC: on a 33-session Hard Set, baselines hold 54.55–69.70% versus 84.85% for Self-GC; on a 332-session production suite, baselines hold 77.71–87.46% versus 91.27–94.58% for three Self-GC planner backbones.
EvaluationSessionsSelf-GC no-impact rateBaseline no-impact rate
Hard Set (adversarial)3384.85% (43.95% of prefix tokens pruned)54.55–69.70%
Production-derived suite33291.27–94.58% (3 planner backbones)77.71–87.46%

Source: Hao et al., arXiv:2607.00692, "Self-GC: Self-Governing Context for Long-Horizon LLM Agents," Xiaohongshu, July 2026

Read the Hard Set row carefully, because the detail that matters isn't the headline percentage, it's what the paper says right next to it: the heuristic baselines pruned more aggressively than Self-GC's 43.95% and still landed a lower no-impact rate. More pruning did not mean better pruning. That's the whole case against age-based heuristics in one line: they have no way to tell the difference between a token that's safe to drop and one that isn't, so more aggressive pruning just means more of both.

It held at production scale, and it shipped.

A result that only survives on a 33-session hand-built set is a demo. The paper also ran the comparison on a 332-session production-derived suite, and tested it against three different planner backbones rather than one tuned model, both a check against the result being an artifact of one specific setup. The no-impact rate held in the 91 to 95% range across all three, comfortably ahead of every baseline tested. Then it went further than most systems papers do: an online account-level split in production measured daytime average input tokens down 10% to 15%, with peak reductions near 20%. That's a smaller number than the benchmark's headline pruning rate, which is exactly what you'd expect, real production traffic is messier and less uniformly prunable than a curated evaluation set, and it's a more trustworthy number for the same reason.

Where cache-aware commit fits next to compression and caching.

This isn't a replacement for the token-reduction techniques already in a typical stack, it's a layer aimed at a different failure mode.

Compression (LLMLingua-style)Prompt cachingSelf-GC
What it acts onA single request's prompt textA repeated, byte-identical prefixMulti-turn agent state across a whole session
Decision basisToken-level information scoreExact prefix matchObject-level future dependency
Reversible if wrongNo, tokens are goneN/A, nothing is removedYes, recoverable sidecar
Breaks the prefix cache if applied naivelyYes, if compression output changes per callN/ADesigned not to, via cache-aware commit
Best targetA single long prompt or retrieved documentStatic system prompts, tool schemasLong-horizon, multi-turn session state

The three stack rather than compete: compression shrinks what's about to be sent, caching discounts what repeats unchanged, and object-level context governance decides what's still in the window at all by the time either of the other two gets a turn. This blog has already shown that these levers are multiplicative, not additive, when they interact well, and can actively cancel out when one breaks the other's assumptions; Self-GC's cache-aware commit step is a direct example of a system built to avoid the cancel-out case on purpose, rather than discovering it in a postmortem.

Build a simplified version against your own agent loop.

You don't need the paper's exact planner or its evaluation harness to test the underlying idea against your own agent. The shape of a minimal version:

from dataclasses import dataclass, field
from enum import Enum

class Action(Enum):
    KEEP = "keep"
    FOLD = "fold"
    MASK = "mask"
    PRUNE = "prune"

@dataclass
class ContextObject:
    id: str
    kind: str          # "user_turn" | "tool_span" | "skill_state"
    content: str
    # sidecar holds the full original content even after fold/mask/prune,
    # so a later step can still recover it if the planner guessed wrong.
    sidecar: str = field(default="")

def plan_actions(objects: list[ContextObject], upcoming_task: str, planner_lm) -> dict[str, Action]:
    """Side-channel call: ask a planner model which objects the
    upcoming step actually depends on, not which ones are oldest."""
    prompt = (
        f"Upcoming task: {upcoming_task}\n\n"
        f"Objects in context:\n" +
        "\n".join(f"[{o.id}] ({o.kind}) {o.content[:200]}" for o in objects) +
        "\n\nFor each object id, return keep, fold, mask, or prune, "
        "based on whether the upcoming task still depends on it."
    )
    return planner_lm.structured_complete(prompt, schema=dict[str, Action])

def commit(objects: list[ContextObject], actions: dict[str, Action], cache_boundary_id: str):
    """Harness step: apply only past the cache boundary, keep sidecars,
    never destroy content that was pruned or folded."""
    committed = []
    past_boundary = False
    for o in objects:
        past_boundary = past_boundary or o.id == cache_boundary_id
        action = actions.get(o.id, Action.KEEP)
        if not past_boundary or action == Action.KEEP:
            committed.append(o)
            continue
        o.sidecar = o.sidecar or o.content
        if action == Action.FOLD:
            o.content = summarize(o.content)  # short form, sidecar keeps the full original
        elif action == Action.MASK:
            o.content = f"[masked, recoverable: {o.id}]"
        elif action == Action.PRUNE:
            continue  # dropped from the live window, sidecar still holds it
        committed.append(o)
    return committed

The two details worth keeping even in a much simpler version: never overwrite sidecar once it's set, so a wrong prune is recoverable, and never apply an action ahead of cache_boundary_id, so the edit doesn't retroactively change a prefix a provider might have already cached. Nadir's context optimization already strips redundant tool schemas and repeated boilerplate before a request goes out, which is the single-request version of the same instinct; this is the same discipline applied across an entire multi-turn session instead of one call.

What this doesn't solve.

The planner step is an LLM call, or a call to a smaller model standing in as one, so it's a side-channel cost that has to stay smaller than the tokens it reclaims, plus whatever latency it adds to the loop. The published numbers come from one company's production traffic and a 33-session adversarial set built to stress-test the approach; both are real evidence, neither guarantees the same no-impact rate on a materially different agent workload with a different mix of tool calls and session lengths. And this is a systems paper describing one company's design, not a benchmark run against every existing long-horizon memory approach, so treat the specific percentages as evidence the design pattern works, not as a universal constant to plug into a different stack unmeasured.

Conclusion.

The insight Self-GC is built on isn't complicated: an agent's context is not one flat string that gets longer, it's a collection of distinct objects with different lifetimes, and pruning by age throws away exactly as much signal as it happens to catch by accident. Indexing objects, scoring them against what's actually coming next, and committing the result through a path that keeps things recoverable and doesn't quietly break the prompt cache, is closer to how any other long-running system manages memory than what most agent harnesses do today. The gap between "pruned 43.95% and broke 15% of continuations" and "pruned harder and broke 30 to 45% of them" is the whole argument for treating context governance as its own discipline, not a side effect of however truncation happens to be implemented this week.


Sources: [Hao, Meng, Yin, Zhu, and Cao, "Self-GC: Self-Governing Context for Long-Horizon LLM Agents," arXiv:2607.00692, Xiaohongshu, submitted July 1, 2026](https://arxiv.org/abs/2607.00692).

More on context & compression

What Nadir is

Nadir is an LLM router. Nadir sends each prompt to the cheapest model that can handle it. A small decision model reads each prompt against your model menu and picks the lowest-cost model it expects to handle it. When the decision model is uncertain or unavailable, the request goes to the strongest model on the menu. The routing step makes no call to your model provider.

Nadir runs two ways. The decision API returns a model, reasoning-effort, cache, context, and policy recommendation without calling a model provider, beside the gateway you already run. That is how a shadow-mode evaluation works, and its projected savings stay advisory. The OpenAI compatible managed proxy executes the route, and migration is a two-line change: point the base URL at api.getnadir.com and set model to auto. On that path an optional verifier can score a complete non-streaming answer and escalate to a stronger model when it misses the configured bar. Streaming bypasses post-generation verification. BYOK is supported on every tier.

For coding agents, Nadir Auto is a terminal launcher for Claude Code or Codex that asks Nadir for the main-session model before each turn, with your configured model as the fallback and cost ceiling. Delegation integrations for Claude Code, Codex, or Cursor instead recommend a model tier for subagent work, and the agent decides; the Claude plugin can also move a main-thread turn to Haiku when Nadir's cache check prices it cheaper, unless its switch_main_model option is off. Either way inference runs on your own provider account, so Nadir holds no provider keys on that path.

What the numbers are, and what they are not

Nadir publishes each evaluation with its scope. On checkable code, run-check-escalate solved 392 of 395 common HumanEval and MBPP problems (99.2%), graded by running the canonical tests; that applies only to tasks with runnable deterministic tests. Nadir-Tumbler posts an arena_score of 72.3 on RouterArena's public scorer, 5th of 23 routers, which measures the routing decision on RouterArena's own model pool. A reference-assisted RouterBench evaluation over 11,420 held-out triples produced a 60% lower projected cost than always-Opus with about 98% retained quality and a 1.7% catastrophic-route rate. That experiment gave the verifier the expensive-model reference answer, which production does not have, so it is a research ceiling and not the deployed path.

None of these is a production guarantee, a universal savings rate, or a forecast for any particular workload. Customer savings are reported from measured execution against a declared baseline, and customer quality only from outcome-labelled traffic. Projected savings and realized savings are separate artifacts and are never blended.

Design-partner program

Three rungs, picked by risk appetite. Rung 0 Shadow runs advisory decision calls alongside live traffic and returns a projected receipt, with nothing in the request path changed. Rung 1 Hosted is the two-line swap on a production slice and returns a realized receipt. Rung 2 On-prem is a supervised six-week proof of concept inside the partner's VPC, where no prompt, response, or usage reaches Nadir. The commitments are the same at every rung. Apply for a rung directly: Rung 0 Shadow, Rung 1 Hosted, or Rung 2 On-prem. Not sure which fits? Start at getnadir.com/contact/?reason=design-partner.

Licensing

NadirClaw is the self-hosted core, source-available under the PolyForm Noncommercial License. Source-available is the correct label; NadirClaw is not open source. Nadir Route's hosted plan has no base fee and charges a variable fee only on measured savings from requests Nadir executed.

Pages on this site

Machine-readable summaries of this site: llms.txt and llms-full.txt.