DEV Community

Enjoy Kumawat
Enjoy Kumawat

Posted on

My AI Agent Writes Great Code and Forgets All of It by Tomorrow

Here's a conversation I had with my coding agent three times in one week:

Me: The DEV.to API 403s us.
Agent: Let me add a User-Agent header — their bot filter rejects the default one.
Me: Right. We figured that out on Monday. And Tuesday.

The agent was correct every time. It was also starting from zero every time. Monday's hard-won lesson — dev.to blocks the default user agent — evaporated the moment the session closed. Wednesday-me paid the same debugging tax as Monday-me.

This is the part of "agents write code, but they don't remember" that actually hurts. It's not that the model is dumb. Within a session it's sharp. The problem is that every session is session one. All the context you built — why a function is shaped weirdly, which approach you already tried and rejected, the username that has an underscore in one place and not another — is gone. You're not pair-programming with a senior dev. You're onboarding a brilliant amnesiac, daily.

Why "just put it in the prompt" doesn't scale

The obvious fix is to dump everything into your instructions file (CLAUDE.md, AGENTS.md, whatever your tool reads). I tried. It rots fast:

  • It becomes a 600-line wall the model skims and ignores.
  • It mixes stable facts ("the token lives in .env") with episodic ones ("on the 23rd I tried X and it failed").
  • Nobody prunes it, so wrong facts linger and actively mislead.

A single flat file is a junk drawer. What I actually wanted was a small, typed memory — different kinds of knowledge in different places, each with a rule for when to read and when to write.

The fix: four files and four protocols

I gave the project a docs/project_notes/ directory. Four files, each one job:

docs/project_notes/
├── bugs.md        # known bugs → their solutions
├── decisions.md   # why things are built the way they are (mini-ADRs)
├── key_facts.md   # usernames, endpoints, file purposes, run commands
└── issues.md      # work log, newest first
Enter fullscreen mode Exit fullscreen mode

That's it. No database, no vector store, no embedding pipeline. Markdown the human and the model both read.

The trick isn't the files — it's wiring triggers into the instructions file so the agent knows when to consult and update them. The entire memory protocol is four lines in CLAUDE.md:

## Project Memory System
- Encountering an error → search `bugs.md` first
- Proposing an architecture change → check `decisions.md` for conflicts
- Need a username/endpoint/command → check `key_facts.md`
- Completing a phase of work → log it in `issues.md`
Enter fullscreen mode Exit fullscreen mode

Now the DEV.to 403 lives in bugs.md once, as a fact, not a rediscovery:

### dev.to API returns 403 on every request
**Cause:** dev.to rejects the default HTTP-client User-Agent (bot filter).
**Fix:** send `User-Agent: Mozilla/5.0`. Applies to all /api/articles calls.
Enter fullscreen mode Exit fullscreen mode

And the gotcha that bit me twice — same person, two usernames — lives in key_facts.md:

## Usernames
- GitHub:  enjoykumawat      (no underscore)
- DEV.to:  enjoy_kumawat     (with underscore)
Enter fullscreen mode Exit fullscreen mode

The next time the agent reaches for a username, it reads the fact instead of guessing and getting it half-right.

The rule that makes it actually work: write-on-completion

Reading is easy. The discipline is writing. A memory system only compounds if knowledge flows back in. So the single most important protocol is the last one: when you finish a chunk of work, log it. My issues.md is append-only, newest first:

### 2026-06-23 - DEV.to publisher + 403 fix
- Status: Completed
- Built reusable stdlib publisher. Root cause of the
  intermittent 403 was the default User-Agent. Fixed with
  a Mozilla UA + H1-strip on the markdown body.
Enter fullscreen mode Exit fullscreen mode

That one entry means future-me (and future-agent) gets the outcome and the reason for free. The work log is the difference between "we have notes" and "we have memory."

What changed in practice

  • No more re-debugging. Solved problems stay solved. The 403 conversation hasn't happened a fourth time.
  • Decisions stick. When I'm tempted to re-architect something, decisions.md reminds me why it's that way — usually because I already tried the "better" idea and it broke.
  • Onboarding cost dropped to near zero. A fresh session reads four short files and is roughly as caught-up as I am.

Keep it small or it rots

Two failure modes to avoid, both learned the hard way:

  1. Don't log what the code or git already says. "Renamed x to y" is in the diff. Memory is for the non-obvious: the why, the dead end, the gotcha. If git can answer it, don't write it.
  2. Prune wrong facts immediately. A stale fact is worse than no fact — the agent trusts it. When decisions.md no longer reflects reality, the fix is a delete, not an append.

The whole system is four markdown files and four lines of protocol. No framework. The insight isn't technical — it's that an agent's memory has to live outside the agent, in artifacts that survive the session, with explicit rules for when to read and write them.

Your agent doesn't need a bigger context window. It needs a place to write things down — and a habit of reading them back.

Top comments (1)

Collapse
 
max_quimby profile image
Max Quimby

The write-on-completion rule is the part everyone skips, and it's exactly the part that makes this compound — totally agree. One thing that's saved us once these files grow: a promotion step. Episodic notes start in the work log, but the moment the same lesson shows up twice (your 403 is the perfect example), it gets promoted out of issues.md into bugs.md/key_facts.md as a durable fact, and the episodic mention gets trimmed. That keeps the "always read first" files small enough that the model actually reads them instead of skimming a wall.

The rot you flag is the real long-term enemy, though — have you hit the point where an entry goes stale rather than just redundant? We had a "fix" in bugs.md that was correct for an old API version and then actively misled the agent after the API changed. We ended up stamping each fact with the date/commit it was last verified, so anything older than the code it describes gets re-checked instead of blindly trusted. Curious whether you prune on a cadence or only when something bites you.