What context compaction actually is
A model call is stateless. Every turn, the runtime re-sends the entire transcript: the system prompt, every user message, every assistant message, every tool call and every tool result. That transcript only grows. A single file read can be four thousand tokens. A failing test suite can be ten thousand. An agent thirty tool calls into a real task is carrying a transcript far larger than the task description that started it.
When the projected token count for the next call approaches the model's context window, the runtime has to shrink the transcript or stop. Compaction is the shrinking. The typical implementation preserves the system prompt and the most recent turns, then replaces everything older with a model-generated summary, sometimes with a hard truncation of oversized tool outputs first. The loop then continues from the compressed transcript as if nothing happened.
Implementations differ in the details. Claude Code compacts automatically as the window fills and exposes it as an explicit event. LangGraph gives you trimming and summarization nodes over message state, so the policy is yours to write. CrewAI and most hand-rolled loops default to a sliding window that simply drops the oldest messages. What they share is the important part: a lossy transform applied automatically, mid-task, to the agent's only working memory.
It is worth naming the mechanism precisely, because it explains the failure pattern. The summary is produced by a language model, under token pressure, optimizing for a coherent narrative that lets the run continue. Narrative is exactly what survives. Specifics are exactly what does not.
What compaction is not
It is not the context window limit. The window is a fixed ceiling on how many tokens one call can carry. Compaction is the runtime's response to approaching that ceiling. Confusing the two leads engineers to conclude that a larger window is a fix, when it only moves the point at which the same transform fires.
It is not memory or RAG. Retrieval is a deliberate pull: you decided in advance which facts were worth storing, embedded them, and the agent queries them when it knows to. Compaction is an involuntary push-out of working state you never chose to store. RAG protects what you anticipated. Compaction destroys what you did not anticipate, which is almost always the run-specific detail.
It is not a crash. There is no exception, no stack trace, no non-zero exit. The agent does not report that it has forgotten anything, because from inside the loop the compressed transcript is simply the transcript. This is the property that makes compaction expensive: the damage is silent at the moment it occurs and surfaces several turns later as behavior that looks like incompetence.
What actually gets lost
Refuted hypotheses are the expensive loss, and they are the first thing to go. A summary records what the agent decided and what it built. It rarely records what it eliminated: the approach that looked right and failed, the config flag that turned out to be a red herring, the library version that was not the cause. Negative results are high-value and low-salience, which is the worst possible combination for a summarizer. The practical consequence is an agent that re-enters a branch it already proved dead, spends the same tool calls, and reaches the same failure.
Exact identifiers go next. File paths, line numbers, function and symbol names, commit hashes, container IDs, port numbers, environment variable names. A summary says the agent updated the configuration. It does not say which of the four config files, at which key, with which prior value. The agent that reads that summary now has to re-derive the identifier, and re-derivation is where it guesses.
Raw tool output is compressed hardest because it is the bulkiest. A four hundred line test failure becomes the phrase tests failed. The stack frame that pointed at the real cause is not in the summary. Neither is the warning three lines above it that nobody read yet.
User corrections and constraints decay because they are stated once. Do not touch main. Keep the diff minimal. The service is on the loopback interface only. A constraint given at turn twelve is not repeated at turns thirteen through fifty, so by the time compaction runs it is a single low-salience sentence competing with fifty turns of activity for space in the summary.
Finally, the reasoning behind decisions. The agent tends to retain the decision and lose the argument that produced it. An agent holding a conclusion without its justification will re-open a settled question the moment new evidence looks superficially contrary, because it has nothing to weigh the new evidence against.
Failure modes you will recognize
The repeated fix. The agent applies a change, sees the same error, applies a cosmetic variation of the same change, and repeats. This is the refuted-hypothesis loss made visible. It is also the most expensive failure mode in wall-clock and token terms, because each cycle looks locally reasonable and nothing in the loop flags the repetition.
The lost edit set. A task touches five files. After compaction the agent knows it was refactoring something and can no longer enumerate which files it already changed. It re-edits one, misses another, and produces a half-applied change that compiles but is wrong. Version control usually catches this, which is why an agent that reads git status after compaction recovers faster than one that does not.
The violated constraint. Forty turns after being told not to modify a shared file, the agent modifies it. It is not disobedience. The instruction is no longer in the transcript in any form the model can act on.
The cold handoff. A run ends, the session is closed, and a new session starts with a one-line prompt. Everything the first session learned, including everything it ruled out, is in a transcript nobody will read. The second session pays the full discovery cost again, and often makes a different set of mistakes, which is worse than making the same ones.
The re-derivation storm. Immediately after compaction, an attentive agent re-reads the files it already read to rebuild its picture. This is rational behavior, and it consumes the freshly reclaimed context, which brings the next compaction closer. Left alone, a long run can spend most of its budget rebuilding the state it keeps losing.
How to tell your agent got compacted
The reliable signal is instrumentation, not intuition. Keep a monotonic turn counter alongside the message count in your loop state. Turn count only rises; message count rises with every turn until the runtime rewrites history, at which point it drops sharply. A message count that falls while the turn count rises is a compaction event, and it costs one integer comparison per turn to detect. Runtimes that compact explicitly usually also emit an event or a system message; log it and correlate it with what the agent does next.
The behavioral signals are softer but useful in review. Specificity collapses: the agent stops naming files and starts naming areas of the codebase. It asks for information you already provided. It issues a tool call with arguments identical to one from earlier in the run. It re-proposes a plan you already rejected. It announces that it will start by understanding the project, in the middle of a task it has been executing for an hour. Any of these appearing together after a long run is a compaction signature, not a model quality problem, and switching models will not change it.
Mitigations, ranked honestly
First: externalize state deliberately, before compaction, not after. Write a structured record to durable storage at checkpoints. The fields that earn their space are the goal in one sentence, the current state, the exact artifacts touched, what is proven with the evidence that proved it, what is refuted with the reason, the constraints the user gave, and the single next action. The refuted list is the highest-return field in the whole structure because it is the one thing no other mechanism recovers. Structure beats prose here: a summarizer will compress a paragraph, but an agent reading a labeled field knows what it is looking at.
Second: scope tasks so compaction never fires. This is the cheapest structural fix and the most often skipped. If a unit of work fits comfortably in one window, none of this applies. A task that requires two hundred tool calls is usually a task specification problem wearing a context problem as a disguise. Decomposition is not overhead; it is the difference between a run you can reason about and a run you can only watch.
Third: use sub-agents with fresh context for parallelizable and read-heavy work. Codebase search, dependency investigation, and review return a small conclusion from a large amount of reading. Push that reading into a subordinate context and keep the noise out of the main transcript. The honest caveat is that the subagent's own detail dies with it: you get its conclusion, not its trail, so it is a good fit for investigation and a poor fit for stateful multi-file edits where the trail is the deliverable.
Fourth: retrieval and memory stores, for reference facts. These are genuinely good at stable knowledge that outlives any single run: API shapes, schema definitions, project conventions, prior decisions you took the trouble to write down. They do not solve refuted-hypothesis loss. A refutation is an episodic fact about this run, and nothing writes it into the store unless you explicitly write it. There is also a bootstrapping problem: retrieval requires the agent to know it should query, and a compacted agent does not know what it forgot.
Fifth: larger context windows. They help. They do not eliminate the problem, for two reasons. Agents expand their work to fill the available window, so a larger budget mostly buys longer runs rather than safer ones. And recall over very long contexts degrades before the hard limit is reached, so detail buried in the middle of a large transcript is already unreliable while the runtime still reports plenty of room. Treat window size as a budget, not a guarantee.
Why writing it down first is the only mitigation that survives
Compaction is one member of a family. The same in-context state disappears when the process is killed by the OOM reaper, when a rate limit ends the run, when a deploy restarts the worker, when a quota resets, when the network drops, or when a human closes the laptop. None of these give the agent a turn in which to save its work. Everything held only in the transcript is volatile by definition, and the runtime is not obligated to warn you before it stops existing.
That is the whole argument for writing state out early. The only state that survives is state that was already outside the process when the process ended. A handoff written at the end of a run is a handoff that frequently never gets written, because the ending is what went wrong. Write at checkpoints instead: before a long or risky tool run, immediately after a hypothesis is refuted, when a constraint arrives from the user, and whenever the runtime signals that it is approaching the window. The cost is a few hundred tokens per checkpoint. The alternative cost is the entire run.
The Delx Continuity Capsule is one open format for this, with named fields for goal, evidence, refuted hypotheses, constraints and next action, designed so a fresh session can read it first and resume warm. It is worth saying plainly that any structured format works. A markdown file in the repo, a JSON blob in a key-value store, a comment on the ticket. What matters is that it lives outside the process, that it has a field for what was ruled out, and that writing it is a habit rather than an intention.
A checkpoint discipline that holds up
Make the next session read the record before it reads anything else, and make it verify rather than trust. A handoff is a hypothesis about the world, not a description of it. The first thing a resuming agent should do is check the record against reality: run git status, re-read the file the record claims was edited, re-run the test the record claims passes. Compaction can happen between writing the record and the state it describes; so can another agent, another process, or a human.
Then keep the record small enough to survive its own summarization. A handoff that is itself ten thousand tokens will be compacted along with everything else. Prefer identifiers over narrative, one line per refuted hypothesis, and links or paths to the raw evidence rather than the evidence inlined. The record is an index into durable artifacts, not a copy of them.
The last piece is cultural rather than technical. Treat every long-running agent as a process that will die without notice, because it will, and judge the design by what remains afterward. An agent that finishes the task is good. An agent whose successor can pick up in thirty seconds with the failed branches already marked is the one that scales past a single session.