2026-07-10
A Learning Loop That Makes My Agent Wiser Over Time

My coding agent kept solving the same problems twice.
One session it would work out that a native confirm() dialog can’t be visually QA’d, so a test that leans on one proves nothing. Two days later, fresh context, it would write that exact test again. The same thing happened with the phone number that has to match when someone types it with a dash, and with the amber contrast rule a “matched” label kept failing. Each was a real lesson, learned once and dropped when the session ended.
You can write these down in a memory file. A flat file captures them, but nothing separates a durable lesson from a one-off, and nothing carries a note into a rule the agent actually follows.
Before building anything, I looked at how people solve this, from self-editing memory frameworks to research systems that evolve their own reasoning. All of it more machinery than one project needs.
So I built the smallest thing that would work, a loop to capture what the agent learns and promote the lessons that keep proving out into rules it follows. It’s plain markdown and one shell hook. Here’s the shape:
agent finishes work
└─ Stop hook nudges: "did you learn something durable?"
└─ capture → one entry in BANK.md (cheap, frequent)
└─ seen twice? → eligible
└─ groom → re-verify vs code → promote to a rule/skill/doc (or delete)
Capture, then curate
The skill has two operations, capture and groom, and each does a bit more than its name.
capture runs after a work session, prompted by a Stop hook (a shell script Claude Code runs when the agent finishes) that asks one question: did this produce a durable, team-shareable lesson? If it did, the agent appends one entry to BANK.md. It isn’t a dumb append. Three gates run first:
- Name the destination. Say where the lesson is headed, a rule or a skill or a doc, or it’s a note, not a learning.
- No duplicates. Grep those destinations so you don’t bank something a rule already says.
- No preferences. Personal preferences stay in your own memory, out of the shared bank.
A lesson seen a second time bumps a counter instead of adding a duplicate.
groom is the curation pass, run every so often. It only considers entries seen in two separate sessions, so a one-off fluke never graduates. For each one it re-checks the lesson against current code. The stale ones get deleted, never promoted. A gotcha that got fixed upstream would only mislead the next session. The survivors get routed to a durable home: a rule, a skill, a standards doc. That routing is the promotion. A lesson that keeps proving out stops being a note in the bank and becomes a rule the agent follows on every run.
You can run groom by hand when it occurs to you, or wire it to a schedule with cron or any scheduled-task mechanism instead of triggering it inline. Automating it is arguably the better call. The bank keeps draining without depending on you to remember.
Three things hold the line: seen twice before it counts, re-verified against live code before it graduates, and only two exits, promoted or deleted. That discipline is the part I care about.
What’s in the box
The whole thing is a handful of files and one hook registration:
| File | What it does |
|---|---|
.claude/hooks/check-learnings.sh |
Stop hook. One nudge per session: “did you learn something durable?” |
.claude/skills/boswell-learnings/SKILL.md |
The engine. The two operations, capture and groom. |
.claude/agents/boswell-learning-reflector.md |
Headless harvester. Proposes entries from a finished automated run, where no Stop hook fires. |
docs/learnings/BANK.md |
The live bank. Entries in transit. |
docs/learnings/INDEX.md |
A menu of the live entries. |
docs/learnings/archive/ |
Tombstones of promoted entries. |
BANK.md holds the full entries, each with a bit of frontmatter and a body. Here’s a real one, trimmed for length, the confirm() gotcha from the top of this post:
type: gotcha
context: boswell-web-qa, verifying destructive-action flows in the browser
promote-to: boswell-web-qa (QA proof rules)
Native browser dialogs (
window.confirm,window.alert) are not visually QA-able. They render outside the page DOM, so a screenshot can’t prove them and a JS stub may advance past one but can never stand in as proof it fired. Verify a confirm-gated action via the real CDP dialog event plus the DB/state outcome it produces, never via a stub that suppresses it.
The promote-to field is where the first capture gate makes you name the exit target, the durable home the lesson is headed for once it graduates. Mine points at a QA skill; yours will point at your own rules and skills.
INDEX.md is just a lookup table, a “when this bites you” cue for each entry linked to its spot in the bank. You scan the cues to decide whether to open the full entry, instead of loading all of them into context.
That index is the hand-maintained version of what a vector store does automatically. While the bank is small you can skip it and just read the entries; two files to keep in sync isn’t worth it yet. Once the list gets long enough that scanning it stops working, that’s your signal to move past a loop like this one.
Degrees of this
This is the simple end of a spectrum, on purpose. Here’s where it sits and when you’d reach for more.
This loop. Project-local markdown, lessons promoted into plain rule files. Reach for it when you have one repo, you want the memory git-versioned and readable, and you want zero new infrastructure. You can read the whole thing and delete what’s wrong by hand.
Letta / MemGPT. Self-managing memory: a vector-backed store the agent edits through tools, with an OS-style split between what’s in context and what’s in archival storage. Reach for it when the memory outgrows a hand-kept index, when you have a long-running assistant, or when you need cross-session recall at a scale you can’t eyeball.
ReasoningBank (Google, September 2025). Instead of storing facts, it distills reasoning strategies from both successes and failures, then retrieves the relevant ones at test time. Reach for it when you want the agent to get better at how it reasons, not just recall what it already knows.
Karpathy’s “system-prompt learning”. His proposed third learning paradigm, alongside pretraining and fine-tuning: the model refines its own operating notes from experience. It’s a direction, not a package you install. Reach for it when you’re comfortable letting the agent revise its own governing strategy.
Take it
The files are real, lifted straight out of Boswell, the nonprofit case-management app I run. This is the small, pragmatic version, sized for one project rather than a platform. They ship unaltered, so names and paths still say boswell and docs/standards, and the zip’s README spells out exactly what to change. Generalize before you use them.
The fastest way to adapt it is to point your own agent at the folder. Tell it to read every file, rename the boswell identifiers to your project, rewrite the promotion destinations to match your repo’s actual rules and skills, and register the Stop hook in your settings. Leave the mechanics alone: the two operations, the gates, the seen-twice governor, and the re-verify-before-promote step.
I picked the simple end and I’m in no hurry to leave it. Each lesson that graduates is one the agent won’t work out from scratch again, and the bank is still a file I can read in one sitting. That’s wiser and boring, which is exactly what I wanted.