AI Agent Forgets Context Between Sessions — Why & the Fix
Your AI agent forgets everything between sessions because working memory dies at the session boundary. The three-case test for what to write down — and what not to.
Search “how to keep session context in VS Code” and you’ll find tmux, Settings Sync, and persistent terminals. None of them solve the problem you actually have, because your problem is not the IDE — it’s the agent. An AI agent’s working memory dies at the session boundary. Open a new conversation tomorrow and the agent knows nothing about yesterday’s decisions, today’s blockers, or last week’s failed approaches it should not repeat.
Most teams answer with a PROGRESS.md. Most teams write too much in it. We tested. On a refactor where every decision was code-expressible — a new helper, a new pattern — the run with no PROGRESS.md was cheaper on every metric. The next session simply opened the file and saw the helper. On a different task — a backward-compatibility shim marked “remove after migration” — removing PROGRESS.md quietly broke things. The shim looked permanent in the code; without the note, the next session left it as dead weight. The rule is simple. Write down only what the code cannot express. This page shows you the three-case test, the marathon-vs-split math, and what Anthropic calls Context Anxiety — and why splitting too early makes it worse.
“Session persistence” is a VS Code problem. Yours isn’t.
The internet treats “session persistence” as a Settings Sync problem — keep your themes, extensions, and open terminals across machines. For an AI agent the problem is somewhere else entirely.
An agent’s working memory — the running mental model it built of your task: the decisions it made, the dead ends it ruled out, the constraints it inferred — lives only inside the conversation. When the session ends, that memory is gone. There is no Settings Sync for working memory. tmux will keep your shell alive; it will not tell tomorrow’s agent why you rejected the first three approaches. The IDE persists your environment. Nothing persists the agent’s understanding. That gap is layer 5 of the harness — cross-session context — and it is the one that makes a capable agent repeat last week’s mistakes with full confidence.
- What VS Code persists. Themes, extensions, keybindings, open files, terminal panes. Your environment. Synced across machines, restored on reopen.
- What nothing persists. The agent’s working model of the task — decisions, rejected approaches, inferred constraints. It dies at the session boundary and is silently re-derived from scratch.
The hidden cost: Recovery Cost
Every new session pays a tax before it does any useful work: the tokens it burns rebuilding the mental model the last session already had. We call it Recovery Cost — and it’s invisible until you measure it.
With a clean handoff artifact — a short note that says where things stand and why — Recovery Cost is near zero. The new session reads the note, reads the code, and resumes. Without one, the agent re-reads files until it has reconstructed enough to feel oriented. Often it never fully does. It burns through the context budget re-deriving what it could have been handed, and then — running low on room — it makes simpler, more conservative decisions than the task actually warrants. Anthropic has a name for this failure mode: Context Anxiety. An agent that senses its context filling up starts cutting corners, summarizing prematurely, and avoiding the deep reasoning the work needs. A bad handoff doesn’t just cost tokens; it lowers the quality of every decision the next session makes.
The counterintuitive part. The fix for Context Anxiety is not “write more down.” Over-stuffed handoff notes are themselves context the next session must read, reconcile against the code, and partly distrust. The goal is the smallest artifact that eliminates re-derivation — not the largest.
The contrarian rule: don’t write a PROGRESS.md for everything
The reflex is to dump everything into a running log “so the next session has context.” That reflex is wrong. Most of what you’d write is already carried forward by something more reliable than prose: the code itself.
Code-expressible state flows forward on its own. If you added a helper, the helper is in the file — the next session sees it and uses it. If you established a pattern, the pattern is visible wherever it’s applied. If you finished a refactor, the new shape is the state. Writing “I added a parseConfig() helper” into PROGRESS.md is duplication: you’re maintaining two copies of the same fact, and the prose copy is the one that goes stale. Write down only what the code cannot express:
- Temporary shims. “This compatibility shim is intentional — remove after the v3 migration.” The code looks permanent; the intent isn’t in it.
- Non-obvious choices. “We use a busy-wait here, not async, because the SDK deadlocks under the event loop.” The code shows what; only the note shows why.
- Decisions without a test. “Rejected the cache-first approach — it broke under concurrent writes.” Nothing in the codebase records the road not taken.
- Open blockers. “Waiting on the upstream API fix before the retry path can ship.” A state that exists nowhere in the source tree.
The three-case test we use
Before you write a line into any handoff artifact, run it through three questions. They take five seconds and they keep your notes small enough that the next session actually reads them.
| Visible in the code? | Captured by a test? | Decision |
|---|---|---|
| No | No | Write it down. Nothing else carries it forward. |
| Yes | No | Don’t. The code already says it. |
| No | Yes | Don’t. The test pins it and fails loudly if it drifts. |
| Yes | Yes | Don’t. Doubly redundant. |
One question, really: is this decision recoverable from an artifact that can’t go stale? If it’s in the code, the code is the source of truth. If a test enforces it, the test is the source of truth — and unlike prose, a test fails when reality diverges from it. Only when both say “no” does the fact have nowhere to live but a note. Write that. Skip the rest.
Marathon vs split: when to start a new session
“Best practice” says break work into small focused sessions. On the economics, that advice is backwards for small tasks — because every handoff has a Recovery Cost.
On a small task, one long session is roughly 3× cheaper than three short ones. The single session keeps the whole mental model warm in context and never pays to rebuild it. Split it into three, and each new session re-reads the code, re-derives the state, and re-orients before doing anything useful — three Recovery Costs instead of zero. You don’t save money by splitting; you spend it three times.
The rule. Split only when the context window forces you to — when the task genuinely won’t fit — not because a blog post said short sessions are tidy. And when you must split, that’s exactly the moment the three-case test earns its keep: a tight handoff note makes the forced split cheap; a missing one makes it a fresh re-derivation; an over-stuffed one walks the next session straight into Context Anxiety. Splitting too early doesn’t reduce the problem — it multiplies the boundary where the problem lives.
- Before writing a handoff note, ask: is this visible in the code? Is it captured by a test?
- Write it down only when both answers are no — nothing else carries it forward.
- Do not write what the code already says, or what a test already pins.
- Write down only what the code cannot express: temporary shims, non-obvious choices, decisions without a test, open blockers.
- Split to a new session only when the context window forces it — not because short sessions look tidy.