# AI Coding Agents With Project Memory: 3 Months and 500 Lessons Later

> Source: <https://dev.to/samplex_283d61d7a/ai-coding-agents-with-project-memory-3-months-and-500-lessons-later-48ie>
> Published: 2026-09-07 14:39:15+00:00

Back in June I wrote that [AI coding agents need project memory, not just bigger prompts](https://dev.to/samplex_283d61d7a/ai-coding-agents-need-project-memory-not-just-bigger-prompts-4pbd). The idea was simple. An agent makes a project-specific mistake, you correct it, and next session it makes the same mistake again, because your correction lived in a chat nobody re-reads. So I built a small graph of lessons: one sentence each, tied to the files and commands where they apply, committed with the repo, and shown to the agent at the exact moment it's about to need them.

Three months later, the graph on my own repo holds 542 lessons. Agents wrote most of them, while working on the code. They've been recalled more than five thousand times. I promised a follow-up, and this is it: what the memory learned to do in that time, what it costs to run, and what I'd tell you if you asked whether it's worth switching on.

**TL;DR:** Recall now puts the most specific rule first, understands a task from its wording and not only its keywords, speaks once and before the action, and can tell you which memories are actually earning their place. All of that at about ten tokens per recall. It's open source, MIT, and one command to set up.

Here's a real entry from my graph, captured by an agent after it broke a Windows build:

```
Never split('/') a filesystem path; use basename(). On Windows, path.join
produces backslashes, so split('/').pop() returns the whole path.
trigger: src/**/path*.ts
```

That's the whole format. One imperative sentence, a topic, and a trigger: a file glob, a command pattern, or a keyword. When an agent is about to edit a file that matches, the rule appears in its context. Not in the system prompt, not at session start, but right before the edit, when it can still change the outcome.

Capturing one takes a single line:

```
agentsmesh lessons add "Never split('/') a filesystem path; use basename()." \
  --topic windows-paths --trigger-file 'src/**/path*.ts'
```

In Claude Code, recall is a `PreToolUse` hook. In tools without hooks, the agent runs the query at task start. Either way it's the same file, so a lesson learned in one tool protects all the others. No embeddings, no vector store, no network call.

The improvement I'm happiest about is also the least visible. Ask the graph what applies to one file, and the rule written about that exact file comes first. Then rules about that kind of file. Then rules about the folder. Then the broad, repo-wide ones.

That sounds obvious. It's what you'd expect from any memory, and for the first two months mine didn't do it. It ranked by how many lessons shared a trigger, and since every trigger is unique, an exact path and `src/**` looked the same to it. Now the ranker measures how much of a path a trigger actually pins down, and that signal outweighs everything else combined. A rule that is precisely about the thing you're touching can't be outvoted by three rules that are vaguely about the subsystem.

Keyword triggers got the same care. They match whole words now, so a rule keyed on `art` stays quiet when you type "start". And when an agent proposes a command trigger after a failure, the pattern is anchored to the command, so a lesson about `rm -rf` doesn't fire on `pnpm run format`.

Small things. But a memory that answers with the wrong rule teaches an agent to ignore it. Precision is what makes it trusted.

Until this release, a conceptual lesson only surfaced if its keyword happened to appear in your prompt. Say the same thing in different words and it stayed silent. That's a real gap for rules like "verify visuals against the production build, the dev server serves stale styles", which no file glob can carry.

The newest version closes most of it without a model. When you submit a prompt, the task text is scored against the wording of every active rule, using the BM25 index the ranker already had. A rule joins the candidates when it shares at least two meaningful terms with what you wrote. At most three such rules come along, they rank below anything a trigger matched, and they're labelled, so you can see why they appeared.

It learned some manners on day one. "Top" and "left" are not a reason to bring up a rule about merge-conflict markers, so common English words can no longer qualify a match on their own. Latency stayed where it was, about 0.2 seconds for the whole recall process.

I did price embeddings before doing this. Roughly 100 MB of runtime, a 25 MB model, and a cold start of half a second or more, in a process that runs before every tool call. Then I counted the lessons in my graph that only meaning could reach. Eight. Lexical matching gets the rest for free, so that's where the effort went.

A memory that repeats itself gets tuned out. Two changes made recall quieter than the prompt it lives in.

First, session awareness. Once a rule has been shown, it stays quiet for the rest of the session. The engine listens for the harness's compaction signal, so when your context gets summarised and the rule falls out of it, it comes back. When it's still in context, it doesn't. In my logs, 61 percent of recalls end with "already shown, nothing to add", which is exactly right.

Second, timing. Recall now runs only *before* a tool call. The earlier scaffold also ran it after, which doubled the process spawns and delivered advice at the one moment it could no longer be applied. Halving the noise cost nothing.

The combined effect on cost surprised me. Averaged over every recall, including the silent ones, the memory adds about ten tokens per tool call. When it does have something to say, it's around 190 tokens, two or three rules. Compared with preloading the whole graph into the prompt, that's 18 times cheaper, and the rules arrive fresh instead of buried under everything else.

This is the part I underestimated in June. Storing lessons is easy. Knowing which ones still matter is the actual job.

The engine now keeps an opt-in log of what was recalled and what happened next, switched on with one line in `.agentsmesh/lessons/config.json`. On top of that log, `lessons validate` reports three things I check every couple of weeks: rules that have never fired, rules whose trigger is so broad they'd fire everywhere, and rules that were shown right before the same failure anyway.

I'll be honest about how the config switch came to be. My own instrumentation had been quiet for six weeks before I noticed, because the switch was an environment variable my shell could see and my editor's hooks could not. Moving it into the config file fixed that, and the lesson I captured from it, like any other, was broader than the bug: a hook is not your shell.

When the never-fired report first ran, it flagged 207 lessons, which looked like a graveyard. I read every one. Ten were actually dead: a test helper that no longer exists, an upstream bug that got fixed, a script that changed its exit code. The other 197 were live invariants about files that still exist. Windows path rules. Link-rewriting contracts. CI security. Quiet for a month is not the same as wrong, and I'm glad the tool made me read them instead of deleting them by score. A memory of invariants ages well.

A few things that make this usable beyond one person:

`lessons.json`, so two branches capturing lessons in parallel merge cleanly instead of conflicting on one big JSON file.`lessons add` refuses a pattern that would match nearly every command. An agent can capture freely without being able to flood recall.
| Lessons in my graph | 542, in 60 topics | 
| Recalls served since mid-July | 5,521 | 
| Tokens per recall, averaged over every call | ~10 | 
| Tokens when a rule is delivered | ~190 | 
| Recall latency | 0.2 s | 
| Versus preloading every rule | 18× cheaper | 

Three months, one repository, several agents, and the memory has stayed cheaper than a single paragraph of system prompt.

Recall is lexical, and I'm fine with that for now. Embeddings will come the day the number of lessons only meaning can reach grows past what a few hundred milliseconds are worth. The effectiveness signal is young; the log is filling now, and in a few weeks I'll be able to say "covered actions repeat less" as a number rather than a claim. And it's a CLI without a dashboard, which suits me, though I know it won't suit everyone.

```
brew install samplexbro/agentsmesh/agentsmesh   # or: npm i -D agentsmesh
agentsmesh init --lessons                       # graph, hooks and config
agentsmesh lessons add "Your first rule." --topic setup --trigger-file 'src/**/*.ts'
agentsmesh lessons query --file src/index.ts    # what an agent would see here
```

Add `"telemetry": true` to `.agentsmesh/lessons/config.json` if you want the numbers above for your own project.

Docs: [samplexbro.github.io/agentsmesh/guides/lessons](https://samplexbro.github.io/agentsmesh/guides/lessons/). Code: [github.com/sampleXbro/agentsmesh](https://github.com/sampleXbro/agentsmesh).

In June I ended by asking what your agents keep forgetting. Three months on, I'd love the sequel to that question: what's the one rule you'd want your agent to remember first? Tell me in the comments, and I'll show you the trigger that would carry it.
