All blogs
Architecture

Memory that commits: how Memoir remembers a fact

Most agent memory is an opaque pile of embeddings. Memoir takes the opposite bet: classify a fact into a human-readable semantic path, then commit it like git — so memory is structured, attributable, and reviewable.

Most agent "memory" is an opaque pile of embeddings. You can't see what it learned, why, or when. Ask where a belief came from and the honest answer is "vector #4823, similarity 0.81." Memoir takes the opposite bet: make every memory a named thing with a history.

Memoir doesn't dump text into a vector blob. It classifies a fact into a human-readable semantic path and commits it like git — so memory is structured, attributable, and reviewable.

Let's follow one sentence all the way through. A user tells their assistant: "I prefer dark mode in every app." Here is what happens to it.

The Memoir write pipeline A sentence enters a secret guard, is classified by an LLM into a semantic path, then committed to a git-versioned prolly-tree. Secrets are refused. MESSAGE I prefer dark mode everywhere 🔶 GUARD scan for secrets 🟣 CLASSIFY LLM picks one path 🌳 PATH preferences .ui.theme 🔵 COMMIT write to prolly-tree looks like a secret ⛔ refused nothing is stored
One sentence in → a classified, committed memory out.

The call

The agent passes plain text. It does not say where the fact belongs — Memoir decides. Through the MCP server the call and its result are tiny:

// the agent calls:
memoir_remember({ content: "I prefer dark mode in every app" })

// memoir returns:
{ "success": true, "key": "preferences.ui.theme",
  "confidence": 0.97, "commit": "a1b2c3d" }

That key — preferences.ui.theme — is the whole trick. It is a human-readable address, not a vector id. The same write path runs in the Claude Code, Hermes, and OpenClaw plugins; the MCP server is just the example we'll use here.

Step 1 — the secret guard

Memory is versioned plaintext. A store you can log and blame is the last place you want an API key. So before anything else, Memoir refuses obvious credentials — tokens, passwords, PEM keys, card-like digit runs. "Remember my password is hunter2" → refused, nothing stored.

The secret guard A normal preference passes through the guard and is stored; a credential bounces off and is refused. GUARD "I prefer dark mode" ✓ stored verbatim "password = hunter2" ⛔ bounced
A memory store is not a secrets manager.

Step 2 — classification into the taxonomy

This is the heart of the write path. Memoir has a fixed taxonomy: roughly 200 paths, exactly three levels — category.subcategory.type. An LLM classifier reads the fact and picks the single best leaf. "I prefer dark mode in every app" lands on preferences.ui.theme.

The Memoir taxonomy A three-level tree. The fact about dark mode is classified onto the leaf preferences.ui.theme. memoir preferences context knowledge ui food theme⬅ 'dark mode' layout dietary
The path is human-readable — preferences.ui.theme, not an opaque vector id.

Two nuances worth stating, because they're easy to assume wrong:

  • Verbatim. Memoir stores the fact as given, classified to one path. It does not rephrase or split it — that's a separate feature (auto-capture).
  • The LLM is optional. Pass an explicit path and classification is skipped entirely — instant, keyless, no provider call:
// skip the LLM entirely — pass the path yourself:
memoir_remember({
  content: "I prefer dark mode in every app",
  path: "preferences.ui.theme"
})  // instant, keyless, no classifier

Step 3 — commit to the prolly-tree

The classified fact is written into a prolly-tree store and committed — exactly like a git commit. That one design choice hands memory the whole git toolbox:

  • log — when did I learn this?
  • blame — what's the provenance of this belief?
  • branches — diverge memory for a side conversation, merge later.
Updating a fact is a new commit Three commits on a line: init, the dark-mode fact, and an update scoping it to the editor only. init a1b2c3d + preferences.ui.theme = "dark mode everywhere" e4f5g6h preferences.ui.theme = "editor only" the old value stays recoverable in history
Updating a fact is a new commit — nothing is overwritten in place.

The big idea — front-load the intelligence

Here is the contrast that makes Memoir different from a vector store, and it comes down to when the model runs.

Memoir spends its LLM at write time — classify once into a meaningful path. Vector stores spend it at read time — embed the query and scan for similarity on every single recall.

Where the LLM runs Vector memory needs a model on every recall; Memoir needs a model once at write and reads with plain structure navigation. VECTOR MEMORY Write cheap embed Recall — embed query + similarity scan 🟣 needs a model on every read MEMOIR Write — classify into a path 🟣 LLM, once Recall — navigate the structure 🌳 no model needed
Pay the intelligence cost once, at write — then reads are cheap forever.

Because the structure is meaningful, reading it back doesn't need a model at all — which is exactly why recall is LLM-free by default.

What you get

The payoff of "remembering is a well-named commit":

  • Inspectablepreferences.ui.theme, not vector #4823.
  • Attributable — every fact carries a commit you can blame.
  • Reviewable & reversible — full history plus branches.
  • Portable — it's a git-like store you own.

Read the MCP setup guide

A memory you can read, blame, and rewind — because remembering is just a well-named commit.