The taxonomy
Every saved memory carries amemoryType. The taxonomy is the same across every client
(CLI, MCP, HTTP), so a memory means the same thing no matter who wrote it. Pass it on save;
it defaults to fact.
The type is stored with a database CHECK constraint, so the store rejects an invalid type
even if it gets past the API. Two practical effects of choosing well:
- Browsing. The viewer’s Memories tab filters by type; a wall of untyped facts is much less useful than preferences you can pull up on their own.
- Meaning for agents. The MCP
save_memorydescription teaches agents the same definitions, so an agent saving “user prefers tabs” tags itpreferencewithout being told.
context: those aren’t saved memories
but chunks of ingested files (their kind field is context, and they include a source
saying which file and section they came from). You can’t save a context memory; the
sentinel exists so one result list can hold both kinds honestly.
Beliefs, not rows
A memory in memloom is a belief with a lineage. Rows are never mutated in place. Concretely:- Every memory has a
rootId(the stable identity of the belief) and aversion(1, 2, 3…). - The newest
activerow of a lineage is the current belief. Older versions arestale: kept forever, excluded from recall, dedup, and the graph. - Each version step writes a
replacesedge (child → parent), so the graph and history agree. - Validity is temporal:
assertedAtis when a version became the belief, and a stale version’sstaleSinceis when it stopped.
What creates a new version
Resolving a conflict can also step a version: keep new and merge make the winning memory
continue the existing belief’s lineage, so a resolved contradiction shows up in that belief’s
history instead of orphaning it.
Walking history
GET /memory/:id/history, in the viewer’s memory panel, and
to agents via the read-only memory_history MCP tool. Agents can read history; editing a
belief (update) is deliberately a human action in the CLI or viewer.
Recall never returns stale versions:
memloom_fuse and the dedup candidate search both
filter on status = 'active'. History is where old versions live.