Your agent's memory is a search engine. Code memory should be a neighbor.

You told your coding agent last week to use bun here, not npm. Today, in a fresh session, it runs npm again. Or it remembers everything you ever told it and sends all of it with every request, whether it's relevant or not.
Empryo's own repo shows how hard this gets. One busy file there has 46 memories attached. Every gotcha, every decision, every "never do this here again" eventually mentions it. When the agent edits that file, which three of the 46 belong in the model's limited context? Ranking by how often a memory was used picks the same three every time. Ranking by text similarity picks whichever ones share words with your prompt. Neither holds up in real work.
That problem only exists when memory is connected to a codebase. And that is the point of this post: Empryo's memory isn't a notes feature added to an agent. It is connected to the Genome, Empryo's live map of your files, their imports, and their git history, which the agent already uses to find its way around.
Everything below describes the memory system in the current beta, tested on Empryo's own repo.
The markdown file in the promptLink to this section
The most common memory system in coding agents isn't really a system. It is a markdown file, such as CLAUDE.md, AGENTS.md or a "project rules" file, added in full to the prompt on every turn.
That works when the file is ten lines long. Then it grows. Every rule you ever wrote goes along with every request, whether you're deep in auth code or adjusting a CSS margin. Nothing filters for relevance, so you pay tokens for all of it, every time. Attention is even scarcer than tokens: fifty stale rules are fifty things competing with the actual task.
And the file never ages. Nothing expires, nothing gets replaced, and nothing notices that the file a rule mentions was renamed two months ago. The file can't tell you which lines the agent ever used, so it only grows. Stale rules aren't an edge case; they're the normal state.
The other approach is retrieval, used by memory MCP servers, vector-store plugins, and chat assistant memory. Stored notes are compared with what you just said, and the closest matches are added to the prompt. That is better, because at least it filters. But it relies entirely on what you said. For a general assistant that makes sense, because what you said is all it has.
A coding agent has a better signal: what it's working on. When the agent edits auth/routes.ts, a memory that names auth/routes.ts is relevant by definition. No similarity guess is needed. The file path links the two, and general memory systems ignore it.
What you said is also a weak signal in coding sessions. A common case: you paste a 3 KB stack trace with the actual question in the last line. The pasted text overwhelms any similarity search. Empryo cleans this up before searching memory. It strips code blocks and looks for the last real question, or the last line that reads like prose. Similarity search over raw coding chats mostly matches noise.
Each memory is tied to filesLink to this section
Here is how it looks from your side.
You correct the agent once, "always use bun here, not npm", and the agent writes the correction down itself. Each memory has a one-line summary, the reasoning in its details, a category (pref, decision, gotcha, or context), and, most importantly, the list of files it is about. You don't file anything. Correcting the agent was the only work you did.
A week later, in a new session, the agent opens one of those files. The memory comes back on its own, before you ask, with a label that says why it appeared:
[gotcha] ba35cc60 — Config paths must be resolved from the repo root
· via file, sem 0.59 ↳ has detailsvia file, sem 0.59 is the explanation. This memory names the file being edited, and its meaning is close to the current task. Every memory that appears shows which signals picked it.
It works during a session too. When the agent is about to edit a file with a known pitfall, the warning appears right in the tool result, phrased as an instruction:
· gotcha "Always run migrations through the CLI, not by hand" [ad63a294]
— review before editingIt is also designed not to nag. A memory that has just been shown stays quiet for the next 10 turns. Each session has a limit on how many hints appear. And hints back off once the agent starts looking things up in memory by itself.
If a memory is important enough that you don't want to leave it to ranking, pin it. Pinned memories rank higher, skip the hint limit, and are never demoted or archived automatically. Pinning lets you overrule the statistics.
Finding memories by what the agent touchesLink to this section
When the agent works on a file, Empryo scores every candidate memory using several signals together. In rough order of how much I trust them:
The file itself. If a memory names a file the agent is editing right now, it gets a large boost. A memory about the exact file being changed should beat a vague text match every time.
How rare the file is. This is the answer to the 46-memory pile-up. A memory that is the only one naming a particular file ranks above one of the 46 that all name the busy file. Rarity only ever adds to the file boost. It never pushes a file match below a plain text match.
Files that change together. When you edit auth/routes.ts, Empryo also considers memories attached to files that have historically changed in the same commits, such as auth/middleware.ts. These count for less than a direct match, and only a few neighbors are included. The memory shows up before you touch the second file. Similarity search can't do this, because nothing in your prompt mentions the neighbor.
How widely a file is used. Memories about files that many other files import get a small boost. A pitfall in a file that 60 others depend on matters more than one in a file nothing imports.
Text still counts, through full-text search and a similarity check. But text is one signal among several, not the whole decision.
The similarity check is deliberately simple. By default it runs locally, with no model call, no API cost, and no network, so it works offline. It is rough, so weak similarity scores are treated as noise and given little weight. You can set an embedding model with the memory.embeddingModel setting if you want a better one. But the system is built so the strong signals never depend on the similarity check being good.
One more choice worth explaining: a memory's freshness is based on the last time the agent acted on it, not the last time it was shown. If showing a memory kept it fresh, a memory could stay on top forever just by being displayed. Being shown doesn't keep a memory fresh. Being useful does.
When the repo changes, memory followsLink to this section
Tying memories to files gives you something a rules file or a vector store can't: your repo's own changes tell the system what has gone stale.
Delete or rename a file, and the memories attached to it lose their file boost right away. The boost only applies to files that still exist in the Genome, so a memory pointing at a missing file simply stops winning. Nobody has to run a migration.
The dead reference doesn't hang around unseen either. Memory maintenance looks for memories whose files have all disappeared and flags them for the next cleanup. That catches the exact case a rules file can't: a note about src/api/client.ts still being followed two months after the file became src/api/http.ts.
Memory that notices it's being ignoredLink to this section
The difference between "shown" and "acted on" runs through the whole system, and I haven't seen it elsewhere.
Every memory counts both. If it keeps being shown and the agent never follows up, it gradually ranks lower. If the agent actually uses it, it gets a small boost. Ignore a memory long enough, six showings over at least 45 days with no follow-up, and it archives itself at the start of the next session. Archived memories are hidden from recall but never deleted, and you can restore them. A line in a markdown file has no way to know it was read 400 times and followed zero times. Here, that memory demotes itself and then retires.
Outdated knowledge doesn't linger either. When you write a new rule, the old one can be marked as replaced. It stops appearing in recall but stays in the history, so you can see what changed and when.
Context space is limited, so Empryo adds very little: the top 3 matches, and only their one-line summaries. The agent fetches the full details only when it needs them.
You stay in controlLink to this section
All of this happens automatically, but none of it is hidden. Run /memory to open a browser over your memories, where you can list, search, pin, delete, and restore any of them. It also shows how often memories were shown and how often the agent acted on them. When your memory has aged enough, Empryo suggests a cleanup and lists the oldest and least-used memories first. Pinned memories are never on those lists.
Memory has two scopes. Project memory belongs to the repo. Global memory follows you across repos, for preferences that are about you rather than the code. Both are searched, duplicates are merged so the same fact can't take two of the three slots, and project memory wins a tie because it is more specific.
Nothing is written behind your backLink to this section
By default, Empryo's memory holds only what was explicitly written, either by you or by the agent in front of you. Every write shows up in the conversation. Your chats aren't silently mined into a store you never see.
There is an optional auto-distill feature, because explicit writes alone miss things: a session solves something hard, nobody says "remember this," and the knowledge is lost. It is off by default (turn it on with memory.autoDistill). It saves at most 3 memories per session, only in certain categories, skips duplicates, and tags everything it writes auto-distilled so you can list or remove those memories all at once.
The short versionLink to this section
General memory systems ask: *what does this conversation sound like?*
Code memory should ask: *what is the agent working on, what changes with it in git, and did the last suggestion get used?*
The first question needs a good embedding model. The second needs your repo: its file graph, its git history, and the edits happening right now. A coding agent already has that data. Not using it wastes the strongest signal available.
It all runs locally. No vector database, no embedding service, no memory subscription. It just reads your repo.