Why AI coding agents lose context
The mechanics of context loss in long agent sessions — what gets evicted, why compaction is lossy, and what actually survives a session boundary.
You are four hours into a session. The agent has been productive. Then it deletes
the retry wrapper you spent twenty minutes talking it into writing, replaces it
with a naive fetch, and explains — confidently, politely — that the retry logic
was redundant because the SDK already handles retries. It does not. You told it
that at 11am. It is now 3pm, and the agent has no idea that conversation happened.
This is the single most common failure mode in long agent sessions, and it gets misdiagnosed constantly. People call it "the model getting dumber," or blame temperature, or assume a silent model swap. It is none of those. It is context loss, and it has specific mechanics worth understanding, because the mitigations follow directly from the mechanics.
What actually happens when a context window fills up
An agent session is a growing transcript. Every user message, every tool call, every file the agent read, every 400-line test output, every stack trace — all of it accumulates in a single linear buffer that gets re-sent to the model on each turn. The model has no memory between turns. The transcript is the memory.
That buffer has a hard ceiling. When you approach it, something has to go. There are only three strategies, and every agent harness uses some blend of them:
Truncation. Drop the oldest turns. Simple, fast, and catastrophic for intent, because the oldest turns are where you explained what you were trying to do and why. The transcript keeps the last two hours of mechanical edits and throws away the twenty minutes of reasoning that made those edits correct.
Compaction. Summarize the older turns into a shorter block and continue from there. Better than truncation, but summarization is lossy by construction — that is the entire point of summarization. Something has to be decided as unimportant.
Retrieval. Keep the full transcript on disk, and pull relevant chunks back in on demand. This decouples stored from present, which is the right direction, but it introduces a new failure: the agent has to know it needs to look something up. An agent that has forgotten a decision does not know to search for it.
Most production harnesses today do compaction with some retrieval. Which means the interesting question is: what does a summarizer decide to drop?
Compaction drops rationale before it drops code
Summarization optimizes for reconstructing state, not history. Ask any model to compress a long engineering transcript and it will reliably preserve:
- Which files were modified
- What the current task is
- Errors that are still unresolved
- The general shape of the code that now exists
And it will reliably drop:
- Why an approach was rejected
- Constraints you stated once, in passing, in prose
- The fact that a particular ugly-looking line is deliberate
- Options that were considered and discarded
This asymmetry is not a bug in the summarizer. It is what "summarize" means. A summary of a four-hour session that faithfully retained every discarded alternative and every stated constraint would not be a summary; it would be the transcript. The compression has to come from somewhere, and rationale is the cheapest-looking thing to cut because it does not appear in the current state of the code.
But rationale is exactly what you cannot reconstruct by looking at the code.
Consider a line like this:
// deliberately not Promise.all — the upstream API rate-limits per-connection
for (const id of ids) {
results.push(await fetchRecord(id));
}
Strip the comment and this is a textbook performance bug. An agent that encounters it post-compaction, with no memory of the rate-limit discussion, will "fix" it. It will be right about the general pattern and wrong about this codebase. And it will be confident, because from where it is sitting, this is an obvious sequential-await antipattern with a thousand training examples telling it what to do.
This is the deeper point: context loss does not produce hesitation. It produces plausible, fluent, well-argued wrongness. The agent does not know what it has forgotten, so it cannot flag uncertainty about it.
Knowing and retrieving are different problems
There is a distinction worth making precise, because conflating the two leads people to the wrong fix.
What the agent knows is what is currently in the context window. It can reason over this directly and reliably.
What the agent can retrieve is everything reachable through a tool call — files on disk, git history, a search index, an issue tracker.
Retrievable is strictly larger than known, and the gap is where most surprises live. Retrievability is worthless without a trigger. The agent has to (a) realize a fact might exist, (b) guess where it lives, and (c) decide the lookup is worth a turn. A decision that lives only in a chat transcript from Tuesday fails all three tests. It is not in the repo, so nothing prompts a search; there is no obvious place to look; and the agent has no reason to suspect it is missing anything.
The practical implication is blunt. Anything you need the agent to honor across a session boundary has to exist as a durable, discoverable artifact in the repository. Not in the chat. Not in your head. In a file the agent will plausibly open, or better, in a file something forces it to open.
Session boundaries destroy intent completely
Compaction degrades context gradually. A new session zeroes it. When you close the terminal and start fresh tomorrow, everything not written down is gone — every constraint you stated, every alternative you rejected, every "no, not like that, because."
What survives is the diff. And here is the thing engineers consistently underestimate: reading the diff is not the same as remembering the decision.
A diff tells you what the code became. It does not tell you:
- What the code almost became instead
- Which of the three plausible designs was chosen, and on what grounds
- Which parts are load-bearing versus incidental
- Which constraints were external (an API's behavior, a compliance rule) and thus non-negotiable, versus internal preferences that are fine to revisit
An agent re-reading a diff reconstructs the what with high fidelity and invents the why from priors. Usually the invented why is reasonable. Occasionally it is exactly backwards, and it is confidently backwards, and now it is refactoring against a constraint that no longer exists anywhere it can see.
Symptoms you can actually observe
Context loss has a recognizable signature. If you see these, stop and checkpoint rather than pushing through:
The agent re-solves a solved problem. It hits an error it already fixed an hour ago, and works through the same diagnostic path from scratch. Cheap in tokens, expensive in trust, and a reliable early warning.
The agent contradicts an earlier decision. You settled on cursor pagination. It writes offset pagination in the next module. Both are defensible in isolation; the inconsistency is the tell.
The agent "fixes" something deliberate. The sequential-await case above. Any time it removes something and calls it redundant, dead, or unnecessary, ask whether it has the context to know that.
Suggestions get more generic. Early in a session, advice is specific to your codebase — it references your actual helpers and conventions. Late in a session, it drifts toward textbook patterns that could apply to any repo. That drift is the codebase-specific context aging out.
It asks you something you already answered. The most benign symptom, and the most useful, because it is unambiguous. Answer it, then treat it as a signal that everything else from that era of the transcript is also gone.
It reintroduces a bug you already fixed. The nastiest one, because tests often do not catch it — if the bug was subtle enough to need a conversation, it was probably subtle enough to lack a regression test.
What actually helps
None of these are exotic. They are all variations on one idea: move state out of the transcript and into the filesystem.
Write decisions down where the agent will trip over them
A decision record does not need to be ceremonial. Three lines in a file the agent reads anyway beats a beautiful ADR nobody opens:
## Pagination
Cursor-based, not offset. Offset breaks under concurrent inserts and we have a
high-write table here. Do not "simplify" to offset.
The "do not" line matters more than it looks. It converts a fact the agent might override into an instruction it must actively disobey. Models are much better at respecting explicit prohibitions than at inferring that an unusual choice was intentional.
Put these where they will be loaded: CLAUDE.md, AGENTS.md, a docs/decisions/
directory referenced from the agent instructions, or a comment right at the site
of the surprising code. Proximity beats organization. A comment on the weird line
survives every compaction strategy, because the agent has to read that line to
edit it.
Scope work to what fits in one context window
The most effective mitigation is also the least satisfying: do less per session.
If a task requires four hours of accumulated context to complete correctly, it is too big. Split it until each piece fits comfortably in a single session with room to spare — you want to finish before compaction ever triggers, not right after. Vertical slices work far better than horizontal ones here. "Add the field end-to-end through schema, API, and UI" holds together as a self-contained unit of context. "Add all the fields to the schema, then all the endpoints, then all the UI" requires carrying schema decisions across three sessions.
This is the same structural insight behind the 70% problem — small, complete, verifiable units fail in ways you can see and fix, and they fail before you have sunk four hours into them.
Checkpoint before compaction, not after
Most harnesses signal when they are about to compact. That signal is your cue to write, not to keep going. Before compaction, ask for a durable summary aimed at disk rather than at the transcript:
Before we continue: append to NOTES.md the decisions we made this session,
each with its rationale and any constraint that drove it. Include things we
rejected and why. Be specific about anything that looks wrong but is deliberate.
The output goes in a file. The file survives compaction, survives the session boundary, and — critically — gets re-read at the start of the next session. You have converted volatile context into durable context at the exact moment before it would have been lost.
Do this before the compaction warning if you can. A summary generated from the full transcript is meaningfully better than one generated from an already-compacted transcript, which is a summary of a summary.
Make constraints testable
The strongest form of a durable constraint is one that fails a build. A comment saying "must stay under 200ms" is a suggestion. A test asserting it is a wall. Anything you truly cannot afford to have silently reverted belongs in the test suite, not in prose — because the test runs on every session, forever, with no dependence on whether anyone remembered to read the docs. Getting this right is its own discipline, covered in acceptance criteria that actually work.
Start sessions by loading the relevant state
Ending a session well is half of it. Beginning one well is the other half. A session that starts with "continue where we left off" starts with nothing. A session that starts with "read NOTES.md and the spec for this feature, then summarize the constraints back to me before writing any code" starts with the context reconstructed and — because you asked for it back — verified.
That read-back step catches a surprising amount. If the agent's summary of your constraints is subtly wrong, you find out in ten seconds instead of after ninety minutes of work built on the misreading.
Bigger context windows do not fix this
The obvious objection: context windows keep growing. Won't this problem just go away?
Partially, and less than you would hope.
Bigger windows push the compaction boundary out, which genuinely helps — a task that used to require compaction now fits. That is real progress and it is worth having.
But three things do not improve with window size.
Attention is not uniform across a long context. Models retrieve from the beginning and end of a long context more reliably than from the middle. Something technically present but buried 200k tokens back is not equally available to the model as something stated in the last message. It is in the window; it is not equally in effect. Larger windows have not eliminated this.
Sessions end. No context window spans Monday to Thursday. No context window spans two engineers. No context window survives your laptop rebooting. Session boundaries are architectural, not capacity-limited, and they are permanent.
Bigger windows encourage bigger sessions. Capacity gets consumed by ambition. A 1M-token window does not mean you run the same task with more headroom; it means you attempt a task that fills 1M tokens. The ratio of signal to noise in the transcript stays roughly constant, and the failure mode reappears at a larger scale, now with four hours of sunk cost attached.
The durable fix is not a larger buffer. It is not keeping the important things in the buffer at all. Written artifacts — specs, decision records, tests, comments at the point of surprise — are the only form of context that is immune to compaction, immune to session boundaries, and shareable between agents and humans. They were good practice before agents existed. Agents just made the cost of skipping them immediate and visible instead of deferred and diffuse.
The agent that deleted your retry wrapper was not being careless. It was doing exactly what you would do if you walked into a codebase, saw code that looked redundant, and had no way to find out otherwise. The fix is not a smarter agent. It is leaving a note.
Keep reading
- How to write specs for coding agentsA practical guide to specs an LLM can execute — non-goals, verifiable criteria, named interfaces, and decisions with rationale that survive review.
- ZeNorm vs. AWS KiroKiro closes the loop inside its own IDE with click-through approvals. ZeNorm gates on a score and hands off to your agent. A mechanism comparison, honestly.
- ZeNorm vs. GitHub Spec KitSpec Kit is free MIT-licensed prompt templates run by your own agent. ZeNorm runs a server that refuses. A mechanism comparison, and when Spec Kit wins.