Long-Horizon Context Management: Trimming, Summarization, Compaction, and Progressive Disclosure
The agent ran clean for forty steps. Then it re-ran a migration it had already finished, forgot the constraint the user stated at turn three, and quietly…

Key topics
The agent ran clean for forty steps. Then it re-ran a migration it had already finished, forgot the constraint the user stated at turn three, and quietly abandoned a half-finished subtask. Nothing crashed. The context window was still half empty. That is the trap: a large window makes append-only history feel safe right up until the run is long enough for the failure to surface.
Long-horizon context management is the discipline of keeping a bounded working set useful under continuous pressure. The window is not memory. It is a desk that keeps getting new paper stacked on it, and at some point the paper you need is buried under the paper you already read. The engineering job is deciding what leaves the active set, what gets compressed, what gets offloaded, and what must survive verbatim.
One invariant governs every decision here: every reduction operation must preserve provenance, unresolved work, recovery information, and active constraints. If a technique cannot name which of those it protects and how, it is not a policy. It is a gamble.
Why Append-Only Context Fails at Horizon
Append-only histories grow monotonically. Cost grows with them, latency grows with them, and the model's effective use of early tokens degrades as noise accumulates. You already know the write/select/compress/isolate vocabulary and the attention-budget argument. The long-horizon case adds a second-order effect: the problem is not that the window fills up, it is that the signal-to-noise ratio inside the window falls while the task is still running.
Two failure modes deserve names because they require different fixes.
Context saturation is raw history crowding out signal. Tool outputs, retries, verbose logs, and repeated boilerplate accumulate until the model spends its attention budget re-reading what it already processed. The window is not full, but the useful part of it is.
Semantic drift is subtler. Each lossy summarization pass rewrites the task a little. Summarize a summary, and the third generation describes a task that resembles the original but is not it. The agent is no longer confused about the past; it is confidently wrong about what the past meant.
The asymmetry that drives every design decision sits underneath both: compression is cheap and reversible only if you kept the original; loss is cheap and irreversible if you did not. That single sentence explains why offloading beats deletion, why summaries get versioned, and why the verbatim tail exists.
Three things get conflated constantly, and separating them is the first real move:
| Layer | Question it answers | Failure if confused |
|---|---|---|
| What the model sees | What is in the prompt right now? | You optimize the wrong budget |
| What the system persists | What survives across turns? | You lose state you thought you had |
| What the system can re-materialize | What can be fetched back on demand? | Offloaded detail becomes unreachable |
Trimming, summarization, compaction, and progressive disclosure are not interchangeable cleanup tricks. Each operates on a different layer, and each has a different failure mode.
The State You Must Not Lose
Before choosing a technique, write down what must survive it. Four categories carry the load.
Provenance is where a fact came from and how confident it is. Lose it and errors become unauditable: the agent asserts something, and no one can trace whether it read it, inferred it, or hallucinated it three compactions ago.
Unresolved work is open subtasks, pending decisions, and blocked branches. Lose it and the agent repeats completed work or silently abandons a thread. This is the failure that looks like laziness and is actually amnesia.
Recovery information is how to reconstruct or re-fetch what was dropped. Lose it and offloaded detail is gone in practice even though it exists on disk.
Active constraints are user instructions and environment facts that still bind future steps. Lose them and you get confident violations: the agent does exactly what it was told never to do, with full conviction.
Each category fails differently, which is why a single "summarize everything" pass cannot protect all four. A summary that preserves accomplished work may drop the constraint stated at turn three. A trim that protects recent evidence may delete the file path discovered at step five.
A minimal state schema makes the contract concrete:
{
"task_anchors": {
"goal": "stable, rarely changes",
"constraints": ["user-stated, binding until revoked"],
"success_criteria": "how we know we are done"
},
"long_term_memory": {
"summary": "evolvable, structured, versioned",
"version": 3,
"derived_from": "raw_history_range_0_120"
},
"short_term_working_memory": {
"recent_turns": "high fidelity, verbatim",
"protect_window_tokens": 40000
},
"offload_index": [
{"artifact_id": "tool_output_47", "summary": "test run output", "retrievable": true}
]
}
The decision rule follows from the schema: if a reduction step cannot name which of the four categories it preserves and how, do not ship it. This is the same discipline as any other lossy transform in a pipeline. You would not run a compression step in a data pipeline without knowing what it drops.
Knowledge check
Check your understanding
Answer this question before you continue.
A Trace: One Task Through Trim, Compact, and Recover
Abstract schemas convince nobody. Here is a single coding-agent run pushed through the full pipeline, so you can watch the contract hold or break.
Setup. The agent is migrating a service from a synchronous handler to an async queue. The user stated at turn three: "Do not touch the billing module — it is frozen for audit until Friday." By step 40, the run has produced a large test log, a diff, and a decision to switch from asyncio.Queue to a Redis-backed queue after a benchmark.
Pre-reduction state (step 40):
{
"goal": "migrate handler to async queue",
"constraints": ["do not touch billing module until Friday"],
"completed": ["handler refactor", "queue interface drafted"],
"open": ["wire Redis backend", "update integration tests"],
"artifacts": {
"diff_v3": "path/to/handler.diff",
"bench_log": "tool_output_47",
"decision": "Redis chosen over asyncio.Queue: 3x throughput at p99"
},
"provenance": {
"Redis decision": "bench_log, step 38",
"billing freeze": "user turn 3"
}
}
After trim (step 41). The protect window keeps the last 40k tokens of tool output. bench_log is older than that, so it is pruned from the prompt and written to the offload store. The index entry is added. The constraint survives because it lives in task_anchors, not in the pruned history. The diff survives because it is a pointer, not a body.
After compaction (step 55, stage boundary). The agent finishes wiring the Redis backend and compacts. The summary schema is applied:
{
"accomplished": ["handler refactor", "Redis backend wired"],
"in_progress": ["integration tests half-updated"],
"artifacts_touched": ["handler.diff", "queue.py", "test_integration.py"],
"next_steps": ["finish integration tests", "run full suite"],
"binding_constraints": ["do not touch billing module until Friday"],
"provenance": {"Redis decision": "bench_log, offload_index"}
}
Every field maps to a preservation category. accomplished and in_progress cover unresolved work. binding_constraints covers active constraints. provenance covers provenance. artifacts_touched plus the offload index covers recovery.
Recovery (step 62). A reviewer asks why Redis was chosen. The agent does not paraphrase from the summary. It looks up bench_log in the offload index, re-reads the raw output, and cites the p99 number. The summary pointed at the artifact; it did not replace it.
Now break it. Remove binding_constraints from the summary schema. At step 62, the agent edits a file in the billing module because nothing in its working set says not to. The window is not full. The policy fired on schedule. The contract failed on one field. That is what a preservation bug looks like in production — not a crash, a confident violation.
Trimming: Cheap, Blunt, and Often Enough
Trimming is the first and least destructive lever. It removes stale tool outputs, verbose logs, repeated boilerplate, and superseded observations. It does not remove reasoning, constraints, or open threads. The distinction matters because trimming is the operation most likely to be applied blindly.
The protect-window pattern is the workhorse: keep the most recent N tokens of tool output at full resolution, prune older output beyond a threshold. Coding assistants implement this directly. One documented implementation scans backward through tool calls, protects the last 40k tokens of tool output, and prunes beyond that threshold only when at least 20k tokens are prunable. The constants are tunable; the shape is not. Recent evidence stays exact, older evidence gets compressed or moved.
The critical move is offload instead of delete. Pruned artifacts go to a retrievable store with an index entry. Deletion becomes deferred access. This is what converts trimming from a lossy operation into a reversible one, and it is the difference between a policy and a gamble.
Trimming by recency alone deletes the constraint stated early and the file path discovered at step five. Recency is a proxy for relevance, not relevance itself.
That is the failure mode. A constraint stated at turn three is old by token position and permanently relevant by semantics. If your trimmer only knows position, it will eventually delete something load-bearing.
Trimming is the wrong tool when early evidence is load-bearing and rarely re-derived, or when the model cannot reliably re-query the offload store. If the agent has no working retrieval path back to pruned artifacts, you did not offload. You deleted with extra steps.
Knowledge check
Check your understanding
Answer this question before you continue.
Summarization and Compaction: What to Keep, What to Rewrite
Summarization is a compression operation on a segment. Compaction is a session-level handoff. They get used interchangeably and they are not the same thing.
Compaction replaces history with a structured summary plus a recent verbatim tail, then continues in a fresh session. Coding assistants ship this as both a manual command and a threshold-triggered automatic operation. The mechanics are consistent across implementations: take the conversation history, generate a summary with a dedicated prompt, build a new history from initial context plus recent messages plus the summary, and replace the session.
The summary schema is where drift is won or lost. A schema that resists repeated passes answers future decisions instead of narrating the past:
- Accomplished work — what is done, so it is not repeated
- Current in-progress state — what is mid-flight, so it is not abandoned
- Files and artifacts touched — what exists, so it can be re-read
- Next steps — what comes next, so the thread continues
- Binding user constraints — what still applies, so it is not violated
Notice that each field maps to one of the four preservation categories. That is not a coincidence. The schema is the preservation contract made executable.
Cumulative loss is the real enemy. Summarizing a summary compounds error, and after three passes the description of the task may no longer match the task. Three mitigations, in order of importance:
- Summarize from raw history when available. Do not feed a summary into a summarizer if the original is still on disk.
- Keep a verbatim tail. The most recent turns stay exact, so the model always has ground truth for the immediate present.
- Version summaries. A bad pass can be rolled back. Without versioning, a bad pass is permanent.
Trigger placement matters more than trigger threshold. Compacting mid-subtask is where agents go off the rails; users of these systems report exactly this. Compact at stage boundaries or after a completed unit of work. The threshold tells you when you can compact. The stage boundary tells you when you should.
One honest caveat: reported gains from learned, agent-triggered compression come from specific benchmarks and training setups. A system trained to fold its own context at multiple scales, or one trained to treat context management as a callable tool, can outperform static baselines under a bounded budget. Those results reflect their training data and task distribution. Do not assume the same behavior transfers to an untuned prompt-only pipeline. The mechanism is sound; the numbers are scoped to their setup.
Knowledge check
Check your understanding
Answer this question before you continue.
Progressive Disclosure: Pay for Context Only When It Is Used
Progressive disclosure is a load-time strategy, not a compression strategy. Trimming and compaction reduce historical state. Progressive disclosure controls which capabilities and documents enter the working set at all. Different bottleneck, different fix.
The mechanism: expose tool schemas, skill instructions, and document bodies on demand instead of paying for the full surface up front. The model requests what it needs; the system materializes it. A flat tool surface with lazy schema loading avoids re-paying every product's schema bundle on every call. As you connect more tools, the standing cost stays flat instead of growing with the surface.
The preservation contract still applies, just to a different layer. What must remain in the capability index is enough for the model to know a tool exists and what it does. What can be fetched later is the full schema, the parameter list, the document body. Get that boundary wrong and you get a specific failure: the model does not know a capability exists, so it never asks for it. The mitigation is a compact capability index or discovery hints — enough surface to request, not enough to drown.
The second benefit is caching. Assemble every prompt from layers ordered most stable to most volatile:
- Static system prompt — identical across every run
- Stable session context — organization, user, timezone, skill instructions
- Conversation history — grows, but earlier turns are immutable once recorded
- Turn-dependent context — current iteration's tool results and reasoning state
Across a long run, that ordering means the provider does not re-tokenize and re-process the stable prefix on every call. The stable prefix stays cacheable; only the volatile tail changes. The architectural consequence is that prompt assembly becomes a layered contract, not a string concatenation. The tradeoff is one retrieval round-trip and the discovery failure above.
Progressive disclosure reduces standing cost, not peak cost. A single step that pulls in five large documents still has to fit.
That boundary matters. Disclosure changes the shape of the cost curve across a run. It does not change the ceiling of any individual step.
Knowledge check
Check your understanding
Answer this question before you continue.
Choosing a Policy: Decision Boundaries and Combinations
Four techniques, four decision axes. Evaluate each reduction against:
- Reversibility of the loss — can you get it back?
- Cost of re-deriving — how expensive is it to reconstruct?
- Reuse frequency — how often will this be needed again?
- Stage boundaries — does the task have natural checkpoints?
The default composition for most long-horizon agents:
| Technique | Trigger | Protects |
|---|---|---|
| Trim tool output | Continuously, beyond protect window | Saturation |
| Offload instead of delete | At trim time | Recovery |
| Compact with structured schema | At stage boundaries | Drift, unresolved work |
| Disclose tools/documents lazily | At load time | Standing cost |
When trimming alone is sufficient: short-horizon tasks, small tool surfaces, or runs that finish inside a fraction of the window. Adding compaction here is complexity without payoff. The failure mode of over-engineering context management is that you introduce drift and recovery bugs into a system that never needed either.
When compaction is the wrong answer: tasks where exact intermediate artifacts matter. Precise diffs, exact error strings, numeric results. Offload and re-read beats paraphrase every time. A summary of a stack trace is not a stack trace.
When to escalate to learned or agent-triggered context management: only after a hand-tuned policy has a measured failure you can name. Learned folding requires training data and evaluation infrastructure. It is the right answer to a specific problem, not a default upgrade.
Evaluating Long-Horizon Context: Test the Contract, Not the Token Count
Token count is a proxy. The contract is the test. Build one scenario that forces reduction and resumes the task, then check the four categories directly.
The scenario. Take the migration task from the trace above. Record the expected values before reduction:
{
"expected": {
"goal": "migrate handler to async queue",
"constraints": ["do not touch billing module until Friday"],
"completed": ["handler refactor", "Redis backend wired"],
"open": ["finish integration tests"],
"artifact_pointers": ["handler.diff", "bench_log"],
"source_pointers": {"Redis decision": "bench_log"}
}
}
The protocol. Force a trim at step 41 and a compaction at step 55. Then resume at step 62 with a question that requires each field: ask why Redis was chosen (provenance), ask what is left to do (unresolved work), ask the agent to re-read the diff (recovery), and ask it to edit a file near the billing module (constraint). Compare the agent's answers against expected.
What each failure tells you:
| Symptom | Broken field | Fix |
|---|---|---|
| Agent re-runs completed work | completed not in summary | Add accomplished-work field |
| Agent violates a stated rule | constraints not in anchors | Move constraints out of history |
| Agent cannot cite a source | provenance dropped in compaction | Version summaries, keep source pointers |
| Agent cannot find a pruned artifact | Offload index missing or unreachable | Fix the retrieval path, not the summary |
Only after the contract test passes should you look at the context-load curve. A bounded band is healthy. Monotonic growth means the policy is not firing. Sawtooth collapse with quality loss means compaction is too aggressive — you are cutting muscle, not fat.
Report uncertainty honestly. Benchmark numbers from published context-management systems reflect their training and task distribution, not a guarantee for your pipeline. A system that reports strong results under a bounded budget on a specific benchmark is evidence that the approach works under those conditions. It is not evidence that your untuned pipeline will behave the same way.
The Next Move
Before adding any reduction technique, write down the four preservation categories and the trigger that fires each operation. Then instrument the current agent to log context size, offload index hits, and the four contract fields per step. Run the migration scenario above — or your own equivalent — long enough to force at least one trim and one compaction.
That single run will tell you more than any amount of architecture discussion. The contract test shows which field broke. The context-load curve shows whether your policy is firing. The repeated-work count shows whether unresolved work survived. If any of the four fails, you have found the exact reduction operation to fix — and you found it before it cost you a long run in production.
Knowledge check
Final check
Finish the article by checking the ideas you just learned.
References
Research updated Sep 11, 2026


