TL;DR
Long AI coding agent sessions rot the same way long meetings do: the further you get, the more the agent is working off stale, half-relevant context instead of what's actually true right now. After running an autonomous coding agent for months on real work, I landed on five habits that keep sessions coherent instead of watching them slowly drift. None of them are exotic — they're mostly about treating context as a budget, not a bottomless scratchpad.
The Problem
The first time I let an agent run unsupervised for a few hours, I came back to a mess. Not a crash — worse than that. The agent was still confidently working, still writing plausible-looking code, but it had drifted. It re-implemented a helper function that already existed three files over. It "fixed" a bug that had already been fixed twenty minutes earlier, undoing the fix in the process. It was still following the letter of my original instruction while having lost the plot on the actual goal.
The failure mode isn't the agent getting dumber. It's that the ratio of "signal from ten minutes ago" to "noise from two hours ago" keeps getting worse as a session runs long. Every tool call, every file read, every intermediate exploration adds tokens to the context window, and not all of those tokens stay useful. Eventually the model is reasoning from a context window that's mostly archaeological.
This matters more for autonomous agents than for a human pairing session, because there's no human in the loop to notice the drift early and say "wait, that's already done." The agent has to notice its own drift, and by default, it doesn't.
What made it worse was that the drift compounded. The re-implemented helper function didn't just waste time — it introduced a second, subtly different implementation that the next checkpoint then had to reconcile with the original. By the time I noticed, I wasn't debugging one mistake, I was untangling a small dependency graph of mistakes that had each seemed locally reasonable. That's the real cost of context rot: it's not one bad decision, it's a series of individually-plausible decisions that stop adding up to a coherent whole.
How I Solved It
1. Chunk work into checkpointed subtasks, not one giant instruction
The single biggest lever was refusing to hand the agent one large, open-ended goal ("migrate the auth system"). Instead I break it into an explicit task list up front, with each task small enough to verify independently:
1. Read current auth middleware, list all call sites
2. Write new token-validation module (no wiring yet)
3. Wire new module into one call site, run tests
4. Repeat wiring for remaining call sites, one at a time
5. Remove old middleware, run full suite
Each task gets marked done as it completes. This does two things: it gives the agent a cheap way to answer "what have I actually finished?" without re-deriving it from scratch, and it gives me a place to interrupt and redirect before three more hours of work compound on a wrong turn.
2. Force a self-summary at natural boundaries
Every time a checkpoint closes, I have the agent write a short structured summary before moving on — not for me, for itself:
## Checkpoint: token-validation module
- Done: new module at validators/token.ts, covers JWT + opaque tokens
- Not done: refresh-token rotation (deferred, ticketed separately)
- Gotcha: old middleware silently swallowed expired-token errors;
new module raises instead — call sites must catch explicitly
That "gotcha" line is the part that pays for itself. Without it, an agent three checkpoints later will happily assume the old error-swallowing behavior still holds, because the code that proves otherwise scrolled out of its effective attention even if it's technically still in the context window.
3. Re-read state from source, don't trust memory of it
Long sessions build up a kind of institutional memory in the transcript — "I already checked, that file doesn't have tests." Sometimes that memory is stale by the time it matters, especially if the agent (or a parallel process) touched the file since. Before any destructive or hard-to-reverse step, I make the agent re-read the current state from disk rather than reason from what it remembers observing an hour ago:
# Before deleting the old module, don't trust "I already confirmed
# nothing imports it" from 40 minutes ago — check again, now.
grep -rn "from '.*old-auth-middleware'" src/
It's a small amount of redundant work per checkpoint. It has saved me from at least one agent deleting code that a different task step had just started depending on.
The rule of thumb I use: if reversing the action would take more than a git revert, re-verify from source first. Renaming a variable, tweaking a comment — fine, trust the transcript. Deleting a file, dropping a database column, force-pushing a branch — re-check, every time, no matter how confident the summary two checkpoints ago sounded. Confidence in a transcript is not the same thing as correctness of the world it's describing.
4. Treat context window size as a hard budget, not a suggestion
I stopped thinking of the context window as "how much can fit" and started thinking of it as "how much can stay useful." Concretely: once a session's transcript has accumulated a lot of exploratory dead-ends (files read and rejected, approaches tried and abandoned), I have the agent compact its own working notes into a fresh, terse state file, then treat that file — not the sprawling history — as the source of truth going forward.
flowchart LR
A[Long session transcript] --> B{Getting noisy?}
B -- yes --> C[Compact into state.md]
C --> D[Continue from state.md]
B -- no --> D
This is the same instinct behind why good engineers write status-update comments in long PRs instead of expecting reviewers to replay the whole commit history in their head.
5. Know when to stop pushing a stale session and start fresh
The hardest one to operationalize, because it's a judgment call: sometimes the right move isn't a better checkpoint, it's ending the session and starting a new one with a clean, deliberately-written brief. I now treat this as a real decision point instead of something that only happens when a session literally errors out. Signs I use as a trigger: the agent re-asks something already answered, a checkpoint summary starts repeating itself, or a fix gets proposed for something already fixed. Any one of those is a tell that the marginal token in this session is worth less than the first token of a fresh one.
Starting fresh doesn't mean losing the work — the checkpoint summaries from step 2 are exactly what makes a clean handoff possible. A new session that opens by reading a tight, curated set of checkpoint notes gets a better starting context than an old session ten checkpoints deep, even though the old session technically "remembers more." That's the whole point: a well-written summary beats raw history, every time, because raw history makes the model do the work of figuring out what still matters.
Lessons Learned
- Context rot is silent. The agent doesn't announce "I'm now working from stale information" — it just keeps generating confident, plausible output. You have to build checkpoints that make drift visible, because the agent won't flag it on its own.
- Summaries the agent writes for itself are more valuable than summaries written for you. A "gotcha" line aimed at future-agent-in-this-session catches a different class of bug than a human-readable status update.
-
Re-verification is cheap insurance. A
grepbefore a delete costs seconds. An agent deleting something a parallel task step now depends on costs a lot more than seconds. - Bigger context windows delay the problem, they don't solve it. Even with a huge window, the ratio of relevant-to-irrelevant tokens still degrades over a long session. Compaction is a workflow habit, not something you can buy your way out of.
- "Start over" is a legitimate engineering decision, not a failure. Treating a fresh session as a deliberate tool — not a last resort — produced better outcomes than squeezing one more checkpoint out of an already-drifting session.
What's Next
I'm working on making the "getting noisy?" check in step 4 less of a gut call and more of something the agent can evaluate against concrete signals (repeated tool calls, checkpoint summaries that don't shrink, task list churn). If that turns into something reusable, I'll write it up.
I'm also curious whether the "start fresh" decision in step 5 can be automated the same way — right now it's me reading the transcript and getting a feeling that something's off, which doesn't scale if I ever want multiple long-running sessions going at once without watching each one closely.
Wrap-up / CTA
If you've hit context rot in your own long-running agent sessions, I'd like to hear what signals you use to catch it — drop a comment. And if this was useful, follow me here on Dev.to for more from-the-trenches notes on running AI coding agents on real work.
Top comments (0)