agent beliefs need a compile step

aug 31, 2026

This is an essay about a mistake everyone is making with agent memory, thought through from first principles.

We keep building memory like a chatbot: store what was said, fetch something nearby later, hope the model is careful. That produces fluent recollection and wrong edits. The agent can retell the architecture and still patch the function you renamed last week.

The alternative is older than language models. Compilers already solved “a name that no longer points at anything.” They parse, they check kinds, they bind names to objects, they rebuild what changed, and they refuse when the binding is dead. Agent memory needs that pipeline. Not a better diary.

Grounding is the name of the operation: take a sentence the team believes and attach it to a thing in the repo that still exists. If that attachment cannot fail in the open, you do not have memory. You have literature.

Philosophy of language had this split before anyone embedded a paragraph.

Frege: sense versus reference; a description can be perfectly coherent and still pick out nothing.

Carnap: intension versus extension; the way of picking versus the set of things picked.

Kripke: some names are rigid. They are supposed to keep designating the same object across revisions, not float as whatever currently satisfies a description.

Agent memory is almost all intension. It keeps a definite description of the system (“auth lives in the session helper”) and treats the description as if it were a rigid designator. After a refactor the description still has sense. It has lost its object. The model does not notice, because continuation does not require denotation. It only requires the next plausible token.

There is a second split that makes this worse. Quine’s opaque contexts: you cannot freely substitute equals-for-equals inside “the agent believes that…” Two files can implement the same idea and the belief report still points at the wrong one. RAG substitutes on similarity. Compilers substitute on identity of binding. Those are not the same operation wearing different clothes.

So the failure is not amnesia. Amnesia would go quiet. What you get is a fluent unresolved reference, a Gettier case for tools. The answer is justified (it came from “memory”), it looks true (the sentence is well-formed), and it is only accidentally about the code that still exists.

Compilers were built to be rude about exactly this. An import is a de re commitment: this symbol, not “whatever currently seems like auth.” If the object is gone, the run does not start. Interpreters are de dicto. They evaluate the description until the crash is already downstream. Most memory products are de dicto.

Once you treat beliefs as a language that must compile, the steps stop being features and become obligations.


Parse turns an utterance into propositions that can have extensions.

“We use the App Router, server components by default, client components only when we have to” is one speech act and several claims. Leave it whole and you store a performance, then ask retrieval to do analysis later. That is the use/mention collapse: the file mentions an architecture. Grounding is the attempt to use it, to make the sentence stand in for an object in the tree.

Markdown is source. It is not an AST. Source can be literature. ASTs can be checked.

Typecheck is the admission that truth has kinds. Architecture, convention, contract, anecdote, these imply different commitments, and different illegal moves. A type is not a tag for the UI. It is a restriction on which derivations are allowed. Mix the kinds and the context is unsound in the ordinary sense: you can prove things you should not be able to prove, and the proof is a paragraph.

Type soundness, the boring pair: progress and preservation. Progress: a well-typed claim can either be used or it can fail with a named error, it does not get stuck in fluent fog. Preservation: if it was grounded before the edit, the recompile either keeps it grounded or explains why not. Chat memory has neither. It always progresses (it always speaks) and it preserves tone rather than reference.

The Curry–Howard joke is almost too on the nose to say, so say it once: a claim is a type. The code is the term. No inhabitant, no theorem. Retrieval that returns uninhabited claims is proving from the empty set and calling it context.

Compile is binding. Not “auth is in the backend” as a description that any nearby file can satisfy. The symbol, the file, the hash of the body you meant. Intension is forced to meet extension, or the pass dies.

Then disasters get locations, which is all engineering ever wanted from metaphysics.

Rename: unresolved identifier.

Move: dangling pointer.

Same name, new body: the Kripke failure. The designator looks rigid. The object was swapped. Only a content hash sees that createSession is now a wrapper and the work moved. Humans do theology here (“it’s basically the same function”). Hashes refuse theology.

A chatbot recites the empty name because it is in the sense business. A compiler says: this does not resolve.


Full re-typechecking on every prompt is the interpreted-language fantasy. Separate compilation exists because the universe is large and most of it did not change.

Dirty the file. Dirty the nodes. Dirty the claims. A worklist, not a mood. Compaction cannot emit a worklist. Compaction is an operational rewrite of a transcript — reduce the term, hope the denotation survived. Incremental recompile is closer to make. It is also closer to what ABI stability is about: which commitments may change without breaking callers.

Call it cache invalidation if you want the systems name. Honesty that requires a full rescan is not a policy. It is a wish. The attractors are cheap-and-rotten and correct-and-unused. Correspondence theories of truth lose to coherence theories inside a vector store: chunks agree with other chunks. They do not have to correspond to the commit.

Falsifiability is the design constraint. A belief that cannot come out false against a hash is not a belief. It is a slogan. Popper is cheap to cite and still right here. If your memory layer cannot lose, it cannot be trusted when it wins.

The output of the pass is diagnostics, not narrative.

Path gone. Symbol gone. Two principals that cannot both be default. Text intact, referent substituted. Once errors are first-class you can fail closed: do not load the ungrounded claim; make the next act relink rather than mythologize; show the broken binding instead of a coherence score.

Magic treats the error as a break in the spell.

Soundness treats the error as the only warrant for everything else you are about to say.


Embedding search is late binding with no linker and no identity.

A query enters a metric space. A neighborhood returns. “Nearby” is not an auditable judgment. Similarity is not reference. It is not even extension. It is a statistical stand-in for “might be about,” which is an intensional fog. Sometimes the chunk is the object. Often it is the right filename and the wrong function — the string compared instead of the name resolved, a confusion the linker existed to end.

That is why RAG feels intelligent and still implements the deprecated path. Recall is a property of search. Linking is a property of languages. Lost-in-the-middle is the penalty for inlining the whole program into every compilation unit and asking attention to perform relocation. Attention is an oracle machine. It answers. It does not produce the edge you could check.

Shannon does not save you here. You can have a lot of mutual information with the query and still have no referent in the tree. Information is not aboutness. Aboutness is what grounding was for.

A graph plus content hashes is early binding honest enough to inspect. Addresses, edges, bodies that match or don’t. Failure named. Dirty set recompiled. Closed if you want it closed. Nominal, not structural: looking like auth is not being the binding named auth.

Call it a knowledge graph for the slide. What it is: a symbol table for commitments, and a linker so the next edit is substitution under a binder, not free association in an opaque context.

The temptation is a better historian. Historians are licensed to work in sense. They can give a coherent account of a system that no longer has an extension in HEAD.

The colder product is denotational. What does this sentence still pick out, in this commit, under this task, de re?


That is the whole essay, without the costume:

Agent memory is being built as a chatbot and it needs to be built as a compiler.

Parse claims so they can refer.

Type them so unsound derivations cannot hide in English.

Ground them to inhabitants.

Recompile what the diff touched.

Treat drift as a failed build, which is to say: make the belief falsifiable.

After that retrieval is administrative. You load what still refers. You do not pour the archive into the window and ask a statistical model to keep intension and extension in register by vibes.

A model does not read a million tokens the way an index does.

And a sentence with no referent is not memory. It is a well-formed formula with no model. The compiler would have stopped.