The agent kept implementing the approach we already rejected because it never left the context window
We were changing a small CLI read path. The first pass explored adding an in-memory cache: the agent read the files, sketched the eviction policy, and laid out the tradeoffs. Then we redirected: skip the cache, just read from the source each time, the dataset is tiny.
The final diff still carried three things we never asked for. A cache-eviction helper sat in a utility file. Reads routed through a thin layer the cache had designed around. A test asserted a cache miss that no longer existed. The agent had accepted the redirect in the reply, but the rejected exploration had not left the window, so it kept treating every earlier proposal as a live requirement.
The tell was in the summary: the agent described the cache as "kept minimal for later" rather than removed. It remembered that the cache was discussed; it did not remember that the cache was killed.
Why a rejected branch survives the redirect
The context window stores proposals, files, and tradeoffs as a flat stream of text. It has no status column. Nothing in the loop marks an idea as accepted, deferred, or dropped, so a branch the user verbally rejected looks identical to one still open when the agent reaches for the next thing to write.
Agents accumulate requirements cumulatively. Each turn reads what came before and treats it as background that still applies; a later "no" changes the current instruction but does not erase the earlier exploration from memory. When the agent drafts code, it draws on everything in the window rather than re-deriving which parts are still active.
Verbally dropping an idea is especially weak. "Don't do the cache" removes it from the next sentence but leaves the cache's design, file paths, and helper names intact in the trace. The agent has no mechanism to delete that material, so it keeps surfacing as a plausible place to add a small, harmless-looking piece.
What ships when a dropped branch lingers
- Unrequested abstractions. A helper or routing layer built for the rejected approach survives because it still looks like useful scaffolding, even though nothing calls it.
- Tests for behavior that was killed. A cache-miss case, an eviction assertion, or a boundary the user removed gets written because the scenario is still present in the trace.
- Dead code that drifts. The orphaned helper is harmless today but gets edited in later passes, so it accumulates assumptions nobody agreed to.
- Reviewer confusion. A human reading the diff cannot tell whether the extra layer was requested; it looks like a deliberate design choice rather than a leftover branch.
- Scope creep that stays invisible. Each leftover piece is small, so the total overreach never reads as a feature, just as a few extra functions.
The pattern repeats across redirects. Every time you pivot away from an explored option without clearing it from the working notes, that option keeps contributing a small piece to the diff.
Keep an active list separate from dropped exploration
Treat the context window as memory without a status field, and give it one explicitly:
- Maintain a short active-constraint list. At the top of the working notes keep only what is currently required: read from source, no cache, no routing layer. Everything else lives below it.
- Mark dropped branches visibly. When an approach is rejected, write
DROPPED: in-memory cache — do not implementin the notes instead of only saying "no" in chat, so the agent sees the status rather than the bare proposal. - Restate the active goal before each turn. Before writing code, have the agent repeat the current constraints from the active list rather than re-scanning the whole trace for what matters.
- Gate new abstractions on the active list. A helper that does not map to a current requirement should not land, even if it feels like good scaffolding.
- Review the diff against the active list. After the pass, check every added function against what was actually requested; anything that traces back to a dropped branch gets removed before merge.
Present in the window is not approved
An idea that appears in the context trace is not the same as an idea the user accepted. Without an explicit status, the agent cannot tell a finished exploration from an open plan, and the rejected branch keeps shipping in small pieces. Mark what was killed, keep what is active in one short list, and let the diff prove it.