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 aUser-Agentheader — 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.mdreminds 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:
-
Don’t log what the code or git already says. “Renamed
xtoy” 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. -
Prune wrong facts immediately. A stale fact is worse than no fact — the agent trusts it. When
decisions.mdno 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.
답글 남기기