Research & findings

The Write-Only Diary

The agent wrote to its memory six times for every time it read from it. A store filed into diligently and consulted almost never is a diary — and no instruction was going to fix that, because the gap was in the architecture.


4 June 2026·8 min read

Someone asks their assistant to go through the emails from a supplier they are in a dispute with, file the useful ones, and pull in the attachments. It does the job. Eighteen tool calls: search the mail, read two messages, fetch two attachments, list the vault, read two files, write two more, save some facts to memory, title the conversation, done.

It titled the conversation after the person.

So it knew who they were. It had a memory store holding months of context about that dispute — the correspondence, the drafts, the amounts, the history. And across those eighteen calls it did not once read from it. The only two memory operations in the whole run were writes.

That is not a surfacing bug and it is not a permissions problem. Reads were never suppressed, they have their own visible indicator in the interface, and the instruction telling the agent to consult memory early had been live since that morning. The store had the answer. The retrieval worked. Nothing was broken. The agent recognised the name, and then did not act on the recognition.

One session is an anecdote

So we counted. A scan of a few hundred of our own sessions — several thousand tool calls — and the shape came out unambiguous.

Writes outnumbered reads roughly six to one. Counting only genuine content recall, as opposed to a cheap "what's in here" orientation call, the ratio was closer to ten to one. Genuine recall appeared in about one session in nine. And of the sessions that wrote something to memory, most had never read from it first — filing into a store they had not consulted.

A memory that is written diligently and read almost never is a diary. Diaries are useful to the person who keeps them. They are not useful to the person who needs to know something.

The reads that did happen skewed hard toward sessions where somebody was testing memory on purpose. Organic recall, in the middle of real work, was rarer than the headline number suggests.

The reframe that changed the architecture

The obvious response is to write a better instruction. We did that first, and it is the cheap move, and the numbers above are from after it shipped.

Which forced a decision that had been sitting unmade. Recall is not a nice-to-have here; it is the product. An assistant that knows you is the whole claim. And we were resting that claim on the model choosing, each turn, to go and look — a behaviour that on our own measurements was not happening around nine times in ten.

You do not ship your headline capability on probabilistic compliance with an instruction.

So deterministic retrieval stopped being the fallback and became the architecture. The turn loop itself now always runs a retrieval step before the agent answers, so the invocation cannot be skipped. That is the obvious half. The other half is the interesting one, and we will come back to it.

Then the retrieval turned out to be broken in two opposite directions

Before wiring any of it, we probed the actual store to find out what a read would have returned.

The first probe produced a concerning result — semantic search confidently returning plausible, wrong material at low scores — and it was an artifact. We had pointed it at the wrong store, one with no knowledge of the subject at all, which forced it to return nearest-but-unrelated neighbours. Worth recording, because a retrieval system with nothing to find will always hand you its best wrong answer, and it is easy to mistake that for a general finding rather than a statement about an empty corpus.

The second probe, against the store that actually held the material, gave us the real picture.

Keyed lookup was brittle exactly where people are inconsistent. One individual was split across four separate keys: the bare first name, two different hyphenations, and the full name carrying an accent. Facts about one person, scattered across four entities that the store had no idea were the same person. Worse was the company: the obvious query for it normalised to a key that did not exist, and returned nothing — while more than twenty facts sat under three adjacent spellings of the same name. The most natural question anyone would ask returned zero from a store that was full of the answer.

Semantic search failed in the mirror image. It is robust to all of that — it found the right material for both the person and the company, at healthy scores, without caring how anything was spelled. Its weakness is that it always returns something. The confidently-wrong hits we had worried about in the first probe appear precisely when the answer is not stored. When the answer is present, it scores well clear of the noise floor.

Two retrieval methods that fail under opposite conditions is not a reason to pick the better one. It is an argument for running both and taking the union, which is what we did.

Why not just always look everything up

Because the cost of memory is not storage. Storage is nearly free and getting freer. The cost is what you carry into every single turn, and it is charged on every message for the rest of the product's life.

The naive way to fake a good memory is to re-inject the whole history each turn, which prices your assistant against the size of your life — the longer it knows you, the more every "morning" costs. Retrieving everything relevant on every turn is a softer version of the same mistake. It is affordable in a demo and never afterwards.

So the retrieval step is gated, and the gate is the part we spent the most time on.

A cheap, fast judgment step runs first and decides two things: whether this turn needs memory at all, and if so what to look for. It returns nothing for "hi", for "thanks", and for "what is the capital of France" — general knowledge with no personal hook. It returns targeted queries for "remind me how I like commit messages written". The gate is a model making a judgment rather than a rule matching nouns, because the set of things that might be a name in someone's life is unbounded and any keyword list is a guess about a person you have not met.

It also carries a running summary of the current subject — under twenty-five words — from turn to turn. That is what lets "did he reply yet?" resolve to the right person without re-reading the conversation, and it is a fixed cost regardless of how long the thread runs. Subjects drift mid-conversation, so a summary that updates beats a window of the last few messages.

Then the results are thresholded before anything reaches the agent, with the two halves treated differently: the keyed side is binary and needs no threshold, since an exact hit is either there or it is not, while the fuzzy side gets a floor that discards the confident-but-weak matches the first probe concerned us with.

Most turns never reach the expensive layers. That is the whole economic argument, and it is why an assistant with a real memory can be ordinary to run rather than a luxury.

The principle

The phrase we ended up with is deterministic invocation of a judgment step.

Not deterministic search, which would mean looking everything up every time and paying for it forever. Not agentic retrieval, which means asking the model to remember to remember — and which our own numbers say it will decline to do most of the time, while recognising the subject perfectly well.

The loop guarantees the step happens. The model decides what the step is worth. Reliability comes from the code; selectivity comes from the judgment; and neither is asked to do the other's job.

Recognition is not recall. We had built something that could identify a name, hold months of context about it, and never connect the two — and no amount of instructing it to try harder was going to close that gap, because the gap was in the architecture rather than in the prompt.

Still open

Canonicalisation is deliberately not in this pass: mapping the four spellings of a person onto one key, so the keyed half stops fragmenting. It is the obvious next thing and it is sequenced next rather than bolted on here, because collapsing entities is a decision about identity and getting it wrong merges two people who happen to share a first name.

And the two halves of retrieval still do not compose inside the store — there is no way to ask it a question that spans both. The caller stitches the union. That works, and it is a seam rather than a solution.


Postscript, August 2026. The shape held. Canonicalisation landed the following day and did what it was supposed to. The judgment-gate pattern turned out to generalise well beyond memory — the same structure, a guaranteed cheap decision step in front of an expensive one, is now how several other parts of the system decide whether to do work at all. The write:read ratio is no longer a number we watch, because reads are no longer optional.

One letter a month, when there is something worth saying.

What we shipped, what we learned, and the occasional thing that did not work. No drip sequence, no launch countdowns.

Unsubscribe any time. Or take the RSS feed instead.