Taxonomy

System Families

The 726 systems grouped by eight architectural commitments, the categories almost nothing models, and the boundaries of what counts as memory here.

726 systems do not fall into 182 categories. They cluster around eight architectural commitments, and most systems belong to more than one — a coding-agent memory can also be verification-first, and a host runtime's plugin can also be a hosted service. The families below are lenses, not bins.

Where a system is the clearest instance of an idea, it is named in bold and characterized in place rather than given a category of its own.

Embeddable memory libraries

mem0, langmem, llamaindex, cognee, a-mem, memori, goodai-ltm, pydantic-ai-harness, camel, crewai, agent-memory-supabase, cosmonapse, magicore, membase, mnemosyne, mindcache, all-agentic-architectures, moth-memory-template, ai-workflow, engram-format, memoket-kite, ai-agent-book, widemem-ai, kektordb, yantrikdb-engine, nodedb, longterm-memory-mcp, a-memory, mnemora, engram-cognitive, ontomem, aidememo, cognee-rs, mnemosyne-nabzx, memhop, cortexdb, fernme, fluctlightdb

Called from an application that owns the agent loop. Easy to adopt; weak authority over when memory is written or how recall is used.

AI Workflow Workspace is the family's clearest case of freshness applied to the wrong half of the system. Three memory surfaces — a captured brain, a lessons file compiled into an error-signature cache, and an Obsidian incident vault compiled into a second one — meet at brief.ps1, a router that classifies a query by regex and answers with an instruction rather than a result set: "next: hot-cache hit found. Apply documented fix. Skip traversal." Spending the routing decision deterministically is the design's good idea, and the care around it is real — an index row is hash-validated before the router will classify on it, under the comment "brief must not trust stale index rows", and check-staleness.ps1 hashes every source file against a stored digest. That machinery excludes ai-workspace by name. So the derived code index, which can be regenerated at any time, has a freshness contract, and the memories, which cannot, have none — while the memory path is the one whose verdict tells the agent to stop investigating, admitted on two shared keywords and taking the first row over threshold rather than the best. The field that would close it is already in the schema: every compiled cache row carries status = 'resolved', written as a literal at the single construction site and read by nothing. MOTH is the family's answer to the question the rest of it asks after the fact. Every library here is judged on retrieval, and retrieval cannot recover a record that shares no content word with the question you will later ask — that is decided at write time, by whoever names the file, and almost nothing in this corpus checks it. findable.py does, before the record exists, and returns two verdicts because the failures differ: FOUND? runs the question against the whole record, and failing it means rewriting the content; WINS? runs it against name and description only, because body hits saturate, and failing it means renaming the file. The measurement behind it is committed and reproduces on a clean checkout — 0 of 4 probes sharing no content word with their answer are found, 20 of 20 sharing at least one are, 18 at rank 1 — and the harness reports that boundary rather than a hit rate, on the stated grounds that "hit@1 mostly reports how many zero-overlap probes the author happened to write." Two things temper it. The gate models the ranker rather than calling it, and when recall.py moved to word-boundary matching the model in findable.py did not, so a comment there still describes a substring scorer crediting a filename match at ten times weight where the shipped constant is four and the shipped scorer returns zero. And coverage.py prints the split the rest of the repository lives with: six of twenty-one architecture boxes ship code, thirteen are build prompts, and one of those is the reinforce / supersede / archive lifecycle — so every correction mechanism this atlas measures is, here, an instruction to build one. widemem.ai keeps the Mem0 shape and puts its effort into what must not be forgotten and what must not be claimed. Facts are extracted and resolved into add, update or delete in one batched call, and an overwrite is refused unless it loses nothing or replaces a short memory the model quotes as contradicted. With YMYL enabled, health, legal and financial facts get an importance floor and immunity from decay and purges; retrieval returns a confidence level so an agent can abstain. A test fails any method in core/memory.py that writes without a history entry, README claims are tested, and a public corrections log records the audit gap and the transposed benchmark labels it fixed. Its scope is split: the write pipeline checks every candidate's ids in code, while a search with no user id reads across all users.

AI Agents in Depth is the family's teaching case that measures instead of asserting. The companion repository of a book on agents implements user memory four ways — notes, enhanced notes, JSON cards and advanced JSON cards — written by a background LLM agent's tool calls and injected whole into the prompt, then commits hashed, credential-free evidence for all four and for three retrieval arms on one sixty-case suite with an external judge. The richest representation does not win: enhanced notes pass 0.867 and advanced cards 0.817, a three-case gap from a single run, while contextual retrieval combined with cards reaches 0.950. The evaluation README's fixture table, which shows cards far ahead of notes, is the part to discount.

All Agentic Architectures is the family's clearest case of a backend seam that nothing tests across. It is a teaching catalog — 38 LangGraph architectures, each with a notebook and a cited paper — over a 497-line memory package offering one API across FAISS, Chroma and Qdrant on the vector side and NetworkX and Neo4j on the graph side, switched by a .env line. Three things diverge across that seam and none is caught, because not one of its 80 test cases references EpisodicMemory, SemanticMemory, facts_about or NetworkXGraphMemory. SemanticMemory.facts_about serializes its depth argument into a Cypher string and the NetworkX translator parses it back out with int(tok.split("..")[-1].rstrip("]")), which sees 2]-(other), raises, and falls to one hop for every value — while the architecture above it defaults to traversal_depth=2 and its notebook tells the reader what to expect at depth 2. get_vector_store's FAISS branch never references collection_name, which is the parameter the notebooks recommend for per-user isolation. And a repeated triple is a duplicate parallel edge on MultiDiGraph and idempotent under Neo4j's MERGE. The sharpest version of the problem is the one that reaches the reader who follows the advice: no code path in the library writes to disk, so Qdrant and Neo4j are the only stores that outlive a process — and EpisodicSemanticAgent._retrieve gates episodic recall on an in-process Python list a fresh object starts empty and lists semantic entities only under isinstance(backend, NetworkXGraphMemory), so the one configuration in which this library's memory survives a restart is the one in which its flagship memory architecture retrieves nothing from either half. A catalog is copied from one architecture at a time, which is why the arithmetic matters more here than in a system nobody imitates.

Membase is the family's answer to the scope question the others leave open. Every library here takes a user_id or a tenant string from its caller and trusts it. Membase takes a wallet: each write to its remote hub carries a secp256k1 signature, and the client refuses to file a memory under any owner but the address that signature recovers to (_coerce_owner, src/membase/storage/hub.py:18), because the hub 401s otherwise. That is a stronger boundary than anything else in this family has, and it is worth separating from the rest of the implementation, which is the weakest read path the atlas has catalogued — see Membase.

MindCache is the family's answer to the question every trust-state page in this atlas ends on: whether the status reaches the query. Its decision rows carry a database enum — active, inactive, superseded, rejected, conditional — assigned by an LLM handed a semantic cluster of related decisions, which also writes back a one-sentence reason for each verdict. The part that separates it from a dozen systems here with a status column is placement: status.in_(["active", "conditional"]) appears in the embedding job, in three tree-cache queries and in the client's retrieval query, including the fetch that assembles the similarity candidate set. So a decision superseded after it was embedded leaves the read path without anything having to delete or re-embed it — the stale-vector leak this atlas records for most systems that supersede, closed by filtering where the candidates are gathered. Against that, its README badges two BEAM benchmarks as Passed while the three committed result files carry no score of any kind, and its detached-memory repair re-anchors by argmax with no similarity floor, on the read path — see MindCache.

Mnemosyne is the family's maximalist, and the one whose correction semantics are strongest by accident. One pip install, one SQLite file, one required dependency, and roughly thirty tables behind it: two memory tiers, a regex-fed structured layer, an episodic graph, a consolidated-fact store, harmonic beliefs, canonical identity slots and append-only annotations. Nothing here carries more mechanism per unit of operational footprint, and nothing here puts more between two installations claiming to run the same system — the vector type, the three scoring weights and every per-voice ablation are environment variables read at import. Its extracted facts are keyed by a SHA-256 of the triple, which is what turns its supersession into the value-keyed refusal the rejected-value tombstone page spends its length looking for — a consequence of the key rather than a decision, and the report says why that matters. It is also the atlas's second reading of one engine: Mnemopi is its Bun port, and the inverted provenance weights that report criticised are the same table here.

GoodAI LTM is the family's cautionary comparison, and it points at the two framework contracts above. Dormant since February 2024 and carrying no scope key at all, it nonetheless declares on its base interface what neither ADK nor AutoGen can express: add_text returns a text_key, and replace_text, delete_text and get_text all take that key back. Insert returns an address; update and delete use it. A small research lab shipped the addressed lifecycle in 2024 and the 2026 framework abstractions from Google and Microsoft did not — one declining to declare a removal method, the other unable to, since MemoryContent has no identifier to remove by. Its deletion is still removal rather than rejection, which is the distinction the correction section draws: being able to delete a memory is not being able to reject a value. Cognee is the outlier in surface area — a knowledge pipeline platform with ontologies, dataset permissions, and provenance rollback behind a small remember/recall API. LlamaIndex composes memory from pluggable blocks behind a truncation contract that no shipped block implements. A-MEM is a compact Zettelkasten research sketch whose linked-note evolution idea outruns its implementation. Memori ships a Rust core with Python and TypeScript bindings over seven backends, and is the atlas's sharpest illustration of the family's tradeoff taken one step further: the schema, the drivers and the migrations are all open, and the extraction that decides what a fact is runs in the vendor's hosted service.

The Pydantic AI Harness is the family's answer to the tradeoff below, and it answers it by narrowing the claim. Its memory is a Markdown notebook behind four tools, with no unit below the file, no status, no confidence and no provenance — and the engineering all sits where a library actually can act: what reaches the prompt, and who is allowed to ask. The namespace is resolved from run context and documented as never exposed as a tool argument, so a model has no argument in which to name another tenant; then list_subfiles re-checks every returned path against the requested prefix and raises if a store hands back anything outside it. That check is unique in this atlas. Every other system with a pluggable store trusts the store to have filtered; this one treats its own backend as untrusted and verifies the boundary on the way back, which is the difference between enforcing a scope and asserting that it was enforced. Writes carry an idempotency id derived from the run and the tool call, so a retried write is a replay rather than a second append — the failure mode an agent framework hits constantly and which almost nothing here models.

CrewAI models scope as a filesystem, and is the only system here that does. A MemoryRecord carries a hierarchical scope path — /company/team/user — and MemoryScope is a view of the store rooted at a subtree, with subscope() to descend and a read_only flag, while MemorySlice spans several. Multi-tenancy becomes an object a caller holds rather than a parameter they must remember to pass, and the prefix gives hierarchy for free. A second axis sits on top: every record has a source and a private flag, and recall filters if not r.private or r.source == self.state.source. Both boundaries are applied on the read path and one of them is proved by a committed test.

Then it hands a language model a delete. analyze_for_consolidation returns a ConsolidationPlan whose actions on existing records are keep, update or delete, executed inline on every write, so a model comparing new content against what is stored decides what to destroy. No tombstone, no append-only record, no trust state that would let a doubtful record be withheld instead, and no review surface — the CLI's memory TUI is a browser whose every update() is a panel repaint. The most carefully scoped store in the atlas is also the one that most readily authorises an LLM to remove what it already believed, and nothing measures how often that judgement is right. Two smaller things worth keeping: MemoryMatch.evidence_gaps reports "information the system looked for but could not find", which almost nothing else here does, and match_reasons names why a record ranked — with a test asserting "recency" is absent when the decay term does not clear its threshold.

The unit question has a literature, and it predates the memory frameworks arguing about it. Dense X Retrieval: What Retrieval Granularity Should We Use? (arXiv:2312.06648, submitted 11 December 2023, last revised 4 October 2024) indexes a corpus at four granularities — document, passage, sentence, and a proposed proposition, an atomic expression of a single fact in natural language — and reports that the choice of unit moves both retrieval and downstream question answering, with propositions ahead of passages. Read against the memory_unit column of the matrix, that is the same decision every system here makes and few of them argue for: the systems that extract atomic facts and the systems that keep whole messages are picking different points on that axis, usually without citing the question. The finding does not transfer unexamined — a proposition extracted from a static corpus is not a claim that can later be contradicted, which is the property this atlas cares about and retrieval granularity does not address — but a design that has not decided its unit deliberately has decided it anyway.

CAMEL is the family's floor and its clearest warning about stored scope. Its memory unit is the message rather than the fact — MemoryRecord is a chat message, a backend role, a UUID and a timestamp, with nothing extracted or derived — so it sits at the boundary between memory and window management, on the memory side only because VectorDBMemory embeds messages into a durable store and recalls them by similarity across sessions. Every record carries an agent_id that is set on write, serialised both ways, and applied on no read path; isolation comes from handing each agent its own storage object, which is a convention rather than a mechanism. Two smaller things are worth recording because they are the shape of drift rather than of design: the recall query is _current_topic, set to whatever the last user message said and initialised to the empty string, so the first retrieval in a fresh process is arbitrary; and ScoreBasedContextCreator neither scores nor filters, its token_limit documented as "Retained for API compatibility. No longer used to filter records." A name and a parameter outlived the mechanism they described.

Tradeoff: a library can store and retrieve, but it cannot guarantee the model calls the right tool, verifies a fact, or uses recall safely.

MagiCore is the family's .NET member, and the one that keeps a robot's memory beside a chat memory in the same store. Formerly Mem0Sharp, it rebuilds Mem0's extraction-and-conflict-resolution shape over Microsoft.Extensions.VectorData, writes a history entry with the old and new text on every mutation, reconstructs the store as of a record time and rolls it back, and carries an event time on every row that a confidence-gated interpreter filters on — the dedup key includes it, so one sentence at two dates is two memories. Its robotics plane stores immutable sensor evidence and replays it by capture time into a belief per object with a state that says why not to trust the position: stale, occluded, missing, uncertain, conflicted. Two things bound it. The durable history collection is written on every mutation and read by nothing — every reader consults an in-process queue, so point-in-time reads and rollback do not survive a restart, the Qdrant store keeps no history at all, and rollback is the one mutation the history omits. And the belief state is derived at every recall and stored nowhere, so nothing can list the conflicts without replaying for them.

Engram Format is the family's published half of a closed product, and what it lets a reader verify is the vault and the gates, not what is done with them. The axiom-engram Rust crate and a normative FORMAT.md — a SQLCipher vault keyed from the machine id or an Argon2id passphrase, a documented schema at version 7, an FTS5 index, a 384-dimension embedding table, every KDF parameter and cipher construction written out — released to crates.io on 5 September 2026 by the maker of Engram, whose daemon, REST and MCP servers, browser vault, relay and imagination engine are in a private repository. The capture pipeline is real and typed: a noise filter on episodic captures, a normalised SHA-256 dedupe that strengthens the existing row by 0.1 instead of inserting, a paraphrase gate at cosine 0.95 that reports the match and writes nothing, and one transaction for the row, the FTS entry, the embedding and the links. A row born from the imagination engine is imagined = 1, grounded = 0 and quarantined — excluded unconditionally from the near-duplicate report, the related search's vector fallback and semantic-link generation, and from list and search only when a caller asks for LiveOnly; the default search_by_content, list, vector_search and surface_relevant, and everything reachable through the MemoryBackend trait, apply no filter, so whether the specification's "default recall surface" excludes quarantined rows is decided by closed code. grounded flips only by a caller's mutation and the memory_evidence and annotations tables that would carry the reason have no writer; decay is an Ebbinghaus curve with a stability that grows with retrievals, promotion is five retrievals, and the nightly distillation the crate's own header describes is not in the crate. One mark, trust_state, for a state with a public producer and an unconditional filter on three paths; negative_eval withheld because the two quarantine tests assert emptiness with the positive control in a sibling.

KITE is the family's vector-free member, and the one whose benchmark machinery is worth more than its memory. Memoket's Apache-2.0 library declares zero runtime dependencies and stores a memory as one XML artifact: an LLM turns each session into dated facts under a controlled topic and entity vocabulary, keeping the raw utterances beside them, and a question is compiled into three candidate JSON plans — select, where, a pipe of sort and head — validated against that vocabulary, and the one returning the most rows is executed over posting lists with a relaxation ladder that records which constraints it dropped and abstains rather than return the corpus. Contradiction is settled at read time and nowhere else: the plan language can sort on event time and take the head, the library's default compile prompt never shows that operator, and the default path orders the evidence pack by date and tells the reader the most recent contradicting row wins. The public API is load, remember, recall and answer, and grep -rn -i "def forget\|def delete\|def supersede" src/ matches nothing, so a wrong extraction is permanent and an erasure request has no path through the library. What surrounds the claim is unusually rigorous — dataset revisions and SHA-256s pinned in a manifest, judged rows sealed by digest, a verifier that recomputes the published metric from the sealed bytes rather than a re-opened file because "recomputing from a re-opened file would accept any edit to the verdicts that preserved the row count", and a contamination gate that scans every shipped prompt string for terms concentrated in a small fraction of a benchmark corpus. Two things stop a reader using any of it. The judged rows the manifest names are not committed and the release they are meant to be attached to carries no assets, so the verifier runs only for its author; and the two headline scores come from a shared benchmark baseline that turns on eleven switches the library default leaves off, and from per-benchmark bindings with their own prompts, kind vocabularies and seed taxonomies — LoCoMo's enables an inference pass under a comment reading "Its QA set contains no unanswerable questions, so a bounded inference pass can only recover an answer, never invent a refusal" — while the library's own default profile declares none of them and the pipeline reads every one with a getattr that defaults it off.

KektorDB is a Go vector database carrying an agent memory layer, and its engine is ahead of its memory policy. HNSW over mmap arenas, roaring-bitmap filters, BM25 and a CRC-framed append-only file sit under a property graph with soft-deleted edges and an LLM gardener that consolidates similar memories and writes reflections about contradictions. Superseded and consolidated memories stay in the index as flags that recall_memory excludes and the adaptive, scoped and scored retrieval tools return.

YantrikDB Engine states what its time travel cannot do, in the header above the code that does it. The embeddable Apache-2.0 Rust engine at version 0.23.0 — 198,235 lines across five crates with 2,274 test functions — that the atlas had previously read only through its server and its Hermes plugin. Memories carry stored decay parameters computed at read time, a consolidation_status of active | consolidated | tombstoned, a CHECK-constrained synthesis_state, and — as of v48 — event_time_min / event_time_max that the schema names as event time against created_at as transaction time. recall_as_of(query, t) answers "what did this database believe at time t?" from two ledgers alone: record_revisions, which correction writes the prior text, metadata, importance and valence into, and record_links, whose dated edges hide a record from an as-of read only when the edge already existed at t — "a later supersession does not rewrite what was believed then". Under a heading reading HONEST LIMITS, documented rather than hidden, the module then lists what it cannot do: ranking is present-day, forgotten records stay forgotten, and the pool runs with skip_reinforce because "archaeology must not masquerade as usage" — a separation between reading history and using memory that almost nothing else here makes. The same habit appears on an index that carries a signed note admitting it does not satisfy its query's ORDER BY, and on the encryption migration that vacuums because "THE SEAL IS NOT THE ERASURE". Its audit op is written by log_op_in_tx inside the caller's transaction from twelve modules, so a mutation cannot commit without its record. The boundary it does not hold is scope: recall takes namespace: Option<&str> and emits the predicate only when one is supplied, so the isolation the Hermes plugin is credited with is the plugin's discipline rather than the store's. Four marks; the write-resolution columns that record a dismissal and its reason are written and never read back, so no tombstone.

NodeDB makes access-predicate coverage a compile error rather than a convention. A BUSL-1.1 multi-model database — 117,535 lines of Rust across twenty-five crates, 16,585 test functions — pitched as "[t]he memory and storage engine for AI agents". The memory half of that is framing: the README offers "semantic, relational, episodic, and time-series memory in one engine", the string episodic appears nowhere in the engine's Rust, there is no memory schema, decay, consolidation, provenance or agent-facing memory API, and nodedb-mem — the one crate whose name suggests otherwise — is arena and budget management for RAM. What is here matters more to a shared memory store than any of that. Row-level policies are "predicates injected into physical plans as mandatory filters. Not bypassable by application code", and the injector is "[e]xhaustive over [PhysicalPlan] and every engine's own op enum (one module per engine)", resolving each variant to Inject, Refuse, Admit or No-op under a stated invariant: "A write is never a silent no-op." The nine dispatch modules — kv, document, columnar, graph, vector, text, array, crdt, meta — hold no wildcard match arm between them, so a new operation fails to compile until someone decides which outcome it gets, and the subsystem's single _ => rejects an undecodable row batch "so the policy could not be evaluated against it". A write check left at PendingInjection "reads as 'never ran'" and a second pass refuses it, so the absence of a decision is a distinct state from a decision to allow. Its end-to-end test requires a policy-excluded row to read back absent rather than as an error, because "an error distinguishable from 'no such key' is itself a probe for keys the caller may not read" — and keeps unpoliced baselines beside it, since "a scan that cannot decode its own result cannot be said to filter it either". A bitemporal collection carries required _ts_system, _ts_valid_from and _ts_valid_until columns that the scan turns into two independent predicates. Three marks; the audit log covers security and DDL events rather than row mutations, so none for audit.

LongtermMemory-MCP puts its whole forgetting policy in one readable table. An MIT TypeScript MCP server at version 1.4.4 — 2,763 lines with 109 tests — that states its lineage and its difference in a comparison table: inspired by mcp-mem0, but SQLite through a sql.js WASM build instead of Postgres, all-MiniLM-L6-v2 in process instead of the OpenAI embeddings API, cosine in memory instead of a cloud vector database, and no LLM dependency at all. DECAY_CONFIG is a half-life per memory type — ephemeral 10 days, task 30, conversation 45, general 60, preference 90, fact 120 — with a floor per type so nothing decays to nothing and a protected-tag set of core, identity and pinned that exempts a memory entirely; computeDecay is four lines. Most decay in this corpus is either an untuned constant or a model spread across three files, and this is a table a user could read and argue with. The second decision is write amplification, made deliberately: decay and reinforcement are computed on every access and persisted only when the change crosses half a point — shouldWriteDecay at a 0.5 drop, and a reinforcement accumulator banking 0.1 per access that writes at 0.5 — which matters because sql.js exports and rewrites the entire database file on each persist. Dedup is an exact content hash that throws naming the colliding memory's id, and schema_meta carries a version with a migration test covering it. No marks: memory_type is a write-time genre, importance is a continuous weight, neither withholds anything from retrieval, and there is no status, provenance, supersession, validity interval or change record — deletion is deletion, and delete_all_memories is irreversible.

a-memory makes a suppression flag stick by deciding what a missing argument means. An MIT Python library of 66,891 lines with 281 test files, pitched as "4-tier agent memory with hybrid search and a real knowledge graph — all in plain SQLite files. Zero cloud. Zero external APIs." A long-term fact carries a visibility of visible | pinned | private | hidden; search and key lookup both select visibility NOT IN ('private','hidden'), and the block that injects facts into a model's context reads only visibility='pinned' — narrower again, under an invariant written beside it: "C8: private facts never leave the store via recall (the inject pinned block does not read them)." What turns that into a quarantine rather than a flag is the re-save path. A write that passes no visibility re-reads the stored one before updating, under a comment naming the bug and its date — "a 'hidden' row re-saved with the same canonical key would otherwise be back to 'visible' — F1 sanitation, 2026-09-12" — so, as the API docstring puts it, "'hidden' works as a key quarantine: future writes update the row but never un-hide it". Most systems here with a suppression flag lose it on the next write to the same key. Scope is bound the same way: get_layer(layer_type, user_id) returns a handle carrying both, user_memory() and agent_memory() are the accessors, and every core read begins WHERE layer=? AND user_id=? — a predicate a caller has no argument to change. Every update, insert and delete appends a ledger row with the full before and after image and an attribution. The uniform caveat is that the two supporting mechanisms are advisory: _record_history "[d]egrades to a warning so memory writes never fail on history" and _record_temporal is "advisory, never fails a save", both under a bare exception handler, so a dropped ledger row and a broken interval chain are equally invisible and nothing counts them. The right policy, silently implemented. Three marks; core_memory_temporal's valid_from is the write instant on the same clock as updated_at, so its point-in-time read is version time travel and not a second axis.

mnemora makes provenance the tag of a union, so a memory cannot exist without saying where it came from. An MIT TypeScript cognitive layer at version 0.1.1 — 87,938 lines across six packages with 1,212 test cases, documented in Japanese — built to sit beneath LangGraph, Mastra or a hand-written agent rather than replace one, and aiming to give an application remembering rather than saving. Provenance is a discriminated union of stated | inferred | consolidated | reflected | imported, and the module states the reasoning: the principle of distinguishing the AI's inference from what the user stated "is implemented as the value of kind itself rather than as an additional flag". Each arm demands its own evidence — stated a source observation and a time, inferred the model, the prompt version, the basis memory and observation ids, and a confidence — so a memory whose origin was never established is a value the type system will not build. The union's spelling lives in one place after it was found hand-copied into a recall query, under a rule worth quoting: "when a closed union's spelling exists in two places, fixing one and forgetting the other depends on attention, and will certainly fail." Recall admits active and contested — a disputed memory is surfaced, not resolved away — while superseded, archived and forgotten stay stored and leave retrieval, and the count beside the scan carries the same predicate so a withheld row cannot move it. Three clocks are kept apart: occurred, recorded, and a validity window whose gate builds isExpired and isNotYetValid as separate predicates. Three marks. It has no tenancy to enforce and says so where a reader will look: "mnemora keeps no ledger of tenants. tenantId is an opaque string the caller passes; it performs no existence check and no authentication" — the right place for the boundary in a library one layer down, and the thing to know before treating the tenant column as a control. Engram Cognitive has four columns named for two time axes and one clock that writes them all. An Apache-2.0 Python library at version 2.4.1 — 15,574 lines across 59 files, one SQLite file holding episodes, facts, entities and a weighted graph — whose facts table carries valid_from and valid_to beside recorded_at and superseded_at, the schema of a bitemporal store. Both writers that exist stamp valid_from and recorded_at with the same now, close_fact writes a single now into valid_to and superseded_at together, and no public method accepts a validity time, so get_facts_as_of reads a version chain over write time rather than a belief history. What makes it worth reading rather than merely noting is how the suite passes: tests/test_bitemporal.py builds its rows with a helper that sets recorded_at=valid_from and inserts them straight into the store, a combination no shipped writer can produce. The test proves the query and says nothing about the data the project will ever hold. Against that, the project's process is among the corpus's most disciplined. Three scripts hold three invariants — no provider SDK imported at module level, checked with Python's AST rather than a regexp "because indentation is the entire distinction"; an allow-list of exactly three default dependencies plus an observe-and-recall cycle run with socket creation made to raise; and a gate that recomputes the published recall table from committed per-question records — and tests/test_gates_are_wired.py asserts all three run in release.yml, because a tag push does not trigger ci.yml and until August 2026 the workflow that shipped the wheel ran none of them.

OntoMem keeps the inputs to a merge because the merge destroys them. An Apache-2.0 Python library at version 0.6.0 — 5,774 lines over 36 files — that consolidates each new extraction into a single record per composite key rather than appending observations and ranking them later. Its source ledger exists for the consequence, and states it: "because merges are destructive, the only way to remove a source's contributions precisely is to re-merge the surviving sources' raw results for the affected keys." With the ledger on, removing a document recomputes every affected key from the survivors and deletes only those nothing else contributed to, while the coarse fallback that every ledger-less store is forced into ships beside it under the name strategy="touched". The overhead is given as a number rather than waved at. Two smaller pieces travel well — the FAISS index records the embedder that produced its vectors and refuses vectors from another embedding space, turning a silent similarity failure into a refusal at load, and a semantic edit validates key-invariance so removing one wrong fact cannot move the record to a different key. It carries no capability marks, and the reason is coherent with its design: a merged record has no status, no validity interval and no supersession link, so the store holds the current value for a key and has no vocabulary for a claim that has stopped being true.

AideMemo makes the authorized scope a different type from the requested one, so no backend can be handed an unscoped write. A dual MIT/Apache-2.0 Rust working memory at version 0.1.0 — 95,480 lines across thirteen crates, one binary serving a CLI, MCP over stdio and HTTP, and bindings for four languages. Most multi-tenant stores on this page take the caller's word for which tenant a write belongs to and then rely on a predicate somebody remembered to add; this one forecloses the question. AuthorizedCommand is "[c]ommand paired with server-owned authorization context", its only constructor returns ProjectScopeMismatch "when the untrusted envelope selects a different project", and MutationCommand — the single argument CommandStore::execute accepts — carries that authorized command as a field, so there is no unscoped value left to pass to an adapter. The audit row obeys the same rule and is transactional: adapters "persist its canonical fingerprint, resource mutation, receipt, change entry, and audit entry in one transaction", and its tenant_id is the "[s]erver-derived tenant" with actor_id the "[s]erver-derived actor provenance" — the record of who changed a memory is not a string the caller supplied. Two annotations carry the same instinct: ActorKind separates a Human ("[i]nteractive person") from an Agent and a Service, and display_name is marked "never used for authorization". What the model says nothing about is truth — RecordStatus governs accounts rather than claims, and the only times are creation, update and a revision.

Cognee-RS puts the ownership check below the layer that documents it. A Rust reimplementation of cognee — 269,864 lines across 30 crates, 236 commits, 3,809 test functions — built as a drop-in companion to the Python SDK, with parity so deliberate that one router comment asks a future maintainer not to "fix" a top_k <= 0 behaviour because it would break the cross-SDK tests. Its HTTP permission helper returns Ok(()) in an open build, because the real grant resolution lives in closed crates; read only that file and the search path looks unguarded. It is not. The orchestrator authorizes the caller's dataset ids — and the ids that dataset names resolve to — against a readable set derived from the requester, fetched in one listing query rather than one per id, because "the requester must not get to choose how much work the authorization check costs", and failing closed to an empty set if neither backend is wired. The test that proves it does not stop at the status code: a recording retriever asserts last_params().is_none() after the 403, so the store was never queried and not merely the answer refused, with the owned-id case asserted immediately beside it. Two marks. The predicate applies when the caller supplies a filter; an unfiltered search carries no owner scope, which matches Python and is written down. What is not written down is in the delete crate: DeleteMode::Soft is the HTTP default, the only branch on that enum decides whether an orphan sweep runs afterwards, and the service's own test asserts the dataset is gone.

Mnemosyne (Nabzx) makes the history the store. An Apache-2.0 Rust core with a Python SDK, an MCP server and LangGraph and OpenAI Agents adapters, 367 commits from one contributor between 6 and 28 September 2026. Every agent write is a commit: a full state snapshot and a commit object in an insert-only redb table, so blame traces a belief through merges to its commit and provenance, and bisect finds where it entered. That is its audit_log. The weakness is everything above the history: no search, no scope key, no trust state. forget drops a node from the next snapshot, and the agent's own recall_at tool reads it back. Both adapters join scope segments with an unchecked colon.

MemHop keeps no retrieval at all and puts its effort into the file underneath. It is a Go library over one .meh append-only log whose frames carry a CRC and an agent id, so several agents share a file with every read keyed to its own domain. A turn is closed by one LLM call that turns the user and agent text into a keyword list, and the scene's lists are what a host injects. The originals are swept after seven days, and a background Dream replaces adjacent lists with keywords of a model-written reconstruction. The storage and its isolation tests are careful; the surviving memory is model output, and deletes stay in the file until the host compacts.

CortexDB puts more correction machinery around a memory row than most libraries here put around anything, and then reads the row from a bucket the save instructions never use. A pure-Go, MIT library over one SQLite file, shipped as a Claude Code and Codex plugin with three hooks. A memory is a messages row in a bucket built from scope, user and namespace; supersedes retires it from every search lane, deletion retracts its graph node into history, a trigger-written change log records each committed change, and the graph beneath is bitemporal. The auto-recall hook passes no scope and reads only the global default bucket while /remember saves under user scope, and the graph lane, memory_get and memory_list_all cross buckets.

FERNme writes memory with arithmetic and governs it like a product, and its MCP surface does not inherit the governance. A per-site, per-user graph of namespaced tags weighted 0 to 9 is bumped by a Hebbian rule with no model call and compiled by spreading activation into an eight-tag card. Around it sit consent gates, an HMAC audit chain that outlives erasure, a k-anonymous noised population prior for cold start, and a review queue whose content-hash ids keep a rejection rejected. The weak side is the default deployment: the local MCP server lets the proposing agent accept its own suggestions, defaults every timestamp to 0, which turns off decay and latest-value-wins, and never enables the curation that produces superseded.

FluctlightDB puts provenance in the record type and leaves it to the writer. A dual-licensed Rust engine with Python, MCP and HTTP surfaces, one brain directory per agent. Every episode can carry one of five provenance kinds and a verified flag, and recall, retention and conflict resolution all read them, as weights, an expiry exemption and an injection lane, never as a filter. The storage engineering is careful: generational checkpoints with named fault points and a chaos suite. The agent_id filter runs after truncation, two lanes then inject verified rows from the whole brain, and the default checkpoint never writes the audit-log segment its loader reads. No marks.

Hosted and service memory

openlore, honcho, supermemory, hindsight, redis-agent-memory-server, openviking, agent-memoryforge, memanto, memory-engine, memu, elastic-atlas, mirix, memobase, powermem, memmachine, gobii, cortex, lorekit, agentswarms, universal-memory-engine, omi, mnemory, vllm-semantic-router, openakashic, statewave, caura, commonground, memora-engine, hivemind-activeloop, lobu, halofy, openconcho, maximem-synap-sdk, flair, contextstream-mcp, memorizer, spector, resonant-mind, kagura-memory-cloud, memseek, mindmemos, synapse-layer

Statewave answers the family's hardest question by refusing to ask it at read time. Apache-2.0, 462 commits from April to September 2026 by thirteen authors, 26,041 lines of Python over PostgreSQL with 1,271 test functions beside them. Raw events land append-only in episodes; a compiler derives typed memories from them once per subject change; assembly reads the already-compiled active set, ranks it and packs it into a token budget. The README states the bet plainly — "the same query against the same subject at the same point in time always produces the same bytes. That determinism is what separates compile-then-use from query-time retrieval, where sampling noise leaks into every answer."

Three mechanisms are worth taking. Validity time is separate from record time and both are queried: valid_from/valid_to beside created_at/updated_at, with or_(valid_to.is_(None), valid_to > func.now()) on every read, a TTL sweep selecting on validity, and a receipt diff selecting on record time to report what appeared after a past assembly. The tenant guard is a fitness function: a test parses repositories.py with ast and fails CI when any helper takes subject_id without tenant_id, its allowlist empty and its docstring forbidding additions. And the ranking refuses a signal that is not one — the service excludes a stub embedding provider's scores after finding those "deterministic-but-meaningless vectors" dominating ranking in production.

The best test in the repository is the one for a hole the architecture creates. Superseding a memory demotes the claim, but the episode it was compiled from is still a raw event and the bundle renders recent episodes verbatim — so a correctly-retired fact can walk back in through its own source. test_episode_leak.py seeds two episodes differing only in the numbers, supersedes the memory behind one, calls the real assembly, and asserts the live episode present, the stale absent, 2.9 in the rendered prompt and 3.5 not in it. Any store that keeps raw events beside derived claims has that hole by construction, and most do not test for it.

CommonGround Kernel puts the cause on the audit row, which is the column most audit tables leave out. Apache-2.0, 41,006 lines of Python over PostgreSQL, twenty commits between February and May 2026 from eight authors and nothing since, under a v3r1-preview label. cg_kernel_ledger carries a database-assigned ledger_seq, an event_type, a subject_kind/subject_id, an actor_kind/actor_id and a nullable cause_kind/cause_id — so a row records not only what happened and who did it but what event produced it. Two statements write the table and both are inserts.

Its scope model is worth copying for a different reason: project_id is applied on every repository read — twenty-six where project_id = %s clauses — and it is also part of every composite primary key and foreign key, so a row referencing a parent in another project is not something a bug can write. A test drives the boundary through the API: one project's admin service registering an agent into another is refused with caller project must match path project, and the agent is then asserted absent from the target project's topology. A sibling test asserts the party who created a project leaves no trace in the kernel's own snapshot or public metadata — a constitutional property, and an unusual thing to check.

Two limits belong with it. The kernel offers no retrieval by meaning at all — no embedding, no vector column, no full-text index — because reading is by identity and by sequence, which is coherent for a ledger and means an adopter writes the recall layer. And the payloads every record points at live in CG-Cardbox, a submodule not checked in with the parent, so every question about how content is retained, corrected or deleted has its answer in a repository this reading did not have.

Caura's frontmatter carries all seven capability marks, which is a claim worth checking mechanism by mechanism rather than counting. Apache-2.0, version 3.7.0, 1,199 commits from eighteen authors between April and September 2026, 66,323 lines in core-api and 36,570 in core-storage-api over PostgreSQL with pgvector, beside 143,878 lines of tests holding 5,729 functions. Formerly MemClaw, with the old tool names and environment variables kept working.

The tombstone is the one to check hardest, because it is the mark this rubric refuses most often. Rejecting a distilled skill in the inbox writes its cluster fingerprint to forge_rejected_fingerprints with the rejecting agent, a reason and a cooloff; the next distillation run is handed a PoisonChecker built against that table, and _distill_cluster raises _PoisonedClusterSkip rather than proposing the same cluster again. The docstring states the gate it was written for — "Reject → fingerprint written to poison table; Forge re-run does NOT propose the same fingerprint." Keyed on the value, durable, consulted on a later write. Its limit is that the cooloff defaults to thirty days, so it is a moratorium rather than a permanent refusal.

Three more are worth naming. Admissibility, confidence and provenance are three separate fields: an eight-value status of which only active, confirmed and pending survive the read filter; a nullable confidence in the claim; and is_inferred, which marks a memory the system materialised "so it never silently overrides an explicit fact." The audit log is a per-tenant hash chain whose serialisation head is a separate one-row table, locked FOR UPDATE so the large append-only log is never itself locked, with a per-event idempotency key so a retried flush cannot double-append. And _fleet_scope_clause is one shared builder because — in the same lesson two other systems in this family learned independently — "the identical predicate lived in several queries, one was fixed, and the leak simply moved to the next copy."

Two caveats belong beside the seven marks. The tombstone and the human review both sit behind an org_settings.skills_factory.enabled flag and return 403 when it is off, which is where a new tenant starts. And the accuracy figures in BENCHMARKS.md are not reproducible from the tree — the file says so itself.

Multi-user, API-first, with background derivation. Honcho models workspaces, peers, sessions, and derived representations rather than flat facts. Hindsight runs four independent recall arms with task-specific fusion. Redis Agent Memory Server splits TTL-scoped working memory from promoted long-term memory and carries the atlas's most developed retention policy. OpenAkashic is the family's outlier on the axis the others share: it has no tenant at all. Every other service here separates users; this one is a single public memory that any agent may read without a token and write to after provisioning one in a call, on the argument that a fix derived by one agent should not be re-derived by the next. Its correction machinery is what that choice forces — an agent files a dispute with a rationale and evidence URLs, reviews accumulate, and a scheduled LLM loop consolidates them into a verdict of uphold, revise or supersede parsed from an anchored VERDICT: line. The two read paths then disagree about what supersession means: the note search drops superseded material before it reaches the ranker, with a committed test asserting it never gets indexed, while the public claim search applies a fixed −0.42 score penalty that a claim's own accumulated confirmations, role and confidence largely pay back — so the longest-believed claim is the hardest to demote. Its README also publishes a controlled follow-up that found no significant lift, in the same sentence as the result it qualifies. OpenViking unifies memory, resources, and skills in one filesystem hierarchy with three retrievable granularities per record. Memanto is the only system here whose contradiction pipeline ends in a decision: a nightly pass writes a dated conflict report, and a human resolves each entry as keep_old, keep_new, keep_both, remove_both, or manual with content they write themselves. Memory Engine treats a restricted API key as a ceiling rather than a grant: build_tree_access intersects the key's declared per-path access with the member's live grants by least() at every path, so over-declaring a key is harmless by construction. memU ranks segments and returns the files they belong to, scoring each file by the max of its segments — the search unit and the return unit are deliberately different sizes. MIRIX gives each of six memory types its own table, manager, writer agent and prompt, and enforces a four-level scope in the SQL, the Redis index queries and — since a July 2026 fix — the SQLite fallbacks that had filtered on user alone. Memobase goes further on the same axis by making scope structural: every primary key is (id, project_id) and every foreign key is composite, so a cross-tenant query is a schema error rather than a review failure. AgentSwarms is the family's minimalist and the one that shows how far a platform can get without a vector on the memory path: a Postgres trigger derives a keyword array from each item's content, a GIN && overlap ranks against it, and no embedding is called between storing a fact and finding it again — in a codebase that already runs pgvector for its knowledge bases, so the narrowing is a choice rather than a limitation. What it costs is visible in the same file: the tokenizer drops every token under four characters, so a memory about SQL, Go, npm or an API cannot be retrieved by that word in a developer-facing product.

LoreKit takes the third position on that axis and the one most projects can actually reach: the boundary is neither a filter nor a composite key but Postgres row-level security, so every read is gated on auth.uid() or a matching org_id JWT claim by the database rather than by the query. Its org_scope_bindings table then routes a write on a bound scope to the organisation instead of the writer, checked through lorekit_org_can — which means a team's shared scope is not a naming convention. It is also the atlas's clearest case of infrastructure outrunning epistemics: RLS, org roles, invites, token scopes, per-user caps, an append-only audit log — over a store whose memory model is a keyed slot with no status, no confidence and no history.

Three members mark the family's boundaries on the same axis: what happens to the evidence. Memobase caps a user profile at five sentences per subtopic and fifteen subtopics per topic, and deletes the source transcript after extraction by default; MIRIX ingests screen captures continuously and runs a periodic pass that can rewrite the whole store. One is the smallest useful description of a user, the other is the largest, and neither can say that a value was rejected. PowerMem answers the same question a third way, with an Ebbinghaus retention curve that decides reachability — the atlas's most complete forgetting model, and a reminder that a well-tuned curve is still not a trust state.

MemMachine takes the opposite side of Memobase's bet and is the better system for it. Every raw episode is kept, and each derived SemanticFeature carries metadata.citations — the episode IDs it came from. Because the episodes are still there, those citations resolve: "why do you believe that?" returns text a support engineer can read. That is the rarest property in this family and it costs one array column. The same retention is what makes its correction story thin: a deleted feature leaves no rejected-value record, and only a one-way is_ingested watermark stops the still-present evidence from producing the feature again — protection from bookkeeping rather than from knowing the claim was wrong. It also states its own write lag in configuration (feature_update_interval_sec = 2.0), which almost nothing else here does.

Tradeoff: the API surface is usually easier to study than the decision machinery. In supermemory the hosted core is not visible at all; in mem0 several documented capabilities are managed-platform-only.

Omi is the family's only ambient-capture member, and the constraint shows in the design. It records conversations and screen activity continuously, so its memory is not what a user chose to tell it but what a microphone happened to hear — which produces two problems the others do not have, and it has answers to both. capture_confidence and veracity are separate fields because a misheard sentence and a doubted claim fail independently, and subject_attribution records whether a fact is about the user, a third party or nobody identifiable, because a device that hears other people talking needs to know whose fact it is holding. Its ACTION_POLICY, mapping status to permitted uses, is the idea worth taking from this family, though at the current pin no action path calls it — see Omi. Memora Engine is the family's cleanest supersession, and its one gap is on the other side of the write. A Fastify and Postgres service in 5,230 lines of TypeScript, eleven commits by one author: extraction stores a memory, a model classifies its relationship to the five nearest active memories, and a supersedes verdict above 0.75 confidence flips the older one to SUPERSEDED and records the edge with the model's reason in the same transaction, with a guard that an older memory cannot supersede a newer one. The committed evaluation seeds a superseded and an archived memory into a twenty-five-memory corpus and asserts neither reaches any query's top five over results that are full by construction — and with the status filter removed the superseded memory ranks second, so the case can fail. Deduplication, though, compares a candidate only against active memories: a superseded value repeated in a later conversation is stored as new, and being newer it can supersede the correction.

Hivemind (Activeloop) is the client half of a hosted store, wired into seven coding agents at once, and its memory of record is a skill rather than a fact. Every prompt, tool call and assistant turn is inserted into a Deeplake workspace by a hook, and the agent reads it back by running grep and cat against a mounted path, which a PreToolUse hook validates against an allowlist of 74 builtins and compiles into one UNION ALL of an ILIKE arm and a cosine arm. A background worker mines the last ten sessions in scope and asks a model whether the activity contains a pattern worth crystallising; a keep or merge writes a SKILL.md and an append-only row, and auto-pull installs every author's skills onto every signed-in machine at the next session start with the user filter hardcoded empty. The correction path is the part worth reading: invoking an org skill arms a three-message window, the next user message is judged by a model asked the anti-sycophancy question — "Ignore whether the user seemed happy or polite — a praised-but-wrong answer is a FAILURE" — and a failed verdict produces at most three anchored edits outside a protected region, published as the next version org-wide under a comment reading "No approval gate by design". Its own outcome vocabulary, proposed | applied | reverted, has a producer for the first value only and no reader at all, so the loop can avoid repeating an identical edit and can never tell an improvement from a regression. Scope is the workspace and little else: a git-remote project key filters code-docs search, a directory-basename key filters the session-start resume brief, and memory search carries neither. Nothing is recalled per prompt, since the hook that did so was removed on 16 September 2026, and secret masking runs inside each capturer separately, so the pi extension's own writer inserts turns unmasked.

Lobu puts the access rule in the query rather than in the store, and that is the whole design. One Postgres database with pgvector behind a remote MCP server, 280 migrations and roughly 355,000 non-test lines of TypeScript across nineteen packages. Connector polls, webhooks, device signals, agent writes and tool invocations all land as immutable rows in one events table; a typed entity graph and normalised identity claims sit over it, and a hybrid read combines a lexical rank with the best-matching chunk vector. What distinguishes it from the rest of the family is that one value — an authorization scope of organisation, principal and agent — compiles every read seam's visibility clause, and the third fragment mirrors the source system's own access control into SQL: a GitHub repo's collaborators, a Slack channel's members, joined as a membership edge at recall time. A connection whose ACL sync has gone stale matches neither the passthrough nor the membership branch and its rows are dropped rather than served on stale membership, and the membership test runs over the whole linked set rather than any one of it: a memory derived from two repositories is readable only by someone entitled to both. Correction is masking on an append-only log: a tombstone-typed row stamps the target as superseded, a view hides it from every recall arm, and one read path still reads the raw table, while nothing is keyed on the retired value, so the same text saved again is a new live head. The human layer lives on the entity graph instead — an agent's write to a human-owned field is blocked into a durable approval only a signed-in person can resolve, and a proposal whose value drifted since it was queued is skipped rather than applied.

Halofy draws the same boundary from the other side: the key decides the scope, and the store never sees a namespace a caller chose. AGPL-3.0-or-later, about 96,000 lines of TypeScript on Postgres with pgvector — embedded PGlite by default. Namespace, actor and role come only from the API key; every read binds the key's namespace and its /-split ancestors as an IN list, so a child team reads organization facts and never a sibling's; each outcome, denials and misses included, is hash-chained into audit_log inside the transaction that produced it; and retrieval is a read-only cartridge over a scoped view that a conformance kit checks. Corrections close a validity interval and link the replacement, the one delete is a signed, tombstoned erasure, and knowledge drafted by consolidation publishes only after a person approves it. The gap is in the part its documents stress most: the write-time quarantine of a lower-trust contradicting fact as disputed has a status, a table, a review service and tests, and nothing in the source that creates one — contradictions are instead judged after commit by a model that must be configured, and an underranking one stays readable until someone resolves the finding.

OpenConcho is the rare artifact built so a person can see — and delete — what a memory system concluded about them. An MIT desktop and web client for self-hosted Honcho, 18,136 lines of TypeScript, storing no memory of its own. Honcho's conclusions are typed explicit, deductive, inductive and contradiction; the ConclusionBrowser lists them, writes new ones through a modal, and deletes one through a dialog reading "This conclusion will be permanently removed" that issues the delete against the live server — a person's surface that edits another system's derived beliefs, which is the mark. Two things about the rendering matter before trusting the view. inferConclusionType ends ?? "explicit", so a conclusion whose level is absent or unrecognised displays as an explicit statement — the most certain of the four types, and the failure direction that hides how much a belief was inferred; an adjacent comment records that the generated schema is Honcho 3.0.5, which "does not expose level" at all, while live 3.0.11 returns it on every conclusion. And a "dream" — the unit the whole interface is organised around — is not a Honcho object: the client derives it by grouping conclusions that share an observer, an observed peer and a session within a sixty-second gap, so any count or narrative at dream level is an artifact of that constant. The same file is candid that premises and reasoning_tree "are still unserved — the premise tree stays empty until Honcho ships them", so the provenance view it is built around cannot yet be filled. Its own enforcement is a transport rule refusing to save a token for anything but HTTPS or loopback, tested down to a LAN address not counting as local; instance tokens live in localStorage.

NeuralMind writes a test that its own marketing numbers are reproducible. An open-core persistent memory and context compressor for coding agents — 102,514 lines of Python, MIT except neuralmind/tier2/, which is source-available — whose memory is an index over a repository rather than a store of claims. Its audit trail earns the mark: an append-only JSONL with a tamper-evident SHA-256 chain, a verify that recomputes every entry, and rotation that preserves continuity by seeding the new file from the archived file's final hash, covering the build and ingestion paths and recording failures as well as successes; a second chain under the commercial licence carries governance events with its own genesis hash. The licence statement is the clearest open-core boundary here — one directory, two licences, and a forward-only promise that "every release up to and including v2.0.1 was published entirely under MIT and remains MIT permanently". The unusual artifact is tests/test_site_claims.py, which gates the project's own website: every N× ratio on a high-traffic page must be listed in site/claims.json "with a source and a reproduction command", names on a private_names_never_publish manifest must not appear under site/, and absolute privacy claims are forbidden — with the docstring naming the four drifts that shipped before it existed, including a 63.6× transcription of 65.6×, a latency figure "with no measurement behind it anywhere in the repo", a 100% recall claim "the current public benchmark contradicts (93.75% mean; click is 0.79)", and a real client name in a report every other document anonymises. The chain's weakness sits beside its strength: _emit_audit swallows every exception so the log never blocks a query, and verify skips an unparseable entry as a legacy line with "no chain check" — so the chain proves nothing was altered while a hole verifies clean. One mark.

Maximem Synap SDK proves its identifier contract by parsing its own source — in one of its two languages. The Apache-2.0 public client surface of a hosted memory service, 82,093 lines synced out of a private monorepo, so nothing about the store itself is inspectable. What is, is a scope-mismatch guard whose docstring is a complete incident report: sending a customer_id to a B2C instance meant "the write was filed under the customer, the read asked for the user, and both returned success", and "[o]ne client ran 4,634 consecutive empty fetches across seven days without a single error to look at" — from which the principle, "[a]n SDK that stays silent about a misuse it can see is not being permissive, it is hiding the bug." Its unknown-mode branch fails open on purpose, because guessing would refuse a B2B client's mandatory field against every un-upgraded server, "a far worse failure than the one it prevents". The Python package then makes the guard structural: a test walks the package's own AST, enumerates every public method whose signature takes a customer_id, and asserts each calls the check, with a justified exemption list and a companion test_the_guard_is_not_vacuous whose message explains itself — "found no methods; the rule below would pass for the wrong reason". The TypeScript package ships the same guard function and a parallel test of its five behaviours, applies it at five call sites, and has no coverage test — so user/interface.ts forwards a customer_id as a query parameter unchecked and tool/as-tool.ts puts it into three request bodies unchecked, both of which Python guards. The bug is fixed in the SDK that can prove it. No marks: a client-side identifier check is not a stored scope key applied as a read filter, and the docstring says so, deferring to the server.

Flair wrote its scoping rule into one module because the same rule, scattered, was the leak. An Apache-2.0 TypeScript identity-and-memory substrate at version 0.54.2 — 250,467 lines with 474 test files, an Ed25519 keypair per agent, a Harper instance the CLI installs and supervises, and thirteen runtime adapters. The read-scope module's header says what it replaced: "[b]efore this module existed, SemanticSearch had its OWN inline grant-resolution + a visibility === \"office\" global OR-clause that leaked ANY authenticated agent's read of ANY other agent's memories … Scattering the scoping rule per path is exactly how that leak happened — this module exists so it can't happen again: one rule, one place, every path imports it." Five paths import it, the resolver is composed from a record-type registry rather than hand-typed per site, and a tripwire test introspects the composition against that registry. The private exclusion is argued from old rows rather than asserted: not_equal 'private' over equals 'shared', because the latter "would silently retroactively privatize every legacy row". Promotion inverts the default in the other direction — a candidate becomes shared only if a scope tag, a ruling and a rationale all survive re-verification, so "a shared promoted row must always trace to a recorded justification, never to a default" — and the de-duplication gate is labelled NEVER SUPPRESSES A WRITE after an earlier client-side version silently dropped the second of two distinct findings. Two marks. The model to read before deploying is the one the module is honest about: within an instance every verified agent reads every other agent's non-private memory, grants no longer gate reads at all, and the only hard boundary left is the federation push filter. The documentation is candid in the same register — the match percentage "is not a probability that the memory answers your question correctly", a root-owned install makes semantic search "silently degrade to keyword-only", and the built-in MCP surface is off by default with no documented client using it.

ContextStream MCP Server refuses to let a path be an identity. The MIT-licensed Rust client for a hosted memory service — 229,967 lines across seven crates, 455 commits, 2,820 committed test functions — whose NOTICE says plainly that the platform "includes proprietary backend services not included in this repository". So the memories are behind an API and what this tree decides is which project a machine may speak for, which is where its care goes. checkout_identity.rs opens by naming the threat: a canonical path is not sufficient identity, because a folder can be deleted and another appear at the same path. The answer is a random versioned marker written into Git's common directory and copied into the checkout-local config, with the rule that makes it work stated beside it — creating the marker is deliberately a different code path from reading it, "so ordinary init, context, and hook paths cannot silently bless a replacement folder." A managed git hook, which no human invoked, must satisfy five separate equalities before a single field reaches the wire, then re-reads the config immediately before sending and drops project attribution rather than misattribute it if ownership changed in between. One mark, on that binding and on a read cache whose key is a length-framed hash that comes back None — and so bypasses the cache entirely — for a caller with no identity. The decision statuses are defined and validated here and filtered there, which is why trust_state is withheld: a string put on a wire is not a filter this atlas can read.

Memorizer makes every agent edit reversible and keeps no durable record of a delete. A self-hosted .NET MCP server over Postgres and pgvector, it stores titled Markdown documents the agent rewrites by find-and-replace, snapshots the prior state and writes an event in the same transaction, and reverts in one call. Hybrid search fuses a title-and-tags vector leg with weighted full-text by RRF, chosen with a committed eval corpus. The record is thinner than the editing: two of six declared event types have producers, a delete cascades history away, delete_workspace reports moving memories it does not move, and nothing authenticates a caller.

Spector is the family's clearest case of a correction mechanism that works on one plane and is undone on the other. A Java 25 engine, embedded or served over MCP, REST and gRPC, it stores off-heap engrams ranked by similarity, importance and decay, and isolates each namespace in its own directory. On engrams, an LLM-judged consolidator flags the older of two contradictory memories and default recall skips it, with a committed test holding winner, loser and the forensic mode. On its bitemporal fact log, valid time is written in seconds and compared in milliseconds, and no production read excludes a retracted fact. A release gate filters three flags nothing sets.

Resonant Mind resolves contradictions on the write path and does not tell most of its readers. A source-available TypeScript Cloudflare Worker, 17,088 lines over Neon Postgres and pgvector, that serves one AI's memory through 47 MCP tools and runs a daemon every 30 minutes for novelty decay, archive, Gemini consolidation, dreams and identity proposals. Each new observation retires any same-entity observation at cosine 0.85 or more, setting valid_until and superseded_by as a guarded pair. Only mind_search and graph_look honour that; the surfacing pools, the wake ritual and the HTTP search do not. The supersede pointers carry no delete rule, so a retired pair cannot be deleted, and the attempt removes the embedding first. No marks.

Kagura Memory Cloud is the family's clearest case of correction that hides rather than deletes, and of a default that deletes anyway. A self-hosted FastAPI MCP server over Postgres and Qdrant, it requires workspace, context and user keys on every search call. A supersedes edge shadows an older fact out of default recall, and a server-detected near-duplicate arrives as a suggestion the agent accepts or dismisses; the dismissal reopens only if the similarity moves. The weak half sits in the defaults. With SLEEP_ENABLED unset, a legacy daily task hard-deletes working memories unsurfaced for 30 days, with no record and over only the 100 newest per user. The pinned lane injected every turn ignores supersession.

Memseek compiles memory from YAML declarations onto one Postgres table, and refuses more than it stores. A self-hosted API and worker in the compile-then-use shape of Statewave: immutable records, LLM derivations over bounded sources, token-budgeted artifacts, an MCP allowlist whose one write kind is append-only. A derived record may cite only ids its run was shown. A run or promotion whose captured heads moved commits nothing, and reviewed drafts stay out of every default read until /promote. The weak parts are one all-powerful workspace key, an MCP ingest bridge that forwards status, derived_from and scores its schema omits, and a flagship L1 layer whose supersession no read filters on.

MindMemOS binds the tenant to the credential and then consolidates across the users inside it. A FastAPI service over Qdrant, Neo4j and Kafka, also run as a hosted cloud, that extracts memories from dialogue with an LLM, either as free-text facts behind a deterministic confidence gate or as timestamped values of typed entity properties. project_id comes from the API key, is forced into every Qdrant filter and re-checked on id reads, and a status = active predicate reaches graph expansion. Below the project, user_id is a request field: list and scroll accept it and ignore it, and the dreaming pass finds neighbours by shared entity with no user predicate, then archives exact-text duplicates across that cluster.

Synapse Layer keeps its agent key inside the SQL and out of the row's identity. The Apache-2.0 Python SDK of a hosted memory service whose pgvector store, Trust Quotient and MCP tools are closed; the open tree holds a regex-redacting write path and an opt-in SQLite table. Recall ANDs agent_id = ? onto every branch, and a committed test proves another agent's matching row stays out. The primary key is a content hash, so a second agent storing the same sentence takes the row over. Ranking ignores the match: candidates are any row sharing a word, ordered by a score that is a keyword-and-self-report blend squared. Self-healing, the recall router and the plugin strategies are declared and never take effect.

Agent-runtime memory

humans, letta, animus, ragflow, rainbox, memos, mastra-observational-memory, claude-mem, npcpy, juggler, gitlord, tokenmizer, zerostack, agentmemory, tencentdb-agent-memory, nanobot, cowagent, genericagent, mercury-agent, atomic-agent, mateclaw, waku-agent, loongflow, buzz, openmake-llm, elai, argos, openmasq, khoj, anything-llm, usememos openhuman, aukora-kernel, helm, neko, sillytavern, risuai, soul-of-waifu, z-waif, virtualwife, aura, memledger, ruflo, agentic-context-engine, deer-flow, helix-agi, aimaos, opensre, windie-sandbox, one-agent-many-hats, hestia, muninn, auraos, openvurp, shisad, mcp-memory-service, khabeer, tanglies-agentos, nuum, agentrt, inno-agent, bitterbot-desktop, ox, syke, sivtr, chump, bifrost, hypha, ai-maestro, cua, elizaos, ogad, context-engineering, living-mind-cortex, mama, prismer-cloud, octop-memory, cyrene-agent, autobot-ai, flowly

ShisaD makes trust a lookup rather than an assertion, which is the cleanest answer in this family to who said this. Apache-2.0, 2,114 commits from five authors since January 2026, 141,275 lines of Python of which 13,963 are the memory package, beside 212,716 lines of tests. _VALID_TRUST_MATRIX maps a triple — (source_origin, channel_trust, confirmation_status) — onto a band of elevated, observed or untrusted plus a confidence. Eight origins, seven channel trusts and six confirmation statuses do not multiply out: only enumerated combinations are legal, and an unlisted triple raises TrustGateViolation rather than defaulting to something safe-looking. The caller never supplies the band; the runtime derives it from the ingress handle that admitted the content, which is SHA-256-bound to that content.

Two consequences are worth carrying. build_identity_pack admits an entry only when its band is elevated, so a merely observed claim cannot enter the region the planner reads as the user's own identity — the band withholds rather than reorders. And consolidation cannot launder trust: derived writes carry consolidation_derived as their origin and resolve to the untrusted band by construction, so summarising an observed claim cannot promote it.

The scope model fails closed in an unusual direction. A read or write naming a user_id without a workspace_id is rejected, not narrowed to the user — owner_scope_requires_user_and_workspace — and a supersession whose target sits outside the caller's owner scope comes back as supersedes_target_not_found rather than as a permission error, so supersession cannot be used to probe what exists in another workspace.

The gap is the one its own threat model points at. This system refuses a great deal at admission — poisoned content, unconfirmed external assertions, suspicious entries — and records none of those refusals as a rejected value. The same claim can be presented again and is judged again on its content, with no memory that it was already refused. docs/SECURITY.md cites MINJA and AgentPoison by name and links lhl/agentic-memory for its literature survey; a rejected-value record is the mechanism that literature most directly argues for.

MCP Memory Service demonstrates the pattern it needs, one module away from the place it needs it. Apache-2.0, 3,337 commits from eighty-seven authors since December 2024, 71,120 lines beside 68,820 of tests, over SQLite-vec, Cloudflare, Milvus or a hybrid of them. Its consolidation package is decomposed further than most: associations, clustering, compression, decay, forgetting, insights, belief derivation, contradiction detection and relationship inference each get their own module, under a scheduler and a run tracker.

The belief layer earns trust_state cleanly. A belief lives in its own table with a status of candidate, active or superseded; should_promote requires the confidence to clear a floor and the supporting count to clear a provenance floor; should_supersede demotes when confidence falls back; and get_beliefs reads WHERE status = ? AND confidence >= ? with active as the default. A candidate is excluded, not ranked lower — the distinction the mark turns on — with confidence kept as the separate ordering number.

The memory row skips all of that. When the contradiction detector finds a memory that disagrees with an active belief, quarantine_memory writes {"quarantined": True, "contradicted_belief": ..., "quarantine_reason": ...} into the memory's metadata dict and adds a quarantined tag. The write response tells the caller "⚠️ Memory quarantined: contradicts an active belief." Two MCP tools list and release it. And a search of storage/ and services/ for quarantin returns nothing: no retrieval path reads the flag, so the contradicting memory keeps coming back from ordinary semantic search. The expensive half is built; the one predicate that would make it behaviour is not.

The reason is structural and worth carrying. Every epistemic attribute on a memory lives in Memory.metadata, an untyped Dict[str, Any] serialised into whichever of four backends is configured — so a quarantine filter would have to be written three times against three expression languages. beliefs.status is a column, and filtering it took one WHERE.

Memory is part of the runtime: compiled into context, mutated through first-class actions, tied to agent state. ruflo contributes the one mechanism this family otherwise lacks entirely: a guard on the retrieval path. agentdb-retrieval-guard.ts screens chunks before they are assembled into an agent's context, wrapping the harness's existing tool-output guardrail rather than writing a second pattern library, on the reasoning that a retrieved memory chunk is the same category of untrusted input as tool output. Its header names the attack and cites it — SMSR (arXiv:2606.12703), 93–100% undefended success against 0% behind a certified guard — and it refuses to truncate an oversized chunk because "truncation would let an attacker pad a payload past the guardrail's own scan window", which is the second-order failure most size gates walk into. Then it ships off unless CLAUDE_FLOW_RETRIEVAL_GUARD=true, and annotate-only unless a second variable makes it drop. Three states where the safest is the least likely to be configured, stated candidly in the file. The verdict is also not written back, so a chunk that fails screening is re-scanned on every retrieval and the store never learns that one of its entries is hostile. Letta separates core, archival, and recall memory inside the loop. RainBox routes every belief through one governed write path with a five-actor trust model. MemOS mounts textual, preference, skill, KV-cache, and parametric memory as one cube. Mastra compresses older messages into dated observations and activates them without blocking. TencentDB layers L0 conversation evidence through L3 persona with symbolic tool-output offload. Mercury grades every record on confidence, importance, and durability separately, and keeps a subconscious tier below active recall. Atomic Agent cites numbered invariants from its schema into a design document, keeps a capped vote log beside a score that by default orders eviction and hides profile facts, and carries one mark, negative_eval. GenericAgent governs four file layers with written axioms instead of code. Waku organizes everything around refusing expensive work: a small model decides whether to retrieve at all, consolidation batches, and skill bodies load only on match. LoongFlow carries two unrelated memories in one package — a conventional short/medium/long tier stack, and a population of scored solutions recalled by Boltzmann sampling whose temperature is driven by the population's measured diversity.

Hats answers a question this family usually leaves to the read path: what a self-written memory is allowed to say. Its runtime distils a lesson from each failed run, and assertBehavioural (src/memory/lessons.ts) tests six patterns against the text before it is stored — allow, grant, unlock a tool or profile; disable, bypass, skip a gate or approval; an instruction-override; a path outside the workspace; and an assertion about the state of the configuration — throwing LESSON_REFUSED on a match. The reasoning is committed where the patterns are: a store containing access-widening text "is one refactor away from applying it." Two properties make it more than a filter. The rule document packs/rules/lessons-behavioural-only.md declares enforced_by: memory.lessons.assertBehavioural, and src/registry/loader.ts refuses to load any non-prompt rule whose named enforcement point is not registered, so a guardrail file carries a checkable claim about the code behind it. And the sixth pattern exists because of a dated incident: a run concluded that network egress was off, the user turned it on, and later runs kept refusing to call fetch_url while the tool sat in the allowlist — defended now at three points, two write-time refusals and a line in every system prompt telling the model to prefer the live state over anything it remembers. Against that, its hash-chained audit log records no memory mutation at all, and the memory files are created without the 0600 its own helper takes as an argument — see Hats.

openvurp scopes the memory and not the learning, and the test that proves the first is what hides the second. Each roster agent's remember writes to memory/agents/<id>/vector_memory.db and its turn reads the same store back through a contextvars scope, with a committed case asserting that what one agent remembers the other does not find. But a direct chat with an agent runs Swarm._speak straight to the model, and the hook that turns "hai sbagliato" into a learning event lives only in Agent.run and writes to the platform's unscoped LearningLoop — so the per-agent Mirror that replays corrections as nightly test cases has nothing to replay unless the agent recorded the feedback about itself with learning_feedback, and when it does replay it reads the platform's memory/lessons/ for context whatever the scope. The nightly fade is bound to agent.memory alone, so an agent's store never fades, and both forget methods have no caller. See openvurp.

npcpy is the corpus's most literal answer to "who decides". Extracted memories are written with status = "pending_approval" and a terminal loop walks them one at a time — approve, reject, edit, skip, defer, approve-all — with each decision stamped human-approved or human-rejected. The gate is not advisory: build_context calls get_memories(status="human-approved"), so a candidate nobody has said yes to reaches no prompt, and the row keeps the initial text beside the human-edited final one, which almost nothing else here does. Its failure is the exact inverse of its strength — human-rejected is a status on a row that the extraction path never consults, so the same sentence extracted again arrives as a fresh candidate and the user is asked the same question. A system that goes to the trouble of asking a human throws the answer away when the answer is no.

Juggler makes the opposite storage choice from every other notebook here. Its memory is <project>/.juggler/MEMORY.md and .juggler/ is git-ignored on purpose — private to the checkout, never committed, per-machine — where Basic Memory, claude-mem and TigrimOSR all keep the file somewhere a team could share it and several treat git history as the audit trail. It forgoes that provenance so an assistant's notes about a codebase never reach a colleague's review. Its documentation also states the distinction this atlas's fifth divergence is about: the instructions file is what you write for the assistant, memory is the notes the assistant keeps for itself. The sharp edge is forget, which removes every entry matching a case-insensitive substring and returns no list of what it took.

GitLord is a clear instance of the mechanism the rubric deliberately excludes. Every turn is a git commit, every session a branch, and IndexBuilder regenerates both its JSON index and its Chroma index by walking the log — the log is the authority and the indexes are projections, which is Core Memory's arrangement obtained for free by making the authority a repository. It carries no capability marks and the reason is a category difference rather than a deficiency: it durably records what happened and has no representation of what is believed, so a user's correction and the mistake it corrects are both in the log, in order, with nothing preferring either. Git history is not this atlas's append-only audit column — the rubric says so — and this is the clearest case of why that is a different mechanism rather than a weaker one.

TokenMizer has a status for not knowing, and it is the best answer in the corpus to the problem supersession usually creates. Every other system here resolves a contradiction by picking: the newer decision supersedes, the old row drops out of retrieval, and nothing tells the model there was a disagreement. TokenMizer's contradiction check asks whether the evidence supports that call, and when two decisions share a topic bucket without sharing enough context to call one a replacement — its own example is "Use PostgreSQL for primary user data" against "Use SQLite for the local offline cache" — it marks both CONTESTED rather than "silently guessing and marking one SUPERSEDED — destroying it from resume context on possibly-wrong evidence". The pair is joined by a symmetric CONFLICTS_WITH edge, and CONTESTED is the one status that stays visible in query() and to_context_block() where SUPERSEDED, ARCHIVED and INVALIDATED are hidden — because the point is to put the unresolved pair in front of whoever can settle it.

Its correction record is the richest here too: a DecisionTransition stores "what triggered the change, why the old decision was wrong, what evidence caused the switch, and how confident we are now", in a table deliberately outside the node and edge JSON "so it survives graph pruning". Most systems record that a value was replaced; this records the argument.

And it measures its own extraction, which almost nothing here does. tests/memory_accuracy/test_retention.py runs a synthetic thirty-turn coding session past the extractor against a hand-written ground truth and asserts recall thresholds — 0.4 for tasks, 0.33 for decisions and files. Read those numbers as a disclosure rather than a weakness: this is a project that knows roughly two-thirds of a session's decisions never reach its graph, has written that down where CI enforces it, and has not dressed it up.

DeerFlow has the best-specified memory contract in this atlas, and the narrowest. Three tiers — two abstracts every backend must implement, a management tier defaulting to NotImplementedError, and lifecycle hooks defaulting to no-ops — with the README naming what the tiering replaced: "no more hasattr probing". A noop/ backend ships as the copyable template, and a stated golden rule limits a backend to exactly two channels and one permitted host import. Four backends plug into it, two of which are Mem0 and OpenViking. The cost is that every one must return the default backend's response shape, and the README names the failure: pydantic drops unknown fields silently, so what another system modelled and DeerMem does not simply vanishes. Nothing carrying trust, provenance or status crosses the boundary at any tier. M-flow scores paths where the rest of this family scores nodes. A query anchors on the most precise node it can find — Entity, Facet, FacetPoint or Episode — and an Episode takes the minimum cost over its paths. Each edge costs its own text's vector distance to the query and each hop adds a fixed amount, so only coherent low-cost chains compete. Its stated corollary, "one strong path is enough", is the opposite of the corroboration requirement Graphify and CLIO impose, and is a defensible position for recall rather than belief. The procedural half is weaker than its package layout: the worth-storing screen, the deterministic-first conflict detector and the two-layer retrieval trigger are complete modules that nothing imports, and the live write path asks a model first. Agentic Context Engine is the only system here that records a decision not to act, and consults it. Its deduplicator pairs skills by cosine similarity and asks a model to merge, update or keep them; a KEEP verdict is stored as the pair, the reasoning and the similarity at the time, serialised with the skillbook, and checked in the detector's inner loop before the pair is ever offered again. Two skills that look alike and are not will look alike forever, so without the record every pass re-asks and may answer differently. MemLedger has the most rigorous provenance model in this atlas and does not act on it. Every event names its actor, its cause, the hash of the policy that produced it, and — if derived — the events it derived from, all four enforced by a validator that refuses a malformed event before it reaches the log. A why command returns a fact's creator, sources and history. And the dedup lookup filters status != 'deleted', so a fact the user deleted is re-created on the next extraction rather than refused: the ledger records the deletion perfectly, keyed on the value, terminal in a validated state machine, and the one query that could act on it is written to skip it. Aura is the family's extreme case in both directions. A 1.1-million-line self-hosted runtime whose memory package alone is 26,600 lines across eighty modules, it carries a hash-chained audit — receipts linked by prev_hash, verification that re-hashes the bodies, and sixteen tests for detecting modification, insertion and deletion that passed when run at the report's first pin. It also carries the most complete belief-status machine here (active | trusted | contested, with a resolution API and a refusal to overwrite a trusted belief) in a dictionary that is empty again after a restart, beside a second belief store that persists and has no status field at all. See Aura. Helix AGI is the family's clearest case of a log that records everything except forgetting. Every belief write appends a full snapshot — content, 8-D position, a 384-float embedding — to a journal its own docstring calls "the single source of truth", and the two functions that remove a belief write nothing to it: remove_belief rewrites a category file and clears both runtime indexes, archive_belief sets mass to 0.01. The consequence is not theoretical, because preconscious._resolve_memory_content tries the belief store and then falls back to the journal, so a removed belief whose id still appears in an affect surface or a dangling relations pointer resolves its text out of the log and into the prompt. Two functions that would replay the whole journal back into the manifold are defined and never called. What is worth taking is on the other side of the same file: relation count was removed from a belief's mass under a comment naming the loop it caused — "relations → mass ↑ → gravity ↑ → co-injection → more relations" — which is the reachability-versus-importance failure this report warns about, found in a running system and cut deliberately. See Helix AGI. AIMAOS is the same author's second system and the same memory lineage rewritten, which makes the pair unusually informative about what a year of running one of these teaches. The categories and the nightly consolidation survive; the append-only cognitive journal does not, replaced by a narrative daily entry that is ingested back into memory as a fact. Three of the first system's gaps close by construction: raw conversation chunks become their own category and are exempted from decay under a comment saying that pruning one "silently deletes history no later pass can recover", remove_belief unindexes the relations and template of what it removes, and each office agent's store is a directory built from its own name. What replaces the journal's role in correction is a duplicate detector with a template channel — same phrasing skeleton, shared anchor token, swapped value token — added to catch "contradictions that embeddings place far apart", with a reversal explicitly refused as corroboration. And the new gap is one predicate wide: the superseded wording is kept on the row as previous_content and consulted by nothing, so re-asserting an overwritten value supersedes back.

agent-afk answers the criticism this atlas ends the Helm report on. Helm computes a provisional-confidence cap, earns increases through corroboration, and then formats the surviving facts as - (kind) key: value under "use these, never contradict them" — the number stripped off before the model sees it. agent-afk does the cheap version and does not drop it: a convention fact written without a provenance citation is recalled with an [unverified] marker in the text the model reads, and the write warns. The gate is category-aware — preferences never require file evidence, a learning is not treated as factual codebase knowledge — and supersession has four tested outcomes, including carrying a prior citation forward with a staleness warning when no fresh evidence is supplied. A system that computes trust and ships it as a string has closed the boundary this atlas keeps finding open. The limit is what the string does: applyUnverifiedTag prefixes the content and returns it, so the uncited fact is recalled and ranked like any other and the tag is the whole of the enforcement.

Helm is the family's floor, and it shows how little a working epistemic model costs. One SQLite file opened through Node's built-in node:sqlite, no service, no key required to store anything, 401 lines — and inside them a provisional-confidence cap on any fact the agent thinks it noticed, an evidence counter that is the only thing able to raise belief, decay that retrieval slows rather than resets, and supersession that keeps the row it replaced. It is the cheapest instance in this atlas of evidence before belief and worth reading beside systems a hundred times its size. Its failure is at the boundary rather than in the model: recallMemories formats the surviving facts as - (kind) key: value under the instruction "use these, never contradict them", so the confidence the store worked to earn never reaches the model that consumes it. A system can compute trust carefully and still ship it as an assertion.

Buzz is the family's outlier and the only system here that treats memory as a wire protocol. An engram is a signed, NIP-44-encrypted Nostr event, and the d tag the relay indexes by is HMAC(conversation_key, slug) — so the operator holding the data can read neither its content nor which memory it is, and cannot tell two related slugs apart. Every other private-by-design system in this atlas protects the payload; this is the only one that blinds the index. The price is paid in the same place: engrams are parameterized-replaceable events, so the relay keeps one head per tag and discards what it overwrites. There is no history, no retrieval beyond following [[slug]] references from a core engram, and no model in the loop at all.

Gobii inverts what a memory system decides. Its durable store is a SQLite file the agent designs: no MemoryRecord, no extraction pass, no embeddings — one database per agent, a generated schema prompt capped at 30,000 bytes and 25 tables, and a SQL tool with roughly 12,000 lines of guardrails, autocorrect, recovery and digest around it. The platform mounts its own state as eight double-underscore tables — __messages, __files, __contacts, __agent_config, __agent_schedules, __agent_skills, __tool_results, __kanban_cards — so the agent can join its own data against the platform's, and every one of them is dropped before the file is persisted. What survives is only what the agent created.

The mechanism worth copying is one string per table. BUILTIN_TABLE_NOTES writes each built-in table's mortality into the schema prompt — "built-in, ephemeral (dropped before persistence)", "reset every LLM call" — so the model is told what survives before it chooses where to put something. Every other system here decides the persistence boundary and leaves the model to infer it. Its scope enforcement is also unlike anything else in the corpus: a sqlite3 authorizer denies ATTACH and DETACH so no query can mount another agent's file, alongside load_extension, readfile, writefile and five pragmas — the boundary held by an engine callback rather than a query predicate.

The cost is on the other side of the same decision. A model-authored schema means there is no shape an operator can write against: no tombstone is possible, no trust column exists unless a model invented one, and an erasure request cannot be satisfied generically because the tables differ per agent and were named by an LLM. It is the clearest case in the atlas of deletion being not unimplemented but inexpressible at the platform level.

Cortex asks a question nothing else here asks: may the agent be told this? Every memory carries a sensitivity from public through secret, and memory_search classifies what it is about to return: a supervisor-requiring result runs requestSupervisorDecision and a refusal returns Access denied: <reason>, while a secret result goes to context.approvalGate or requestHumanApproval and a no returns Access denied by human approval. Fifteen other systems in this atlas hold the human-review mark and every one of them reviews a write — approving a memory before storage or editing it after. This one reviews a read, fails closed on refusal, and lets a headless deployment inject its own gate function. It is the closest thing in the corpus to memory access control with a person in the loop, and it is a different axis from the seven columns rather than a stronger score on them: Cortex has machinery for disclosure risk and none at all for epistemic risk — no supersession, no trust state, no tombstone, and consolidation that rewrites.

Beside it sits the sharpest instance of declared-and-unwired in the atlas. src/memory/privacy.ts defines a MemoryPrivacyPolicy with allowedTiers, piiRedaction and a maxRetentionDays defaulting to 90, with setter, getter, redactor and a sensible default, all exported from mod.ts — and nothing calls getPrivacyPolicy. No read consults the allowed tiers, nothing expires at ninety-one days, and the redactPII the pipeline actually runs is a separate duplicate defined in pipeline/builtin.ts. The policies live in a process-local Map, so even wired they would reset to permissive on restart. An auditor reading that file for retention behaviour would draw a guarantee out of it that does not exist. Its tier vocabulary is also the one place tiers are load-bearing, and the tier filter in memory_search carries a NOTE admitting that asking for reflection or graph returns semantic results instead.

OpenHuman stamps a provenance taint on every synced write, and at its current pin nothing turns that taint into a refusal. MemoryTaint labels a note Internal or ExternalSync, fails closed on unknown column values, and survives secret and PII redaction. Its consumer was the subconscious engine, which ran a turn with external memory in context under an origin the approval gate denied external-effect tools for; that module was removed on 22 August 2026, and the gate's deny arm now has no producer, so taint only decides whether auto-recall fences a note as untrusted. The memory engine itself moved into two pinned submodules, tinymemory and tinycortex, behind a MemoryGuard that is the only handle product code holds and that intersects a turn's source allowlist with any explicit scope — on chunk and tree reads, though not on namespace recall.

OpenSRE answers a question three other systems here only patched. The failure is the harness's own output re-entering as evidence, and the atlas has recorded three fixes for it: OpenClaw strips its message envelope, Holographic excludes its host's compaction summaries, Helm stop-lists its own supersession log. All three subtract — they name the strings to remove, and go stale when the harness learns a new phrasing. OpenSRE requires the opposite: an extracted memory typed infrastructure, repository or investigation_learning is refused unless its distinctive tokens intersect with text the user actually typed, computed as a set intersection over a 36-word stop list with no model in the loop. A second regex refuses anything extracted from a transcript containing the product's own sample, demo or benchmark scenarios, so shipped example incidents cannot become a customer's incident history. Both are asserted by committed tests, from both sides — assistant-only infrastructure skipped, user-grounded infrastructure saved.

Two more decisions are worth lifting. One regex module does two jobs: the same patterns that refuse a credential entry to the store also redact the transcript before it reaches the classification provider, with a test asserting a ghp_-shaped token is absent from the prompt — blocking a secret from your disk and blocking it from leaving the machine are different problems, solved here in one place. And memory is off by default on Slack and Telegram, because Slack memory is per-user while Telegram remains host-global; a project that disables its own feature on the surface where its boundary is weakest is rarer than it should be. What it lacks is the other half: forget unlinks the file and records nothing, while extraction re-runs over a thirty-turn window after every turn, so the statement that produced the memory is still in front of the next pass. See OpenSRE.

AuraOS is the family's zero point, and worth keeping in view for that reason. No tests, no licence file: server/main.py reads the core/ identity folder whole, reads the caller's entire transcript whole, splices both in front of the current message, and appends both sides of the exchange afterwards. No extraction, no ranking, no budget, no deletion. Every other system in this family is an answer to a problem this one has not hit yet, which makes it a useful baseline — the version with no retrieval has no retrieval bugs, and the question it cannot answer is what to drop when the context window fills, because nothing measures the prompt. What it does have is the caller naming its own user_id, unvalidated, straight into a file path, with the server bound to 0.0.0.0 and open to every CORS origin by default. See AuraOS.

Muninn is the family's clearest demonstration that governance follows the watcher, not the risk. It keeps two durable tiers in one Postgres database. Extracted memories are written by a background Haiku call that decides worth_remembering and, in the same breath, classifies the row personal or shared — an access-control label assigned by a language model — after which src/db/memories.ts offers no delete and no content update. Drafted wiki pages go through wiki_proposals with a status of draft|approved|applied|rejected| stale|error, a dashboard queue where a person approves or rejects, and an apply-time compare-and-swap that refuses with stale unless sha256(current) === proposal.baseHash. Same repository, same week's engineering; the tier a human was already looking at got the state machine, and the tier that writes about people behind their back got nothing. It is also one of the few systems in this atlas that scores its own retrieval — hit@k, recall@k and MRR over a committed golden set, persisted per run with the per-query breakdown — with the caveat that the memory target is three synthetic rows and three queries written so the lexical arm can match them. See Muninn.

Hestia is the family's only system where background extraction cannot write. Every other runtime here that extracts in the background writes to the store and offers a viewer afterwards; Hestia's note_taker.py puts its proposals in memory/inbox/*.md and a person promotes them with review_notes.py — "nothing becomes part of the brain's live memory until you promote it here, so the brain learns in the open and you stay in control (determinism over intelligence)" — with the autowrite bypass shipped off. The direct tool path is narrow in the same spirit: an out-of-whitelist record type raises, the error goes back to the model to fix, and the test asserts both the exception and that the content is absent afterwards. The cost is that all the judgement sits before the write and none after — no supersession, no rejected-value record, and the novelty check runs against live memory and the queue, so a fact a person deliberately deleted looks new again the next time it is mentioned. See Hestia.

Animus is the family's clearest case of an epistemic state spent on filtering rather than on discounting. An observation carries MemoryState { New, Current, Deprecated } beside a separate weight float, and the state is used to exclude rather than to rank: AppendEpisodic skips a Deprecated row before it can enter the assembled episodic block (src/kernel/context/ActiveMemoryProvider.cpp:258), ListObservationsDueForReview will not put one in front of the model again, and RunPerspectiveRevision refuses to regenerate a layer's narrative when nothing in it is live — the reason written into the comment, that generating from a retired layer makes "the LLM invent narratives from training context rather than reflecting on data". The same file declines to apply the rule to ontology properties, and says so: "Deprecated properties may still be relevant context for the agent." Seven layers named for durations decide when a review fires; the model decides what moves, and a verdict of demote on the bottom layer is a hard DELETE whose audit row keeps the reason and not the text.

It is also the sharpest illustration in this corpus of a scope key declared everywhere and enforced in most of the places it is needed. MemorySearch carries ml.agent_id=? into the SQL on both dialects, and the diary, memory-file and session arms filter too — but the ontology arm carries no agent predicate, ontology_entities is uniquely keyed on (root_category, full_path) with no agent in the key, and ontology_properties.agent_id is NOT NULL DEFAULT 'default' with no writer: the insert binds eight columns and that is not one of them. Agent deletion then runs DELETE FROM ontology_properties WHERE agent_id=?, which removes nothing for an ordinary tenant and every property in the database for an agent whose id is the string default. The Lua bridge is the same gap one layer up: ConsolidationTool states the invariant in a comment — "Agent ID is always from __agent_id (ChainRunner-injected), never from params" — and ToolExecutionService::InjectContext upholds it, but LuaToolProxyCall marshals a script's own table straight into call.arguments and calls handler->Execute(call) without passing through that service, so a script naming whichever agent it likes satisfies the ownership check. See Animus.

RAGFlow is the family's case of memory as a feature inside a much larger product, and it is instructive on both halves. The Memory subsystem is a per-tenant message index beside a RAG engine: an agent turn is stored whole, an LLM splits it into typed children pointing back at the parent, and retrieval is one hybrid weighted sum ordered by recency. The scoping is among the most careful here — _filter_accessible_memories resolves the caller's permitted set before the query is built and returns empty rather than broad when nothing survives, and each backend adapter then overwrites the memory predicate by assignment, so a new caller cannot construct a query that omits it. A committed Go test asks for one memory the caller owns and one owned by somebody else and asserts exactly one comes back. The typing is the opposite story. memory_type is a bit field advertising raw, semantic, episodic and procedural memory; the bits choose which paragraphs enter the extraction prompt, and the type stored on an entry is whatever top-level JSON key the model happened to return — validated against nothing, and read by nothing on the retrieval path. Four typed memories behind one integer, and the integer never reaches a query. Its capacity check is the counterexample to its own looseness: when the forgetting policy is not one it implements, the write is refused — "Memory size reached limit and cannot decide which to delete" — rather than something being evicted arbitrarily. The Go server being ported alongside it validates memory_size and forgetting_policy on write and enforces neither, and the two runtimes do not agree on the ceiling: MEMORY_SIZE_LIMIT is 10 MB in Python and MemorySizeLimit 5 MB in Go, so a memory sized through one API can sit above the other's own maximum. See RAGFlow.

Tradeoff: deeper integration buys behavioural control at the cost of coupling memory to the framework, prompt assembly, and tool loop.

OpenMake LLM is the family's smallest memory inside its largest runtime, and the ratio is the finding. A 112,000-line self-hosted workspace — vLLM behind LiteLLM, sandboxed agents, deep research, MCP tools, native clients — keeps one table of up to fifty sentences per user and injects the newest fifty into every system prompt under a 2,000-token cap, after the static blocks at a boundary the assembler reserves for per-user content; a person types them into a settings tab, or two extractors that both default to off form them from the user's messages, one by regex and one by a model call per turn whose output is kept only when it is phrased as the user…. A stored preference that the client can tighten but not loosen gates injection and formation on every path, a delete leaves a row the extractors and the backfill read back as a tombstone, and the tab's writes land in the platform's audit table. The table's own migrations record a predecessor with keys and importance being dropped with six rows of user data and reintroduced a week later as "explicit only, zero vLLM load", and that predecessor is the shape the data export was written against: its query names four columns the table has not had since May 2026, the helper around it swallows the error, and a person's export has never contained a memory. The extractors' rows carry no audit entry, the prompt block and the backfill have no test, and the tombstone reaches the newest 500 rows.

ELAI is the family's most literature-complete memory and its least wired, and the archive says so itself. An abandoned Rust harness, privacy-filtered and published on 5 September 2026, carries an 11,000-line memory crate built plan by plan from fourteen papers: a bi-temporal SQLite fact table with a non-empty evidence list enforced at insert, per-type trust decay with half-lives from 69 days to 19 years and a per-type retrieval floor, a four-way Mem0-style dispatcher, a regex and credential gate before every write, a tier-driven and NLI-judged contradiction pass that closes the older row at the newer one's start, fail-closed citation re-resolution for code-grounded facts, and a compile-time role firewall under which only the executor and worker roles ever receive a fact and a new role cannot be added without a decision. What writes a fact on a live path is a person typing /remember behind an experiment flag that expired four days before the archive was created, or the admin insert command; the every-fourth-turn extractor counts its candidates and stores none, the safety-critical tier ceiling the prompt-injection test defends is set by nothing outside tests, the write quarantine is a Vec nobody constructs, and the decay scheduler is never spawned. The staleness instrument is the part worth copying whole: a four-case supersession fixture run through the real filter and through the same filter minus its one live-row predicate, both results committed side by side.

Argos is the family's most complete review-then-remember implementation, and it was built against this atlas's rubric. A Hermes plugin with a shared service behind it — DuckDB records, a Kùzu entity graph, local BGE embeddings, an MCP and REST facade — where every fact an extractor finds is a proposal on a seven-state ladder whose top rung the automatic reviewer cannot write: the storage layer raises on it, downgrades an external-origin approval to confirmation, and caps promotion at what the record's grounding label allows. Deletion writes a tombstone keyed on the normalised content and rejection a ledger row keyed on the claim slot, and both the direct write and the proposal path consult both before anything lands. Versions chain with valid_from set to the in-world time and as_of reads; scope runs from tenant cells through user, project and namespace to a per-document access class with deny over allow; an erase request writes a receipt in the same transaction as the delete. Seven marks. Its claims audit maps every README number to a committed judged file — the 89.8 % and 70.4 % on LongMemEval recompute exactly — and records that those runs ingested with dedup off into a fresh store per question and formed no version chain, so the numbers measure retrieval plus an answerer and not the supersession the marks are for. The repository names this atlas as its trust model's reference design and ships the atlas's contradiction test as a parametrised suite with an empty-store control; the marks are read from the code.

OpenMasq is the family's memory for a product whose premise is that the model never sees real data, and the interesting decisions are all about where the leaks would be. A redacting desktop chat client — on-device NER replaces names, organisations and numbers with believable fakes before any network call and a per-conversation vault restores them in the reply — whose Mémoire is one card per entity and a preferences profile, stored in the clear on the machine because a fake is no longer stable across conversations since a per-conversation salt was introduced. Extraction reads the wire, the redacted replay the model already received, answers in fakes and is un-redacted locally through the vault, so no new byte leaves; the vault then doubles as the hallucination filter, because an entity must appear verbatim in the real text or is dropped, and a value present only on the wire is refused as an unresolved pseudonym rather than kept as a note. Selection is a deterministic cascade on real values — mention, presence in the conversation's vault, a distinctive token, then one hop along cards whose facts name a certainly-mentioned entity, under a 4,000-character budget — run before the user's message is redacted so the selected names are forced into the vault and map to the same fake in the block and the text even under the regex engine; memory_search un-redacts the model's query and re-redacts the result. A card updates rather than stacks: a deadline, a budget or a contact replaces the sentence carrying the old one, a restatement keeps the richer wording, and what was removed goes into a three-deep history a person can restore, with the reason written in the type's comment — "a consolidation that overwrites its evidence in silence is the measured failure mode of agent memories." The thresholds carry their measurements — 0.92 for clustering with a margin of about ±0.006, 0.95 between two people, 0.88 for search — and the forced list is filtered for lexicon words, sentence fragments and notorious brands because each had produced a named bug. What it lacks is any state that withholds: reviewedAt and source: "auto" feed an inbox that empties by confirming and nothing on a read path, every card in scope reaches the prompt, deletion leaves no record, and the card's only time is its last update, injected as a date the model is asked to reason from. One mark, negative_eval. The inbox is a to-do list, and a card that never gets confirmed is injected all the same, so it is not a review surface.

Khoj is the family's smallest fact store, and its limit is one argument to one function. A self-hostable personal AI over Postgres — documents indexed with pgvector, custom agents, scheduled automations — whose long-term memory since 3 January 2026 is a UserMemory row per fact: one first-person sentence, an embedding from the same bi-encoder that embeds documents, a user and an optional agent. After every non-automated turn a background task hands the last two exchanges and the facts recall retrieved for that turn to a prompt that introduces itself as Muninn and may answer with sentences to create and ids to delete; deletes are hard, updates are forbidden by the prompt and implemented by the API as a delete and an insert under a new id. Recall is two arms merged by id — the ten most recent facts of the last seven days and the ten nearest by cosine under the search model's confidence threshold — injected as a dated list the model is told to ignore when irrelevant, scoped by user always and by agent when the conversation runs under a custom one, with the default agent reading every agent's facts. The isolation tests seed the facts that must stay out and assert on their text, and the server mode of disabled, default-off or default-on over a per-user switch is tested in every combination. The argument is memories=relevant_memories: the extractor's existing facts are the retrieved set, not the store, so a fact the query did not surface cannot be retired and two facts that contradict each other coexist until one conversation pulls both; the recency arm is what keeps a fresh fact contradictable for a week. A fact has no state, no provenance to a conversation, no confidence and no record when deleted; a settings list edits and deletes live facts and adjudicates nothing. Two marks, both on the scope.

AnythingLLM is the family's most bounded memory, and its constants are its design. A self-hosted chat-over-documents application whose personalisation memory, merged 19 May 2026, is a memories table of one-sentence facts scoped to a workspace or global and capped at twenty and five. A job every three hours takes each idle user's last twenty chats through two tool-calling agents — an observer that must submit at most three candidates with a confidence and a reason, and a reflector that sees every existing row and the free slots and must answer with a scope and an action of create, update or skip — applies the result in one transaction under the caps, and marks the chats processed in a finally, so a run that threw consumes its chats for good. Every chat's system prompt then carries the global facts and the five workspace facts an on-device reranker puts closest to the message and the last three turns, with no threshold, no tool and no query. User and workspace are WHERE clauses on both readers, which is the mark it earns; lastUsedAt is stamped on every injection and read by nothing; and the embed widget calls the prompt builder with a username where it expects a user, so in single-user mode — where every row has a null user — the owner's facts are appended to anonymous visitors' chats whenever memory is on. Beside it the agent's older rag-memory tool still stores free text as a document in the workspace's vector database, where nothing lists it as memory. One mark.

Memos is the family's note service, and its one mechanism is a predicate. A self-hosted Markdown memo store — one text box, tags, a timeline, in development since December 2021 — whose memos carry a creator, a visibility of PRIVATE, PROTECTED, PUBLIC or SPACE, and an optional space, resolved from the caller into a MemoAccessScope that the storage driver renders as one WHERE clause appended before LIMIT on every list and count, so a memo the caller may not read is neither returned nor counted nor paginated over, with an unknown visibility or a missing space denying (store/db/sqlite/memo_access.go:12-40). A two-user store test asserts the viewer's list by exact id set with the owner's private rows absent, which earns scope_enforced and negative_eval. Agents reach it through a stateless streamable-HTTP MCP server that is an allowlist of twenty REST operations built from the embedded OpenAPI document, with the caller's bearer token forwarded unchanged, so an agent's rights are its token's and there is no second policy. A memo is a note: no trust state, no provenance beyond creator_id, no history, a CEL filter with contains as its only text operator and no index, a hard delete by the creator only, and a PROTECTED audience that means every logged-in user.

HUMANs is the family's most literal answer to what a model should be shown, and the answer is nothing it did not just hear. A persistent local agent — Apache-2.0, three commits on 7 September 2026 by one author, 19,689 lines around one SQLite file — whose canonical records are immutable by two database triggers, corrected only by a new record that supersedes the old; whose speech model receives the current utterance and a line of state numbers, with a test asserting the previous event's text is absent from the next call; and whose stored text reaches an answer only when the mind selects an explicit LOOK, scans the heard records, and returns the result through a receipt that code renders and the model never sees. Only a heard event may form language memory — a tool return or a notification is embedded as an opaque payload under its lane — and the committed test seeds three records on one concept, asserts the seen and noticed passwords are absent from every retrieval surface, and asserts the heard phrase is present in the same test. Two marks, the record store and that test. The finding is a producer: the library's remember takes a supersedes_id, the demo and the tests pass one, and the command line's /remember deduplicates by exact text and never corrects, so the shipped mind can add a fact and cannot retire one. No committed benchmark artifact; the whitepaper hashes files Git ignores and tables what it has not demonstrated, including that episodic facts cross to the model at all.

Khabeer is the family's port, and a port is a test of which properties were understood. An Android app built on Termux, with a Java agent runtime inside it whose memory specification says in its first line that it "mirrors the Hermes Agent memory system" — two character-capped Markdown files injected whole, substring-addressed edits, final-state budget checks, an NFKC threat scan, a drift guard, and Hermes's staged write-approval queue, surfaced as an Approve/Reject list on the app's Memory page. Everything mechanical came across. The property Hermes is built around did not: the spec requires the memory block frozen at session start so a write cannot invalidate the prompt cache, and every provider request builder calls systemInstructions(), which re-reads the files from disk — on the Anthropic path once per tool step. The load-time [BLOCKED] fence was ported and the test that earns Hermes its negative_eval mark was not, and a rejected staged write is deleted without a record, so the background review can propose it again ten turns later.

AgentOS (Tanglies) is the family's plainest global memory, and it says so. A FastAPI agent platform at v0.1, fourteen commits on one day: one SQLite table the model writes with a remember tool, keyword recall with Chinese bigrams because FTS5 will not tokenise two-character Chinese words, and an automatic recall on by default that places the matches in the system prompt. The module's own table calls long-term memory shared across sessions, and it is — session_id is written as provenance and never read. With fetch_url always registered, that makes the store a path from any page the agent reads into the system prompt of every later conversation. It carries no mark.

Nuum is the family's memory designed around the prompt cache, and the cache is where it leaks. A local-first Electron desktop for persistent agents, four commits by one author in September 2026, whose memory is two kinds of Markdown file per agent — standing facts in profile.md, dated facts and notes in monthly logs — written by an update_state tool and by a tool-less extractor after every completed, non-small-talk turn, and deduplicated on normalised text across tiers. The rendered section — the first hundred standing facts, and dated lines ranked by tier and a thirty-day recency term under 4,000 characters — is frozen per epoch so the system-prompt prefix stays byte-identical, a stricter cousin of Hermes's session snapshot. An explicit write bumps the epoch and extraction deliberately does not, which a committed test pins; the consequence is that a fact the extractor removes stays in the prompt until the next compaction or explicit write. The extractor is never shown the memory it may contradict, so its remove: lines match only by exact wording, and a removed value leaves no record and can be extracted again. It carries no mark.

AgentRT is the family's C runtime, and its memory daemon reads recency off a position that its own delete destroys. A C11 "OS-grade runtime substrate" at version 0.1.16 whose primary home is atomgit.com and whose GitHub tree is a superproject of seven submodules; memory is mem_d, a standalone daemon of 8,927 lines serving a mem.* JSON-RPC namespace over a Unix socket with its own hash table, its own TF-IDF, one line-delimited JSON file and no database. The README names atoms as the home of memory and openairymax/atoms returns 404 on GitHub, but the daemon is in daemons, which is published, and was read at the pin the superproject records. Its context ledger is the good half: every window entry — system prompt, tool definition, message, tool result, compression block, cache hit — is appended per session with its token cost and a status of ACTIVE, EVICTED, COMPRESSED or DEDUPED, the window read skips anything not ACTIVE, and a transition appends a record carrying a sequence number, a nanosecond stamp and a back-reference, so the sequence replays. The record store is the other half. mem.delete compacts the array by swapping the tail into the hole; mem.recent derives newest-first from array position, so one delete scrambles the order permanently, and the created_at it returns on every item is never sorted by — the function has no test. mem.evolve concatenates its search hits into a new record and retires none of them, so the merged record out-scores its own sources on the query that produced it, against a fixed ceiling that refuses writes rather than evicting. Restarting under a lower ceiling keeps the oldest records and drops the newest without a log. Two marks, both away from the record store: the ledger's status filter, and a knowledge-base isolation test that asserts the negative beside its positive control.

Inno Agent has the family's best-argued evidence model, and two tools that route around it. An MIT personal learning agent built on the Pi coding-agent SDK without modifying its kernel, 84,072 lines across 90 test files, organising memory into an L1 learner profile, an L2 Markdown wiki and L3 session records in SQLite FTS5. L1's atom is a piece of typed evidence: eight kinds weighted from exposure at exactly 0 through free_recall at 0.75 to transfer at 1, multiplied by a hint-level factor, the evaluator's confidence and a spacing factor that pays more for a week-delayed success than a five-minute one — and a guard that clamps the optional numeric score into the band its categorical result allows, because "a model can accidentally emit contradictory fields… so malformed evidence can never invert the learning signal". Mastery is never stored as truth; it is projected from that evidence on every read. The misconception status is the one genuinely stored gate, and it is strict: a correct answer on the concept does not clear a blocker, only evidence explicitly carrying its misconception_id, from a retrieval-kind interaction at hint level 0 or 1 with an evaluator at least 0.7 confident, and a later linked failure reinstates it. Then patch_learner_profile writes an absolute mastery, a free-text diagnosis and an evidence_ids_append list that nothing resolves against the event log, and update_learner_profile submits whole knowledge-state objects including the transfer counter that the stable label depends on; neither appends to events.jsonl, so the append-only record does not cover the profile's own write paths. evidence_ids is meanwhile doing three jobs — provenance, a confidence ceiling raised from 0.35 to 0.6 when it is non-empty, and the dedup set that makes matching real evidence be skipped — while mixing two id namespaces written by two code paths. The careful gate produces repairing, and both read filters test only active. Two marks: the misconception status, and a test asserting the assistant's thinking blocks never enter the session index. The learner's own panel, which edits mastery and deletes goals, writes straight into the profile the next turn reads, so it earns no review mark.

BitterBot Desktop implements the biology it names, and carries two lifecycle columns whose mappings are not inverses. An MIT local-first personal agent at version 2026.2.15 — 740,633 lines of TypeScript across 1,382 test files, of which 127,873 lines and 156 test files sit under src/memory, alongside the skills-marketplace code that shares the directory. Synaptic tagging, reconsolidation, the spacing effect, somatic markers and hormonal scalars are each implemented with a citation rather than gestured at, and a dream engine grades its own cycles by whether their output is later retrieved. Two mechanisms are worth the visit. The SABM belief layer on the knowledge graph is properly bitemporal: relationships carry valid_from and valid_until distinct from the created_at rows are ordered by, supersession closes the interval instead of deleting, and beliefHistory(entityId, { validAt }) deliberately drops the active-only guard every other read applies so a caller can "answer 'what did I believe about X as of T?'" — with a test asserting both that a closed edge is absent from ordinary traversal and present in the history. The canonical ledger names a failure mode most retrieval-gated stores have and nobody states: "importance is orthogonal to similarity: a canonical fact is short, low-entropy, and shares no embedding mass with a cold conversation's first message", so it is addressed by key, injected unconditionally, hard-capped, and demoted by a deterministic score "never an LLM prose decision". The finding is in the chunk store. chunks carries both lifecycle_state and a newer lifecycle; the code knows — chunk-writer.ts heads the section "the tangled cluster the audit's C1 bug lived in" and designates setChunkLifecycle as "the single place that reconciles the two columns". The write side is fixed and a read is not: skill-version-resolver.ts filters both version queries on lifecycle_state != 'expired', and expired belongs to the other column — deriveLifecycleState maps it to archived — so the predicate never excludes an expired skill, while SQL's three-valued logic makes it exclude every row whose lifecycle_state is NULL. The two mappings disagree in the other direction too: the migration maps forgotten to expired and the reconciler maps expired back to archived. Three marks: the lifecycle and canonical statuses, the as-of belief read, and the test that asserts both halves.

SageOx CLI makes a team's memory a git repository, and when two agents' records conflict the third tier is an LLM. An MIT Go CLI for human-agent teams — 651,919 lines across 1,337 test files — whose ledger is a git repo the cloud provisions and the CLI clones sparsely, holding MEMORY.md beside memory/daily, weekly and monthly. Priming does not inject any of it: an agent gets MEMORY.md and a catalogue with file counts under a heading called Progressive Disclosure, then reads what it wants with ordinary file tools — a different answer from retrieve-and-inject, and free at prompt time. Its auto-resolve rule is the best-documented engineering judgement in this corpus: the comment names the failure ("a deterministic wedge that never escalates and never self-heals"), quantifies it — "[o]ne ledger sat 341 ahead / 1055 behind for 13 days with 281 such conflicts" — justifies the scope one artifact at a time by naming each one's canonical source elsewhere, states the trade ("[a]n imperfect summary beats a ledger that can never sync again"), and ends its SAFETY note "[d]o not weaken that guard". The paths it lets accept-theirs resolve are data/ and sessions/, the regenerable ones; memory content is deliberately not among them, which means a memory conflict falls to tier three — an LLM, restricted to claude, gemini or codex by an allowlist whose argv[0] substitution threat is spelled out, bounded at sixty seconds a file, and asked to "[p]reserve user intent on both sides". Its only post-condition is that no conflict markers remain, so a merge that drops or paraphrases one side's record passes. Distillation — the step that turns observations into the summaries agents read — is a POST to the SageOx API; the local pipeline that did it was removed on 9 September 2026 and its spec is marked superseded. Fact categories are write-time genres, nothing marks a fact superseded or withdrawn, and WriteFacts truncates rather than appends. No marks.

Syke brackets every unsupervised LLM rewrite of its graph, and the one number that would bound the damage is computed and unread. An AGPL-3.0 local memory agent — 32,408 lines of Python with 11,539 of tests — that runs as an ambient daemon, watches sessions across eight harnesses through shipped adapters, and serves one MEMEX.md projection plus syke ask / record / memex. Its schema is four small tables: a memory is prose with a created-at, a link carries a free-text reason with ON DELETE RESTRICT on both endpoints, and the identity and current MEMEX are singletons enforced by primary-key CHECKs. The engineering is in what surrounds the synthesis cycle. capture_baseline fingerprints every memory and link before the LLM runs; create_recovery_point clones the database — copy-on-write where the filesystem allows, SQLite backup otherwise — and integrity-checks the clone before trusting it; a lock and a recovery fence guard concurrency, and an interrupted cycle is reconciled before the database is used again. Then validate_state_after_cycle gates the result on invariants worth copying: a pre-existing memory's, link's or MEMEX's created_at may not change — the agent may revise what it believes, not when it first knew it — no link may reference a missing memory, the identity must stay a singleton with no rows outside it, the FTS index must match the memories table, and exactly one non-empty MEMEX must survive; a failure is marked repairable and retried. Inside that same function it computes memories_removed and removed_memory_ids, appends no issue for them, and neither identifier appears anywhere else in the package — the synthesis backend branches only on valid, which comes from issues. A cycle that deletes most of the graph passes. The snapshot means the state is restorable; nothing notices it should be restored. The per-cycle graph change set is likewise assembled and never persisted, so the immutable, receipt-linked history covers the rendered projection rather than the mutations behind it. No marks.

sivtr indexes the shape of your secrets and not the secrets. An Apache-2.0 Rust memory space — 74,339 lines with 768 test functions, plus a CLI, a VS Code extension, an MCP surface and a packaged skill — built on the premise that the memory already exists: terminal failures, test output, tool logs and prior agent transcripts are synced from disk into a SQLite archive and made searchable, rather than written down by hand. Capturing a terminal has an obvious hazard, and the handling is the reason to read it. privacy.rs holds nine credential patterns under a header that refuses to oversell them — it "deliberately only removes high-signal credential formats" and is "a reduction in accidental disclosure, not a security boundary: callers must still ask the user to review the resulting snapshot before publishing." Its scan returns two things, and the two paths use opposite halves: on egress, publication.rs and the remote path keep the redacted text; on ingest, replace_secret_findings throws the redacted text away and keeps only the report, writing a secret_findings row of kind and occurrence count — so the archive records that a session holds three GitHub-token-shaped strings without storing a copy, while the raw record stays local, because a log with its credentials blanked out is often the log you needed. The same discipline runs through the schema comments, which are mostly about what is deliberately not stored: costs "are NOT stored: they are computed at read time from the embedded pricing snapshot, so a pricing refresh re-prices history without touching these rows", the record kind "is not stored: it derives from the record ref", and every record is held twice so a listing never pays for part text. The gap is reproducibility: search/eval.rs is a real IR harness whose stated purpose is to gate ranking changes "on measurable improvement over a fixed baseline instead of feel", and no golden-query file or frozen corpus is committed anywhere in the tree, so the baseline is each user's own machine; a GoldenQuery also labels only what should surface, never what must not. No marks — a record here is an observation with an exit code, not a claim that can later be wrong.

Chump ablated its own memory and published the null. A dual-licensed AGPL/Apache multi-agent fleet coordinator — 326,308 lines of Rust with 4,071 test functions — whose memory is an unremarkable SQLite table with an FTS5 mirror, a confidence float, a verified flag that exempts a row from decay, and no scope key, validity interval, supersession pointer or mutation record. It carries no marks, and that is not why it is here. Chump shipped CHUMP_BYPASS_SPAWN_LESSONS — an environment flag whose only purpose is to turn its own spawn-time memory injection off — and ran a binary A/B: EVAL-056-memory-ablation.md records "n=30/cell binary-mode sweep; NO SIGNAL (CIs fully overlapping)". The flag is one of a family beside bypass_perception, bypass_neuromod and bypass_blackboard, and two sibling ablations report nulls too. It then checked a second, independent way — whether the agent ever textually references the injected state — and reports that "[a]ll 5 NULL-validated modules show ≤1% reference rate in agent text output… All below the preregistered 5% mechanistic-support threshold", with the addendum careful that it "updates rationales, not actions". The public methodology requires Wilson confidence intervals, a preregistration per gap, and an A/A run per series within ±0.03 before any result may be cited — controls that were in place before the nulls, which is what makes them credible. The counterweight is that thirty-nine of the eighty-three eval documents are now stubs reading "moved to a private repository", under a directive binding on every contributor: "Do not state magnitudes, model names, or per-eval IDs in public docs, PRs, or external communications." The method stayed public and the results mostly did not, so the four surviving nulls are what the migration left behind rather than a representative sample. Almost every system in this corpus asserts its memory helps; this is the one that tested the claim against a bypass and wrote down that it did not.

Bifrost tells the model it is searching its absolute long-term memory, and keys the file to one session. An AGPL-3.0 Rust agent runtime orchestrator — 11,091 lines with 121 test functions — whose entire memory layer is 148 lines over Memvid: a manager whose scoping decision is one format!("agent_{}_session_{}.mv2", agent_id, session_id), and a search tool whose description reads "Search your absolute long-term memory for past conversations, facts, or context you have stored using this tool." Because the session is in the filename, past conversations means earlier turns of the present one, and a new session opens an empty file. The exception is the fallback: both the write and the read use session_id.unwrap_or("anon"), so a tenant's session-less turns pool into one shared file — the only configuration in which the description is accurate, reached by the absence of an identifier rather than a decision. The good half is that the two ends agree: the search tool and the commit are constructed from the same tenant-and-session pair a few lines apart, so there is no asymmetry between the write key and the read key, and a commit failure is logged and swallowed so a memory write cannot fail a user's turn. No marks: scope is a filename rather than a predicate, each turn appends the query and answer concatenated with no de-duplication, and the layer exposes no delete, supersede or retire path at all.

Hypha filters on a flag nothing sets. An Apache-2.0 TypeScript agent framework — 148,121 non-test lines across seventeen packages, with a 40,651-line memory subsystem of its own — whose managed record is one of the richest contracts here: an eight-value status, a five-value visibility, a structured scope and its SHA-256 hash, a content hash, four separate unit-interval scores, typed relations, a seven-value index state kept deliberately apart from the trust state, and the flags immutable, sensitive and humanVerified. Three marks, and the read gate earns two of them honestly: record.scopeHash !== hashMemoryScope(request.query.scope) is the first test, before status, expiry or any caller filter, and four of the eight statuses withhold a record rather than ranking it down. The third is a case that adds two records, asserts a retrieve returns both, invalidates one and asserts the same retrieve returns only the other — an exclusion measured against a result proven populated in the same test. What makes it worth reading is the fourth mark, withheld: humanVerified appears six times in the whole repository — the schema, a verifiedOnly retrieval filter, the same filter in the managed store, a ranking feature scored one-or-zero, and a conflict check — and never once on the left of an assignment, in source or in tests. The filter returns the empty set by construction and the score is always zero. Somebody worked out what a verified memory should mean for retrieval and for ranking, wrote both ends, and never built the verifying.

AI Maestro consolidates every night into a store nothing reads back. An MIT-licensed Next.js orchestrator for a fleet of coding agents on one host — 133,039 lines of TypeScript, 1,103 commits — that gives each agent its own CozoDB file and a complete biological-tier memory over it: raw messages as short-term, a 02:00 LLM pass that distils them into six categories split across a knowledge and a reasoning system, near-duplicates reinforcing instead of multiplying, and promotion from warm to long on reinforcement count and age. The write half is wired end to end and runs on a schedule. buildMemoryContext — the function that searches those memories, adds the top preferences and patterns, and returns a markdown block for an LLM prompt — has one caller in the repository, a view === 'context' branch of an HTTP endpoint, and nothing in the tree ever requests that view. One mark: agent_id is bound as a predicate on every long-term read, though the boundary that separates two agents is the per-agent file the query runs against, and the graph-expansion path has already dropped the predicate. Two documented zero-valued switches are swallowed by || defaults, which makes retentionDays: 0 — the way the API says to disable pruning — a request that deletes thirty days of messages.

Cua records a person doing the task and lets a vision model write the procedure, keeping the recording underneath so the prose can be re-derived. An MIT computer-use agent platform — Swift virtualization, a Rust input driver, a sandbox fleet, a benchmark suite — whose durable memory is a small corner of it and is not where the filenames point: pipeline/shared_memory.py is interprocess buffering, callbacks/image_retention.py trims screenshots out of the live context, and trajectory_saver.py writes runs nothing reads back. The memory is cua skills, described in its own docstring as "recorded demonstrations that can guide agent behavior". A person records a screen session; ffmpeg cuts one frame per input event; a vision model captions each into observation, think, action, expectation; and the result is a SKILL.md with a section per step beside the video, the raw events and the frames. Two things generalise. The fourth caption field is a postcondition — three of them describe what happened and Expectation states what should follow, which is what makes a step checkable against the screen rather than merely repeatable. And the raw input is written before the first model call and kept beside every caption, so a generated memory retains the source it was compressed from. No marks: frontmatter is a name and a description with no status, the one timestamp lives a level down in trajectory.json, deletion is removal with nothing recording what was removed, and one global directory per machine means scoping is neither a key nor a predicate — skills.py contains no project_id, workspace, scope, tenant or user_id at all, which is a cleaner absence than a scope stored and ignored. human_review is the near-miss worth naming: a person performs the demonstration and types the name and description, and is never shown the captions that become the memory — cua skills replay opens the source video, not the prose. Retrieval does not exist: three MCP tools, a listing that omits the description, and a model choosing a name. The risk that compounds with what this platform is for is that captioning a demonstration turns whatever was on screen into durable instruction text, and a computer-use agent's screen is untrusted input.

Off Grid AI Desktop filters chats and documents by project and leaves its memory unscoped. It is the AGPL core of an on-device assistant: chats, project documents and an LLM-filtered memory and entity layer in one encrypted SQLite file. The project predicates and their CI-run exclusion tests are careful, and they guard verbatim turns and uploaded files, not memory. Captured memories carry no project key and join every project knowledge base by default, while the prompt forbids other projects' information. Their producers are a retired feature or the private pro/ submodule, so the memory the product advertises cannot be read, and the report carries no marks.

WARNERCO Schematica is the family's teaching specimen of CoALA's four tiers, and what it teaches by accident is the gap between a tier and a belief. The flagship app of a course on context engineering with MCP puts a scratchpad and an episodic log in SQLite, schematics in JSON mirrored to Chroma, and procedural memory in five MCP prompts, behind a nine-node LangGraph pipeline that logs every turn. Episodic recall returns a per-event recency, importance and relevance breakdown. Weaker is what reaches the prompt: an LLM's expansion of each scratchpad note, or its first 75% of words without an OpenAI key. Consolidation appends draft facts that no read filters, and repeats them on every run.

Living Mind Cortex is the family's organism runtime, and its memory promotes whatever nobody reads. An archived FastAPI service with a hormone bus, a circadian clock and LLM dreams stores memories in one Postgres table and mirrors each into a RAM-only heat-equation substrate. Nothing in the service wires edges, so diffusion and fusion never run: every node cools, and eight cold ticks later its text returns as a [CRYSTAL] row at importance 0.90 tagged identity, heartbeat lines included. Recall writes on read, decaying confidence 5 percent per hit, and the phase-vector arm skips the caller's filters. Eight of fifteen tables have no writer, and no test touches memory. No mark; CSM polls its state route.

MAMA routes every memory change through one append, and lets the agent decide which appends are corrections. MIT, a local owner agent (MAMA OS) and a Claude Code plugin on a shared SQLite engine. A memory is a judgment row the agent writes through memory.save. It is never edited: a successor supersedes it, or memory.retire appends a stale or superseded amendment with a reason. Each append binds scopes, writes a memory_events row and an idempotent receipt in one transaction, and catalog reads fail closed on an empty grant. What is weak is where the filters stop. Recall re-attaches every superseded predecessor's summary as [Prior context]. Standing corrections can be saved from turns driven by connector content, while timezone changes cannot.

Prismer Cloud publishes the replica and keeps the authority. The repository is the SDK mirror of a closed agent platform; its daemon holds a per-workspace SQLite copy of the platform's memory wiki, answers recall from it with FTS5 and a link-graph band, and runs post-turn extraction inside the agent's own pod. The access design is the strongest part: a daemon-signed capability fixes the acting agent and workspace, and one visibility function gates search, list, load, browse and injected recall. It stops short twice. The hook intake takes the agent's identity from the request body with no capability, and the turn-start digest writes every hub's path and summary into each system prompt unfiltered, which withholds the scope mark. Supersede, merge, delete and consolidation forward to the server, which is not in the tree.

Octop Memory is a candidate-gated fact store whose review queue has three resolvers that disagree. A Python runtime for OpenClaw and Hermes, on SQLite or PostgreSQL: turn transcripts become raw events, an LLM extracts candidates, and five rule checks drop, merge, park or promote each one into an atom that cites its raw event ids. The status column is out of the model's reach — the parser writes pending as a literal and the extractor's recommended action is never read. The far end is weak. The JSON-RPC approve supersedes a contradicted atom, the CLI approve leaves both live, and the web dashboard's approve writes no atom at all. The opt-in vector arm still returns deprecated atoms.

Cyrene Agent checks evidence quotes in code on one store and gates its profile on one writer. A Live2D desktop companion whose memory is a JSON profile and LLM-extracted episodes, plus an opt-in per-conversation atom store. The atom extractor keeps a fact only when its quote is a verbatim substring of a user turn, and applies a batch only when every operation validates. Episode recall filters on status read from the store, not the index. The rest is looser: a write_memory tool and a profile-only reflection pass write the profile past the judge's explicit-user rule, superseded episodes reach voice-call prompts, decay archives recalled episodes and spares unused ones, and channel senders write the desktop user's profile. Four marks.

AutoBot spreads one platform's memory over six stores, and the filters live in the stores rather than in the prompt. A self-hosted AI platform whose knowledge-base facts sit in Postgres with Redis and ChromaDB projections, beside chat trajectories, verbatim turns, a Redis entity graph, working memory and an SQLite store. The per-store work is strong: an owner argument the SQLite store cannot be called without, a user-and-tenant predicate with a backstop on trajectories, and a web-research quarantine only a corroboration gate lifts. The composition is weak. The always-loaded Essential Context block reads 200 arbitrary facts with no owner or quarantine filter into every chat prompt. The per-turn fact extraction calls a method no class defines. Forget-everywhere omits the fact store.

Flowly puts an enforced lifecycle on top of memory its tools have already written. A self-hosted personal agent whose governance store gives each MEMORY.md note and knowledge-graph triple a six-value status, an audit row per transition and a needs_review queue that only the CLI, TUI and desktop RPC resolve. Consolidation lets an LLM propose merges that a validator applies, and imports cannot activate themselves. The weakness is ordering: memory_append and knowledge_graph add write before the hook governs, and the prompt injects the whole file and graph summary, so review and reject do not withhold a live write. Subagent writes also carry an empty session id and skip the automation gate.

Host runtimes with pluggable memory

Hosts: hermes-agent, openclaw, pi, mateclaw, opencode, nemoclaw, tigrimosr, adk-python, autogen, agno, agent-framework, dexto, cognis, gh-aw, smythos-sre, bytechef, outworked, memorax-code, nanoclaw, neuralmind, dsh-mnemon, yantrikdb-hermes-plugin, goodmemory, memorix, loreai, memtomem, tracedecay, titen, llm-memory-api, demarkus, people-context, state-memory-mcp, the-librarian, akb, jaz, oh-my-hermes, yantrik-os, neoth, strands-agents Plugins mounted on them: holographic, magic-context, metaclaw, byterover, tencentdb-agent-memory, plur1bus, scope-recall-hermes, dsh-ai-memory, skillcorpus, plus hosted providers, dsh-layered-memory, dsh-continual-harness

The runtime ships an interface, not a memory model. Hermes bounds its own curated Markdown hard and freezes it into the prompt at session start while mounting one external provider. TigrimOSR is the exception that proves the family's rule: its own memory is one memory.md per project, and the mechanism worth copying is beside it — a skill synthesizer that stages a proposed skill as SKILL.md.proposed next to the live file, keeps the rationale and the sessions it came from, waits for a person, and promotes by rename. It also forces review when the target skill was authored by a human rather than by the automation, which nothing else here does. OpenClaw ships memory entirely as extensions over a plugin contract. Pi is the limit case: twenty-plus lifecycle events and no memory concept at all, so plugins rebuild indexing, scope, and retrieval from scratch. OpenCode is the commoner case and the more instructive one: it ships the two hooks a memory plugin needs — a system-prompt transform and a compaction hook — marks both experimental, and offers no memory contract, so the plugin this atlas reviews from the other side reads its SQLite session tables directly. A host that offers seams without a contract does not avoid the design work; it relocates it into every plugin, in incompatible forms. NemoClaw sits a layer lower again: it sandboxes Hermes and OpenClaw and declares, per agent, which state directories exist and how each is snapshotted, restored and destroyed. Credentials are sanitized field by field on backup; memory is a directory, copied whole — so the most careful deletion above is undone by an ordinary restore below.

ByteChef is the family's largest host and the one where memory is a socket on a canvas. A workflow-automation platform — 738,068 lines of Java, first commit 12 June 2016 — whose AI-agent node has typed cluster elements for a model, tools, a chat memory, a knowledge base, a retriever and guardrails, so swapping Redis chat memory for Postgres is a different box rather than a code change; nine Spring AI ChatMemoryRepository backends ship behind that socket. Two things are worth lifting out of it. SanitizeTextAdvisor.getOrder() returns Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER - 1, which places PII and secret-key masking exactly one step upstream of the chat-memory advisor so that what is persisted is the masked text — pinned by a test whose assertion message is the property itself, "otherwise unsanitized text gets persisted" — and the agent refuses to build when two guardrails of one kind are configured, because two advisors at the same order make Spring AI's ordering undefined. A design that depends on a total order refusing the configuration that makes it a tie is a move nothing else here makes. Against that, the isolation around the one real scope key is carried by two ThreadLocals that fail open to a live target: TenantContext defaults to the public schema, EnvironmentContext defaults to PRODUCTION, and the S3 chat memory resolves a bucket from the first and creates it — see ByteChef.

gh-aw is the family's only host where the session boundary is a container teardown, and it is the only one that treats its own store as hostile. GitHub's agentic-workflows compiler expands a frontmatter key into GitHub Actions steps that mount a durable directory for the run and sync it back afterwards, over three backends — the Actions cache, an orphan git branch, and a managed issue comment. There is no memory model: the unit is a file, the retrieval is the agent's own Grep, and the compiler validates size, count, glob and extension without ever parsing content. What it does model is who wrote the file. The cache-memory store is a git repository with one branch per integrity level — merged, approved, unapproved, none — and actions/setup/sh/setup_cache_memory_git.sh checks out the branch for the workflow's level and then merges down from strictly higher levels only, so a workflow never reads what one under a looser guard wrote; the level is a branch and a cache-key prefix, not a field on a file. An information-flow lattice over memory is rare enough here to be worth the whole report, and the same script's restore gate is the transferable half: hook files deleted, core.hooksPath set to /dev/null, symlinks deleted, execute bits stripped, and disallowed extensions removed before the agent can read anything, because ADR-26587 reasons that a compromised prior run could have planted an executable. Every other host in this family loads its store and trusts it. The limit is the mirror image: the integrity level describes the run that wrote the file and never the claim inside it, nothing moves between levels, and no mechanism here can mark a memory wrong.

vLLM Semantic Router is the only memory in this atlas that an application cannot see. It is an Envoy external-processing filter, so memory happens to traffic: a chat completion passes through, a per-turn chunk is stored, an embedding search runs against that user's memories, a no-LLM gate applies recency decay, redundancy dedup and a 2,048-token budget, and the survivors are inserted as a message. MemoryType is semantic | procedural | episodic as an actual column; CreatedVia records llm_extraction versus api versus import; and the Store interface declares Forget(id) and ForgetByScope(user, project, types), which is targeted deletion in a contract, the thing almost no host interface in this atlas has. Two artifacts are worth more than the mechanism. e2e/testing/memory_tests/test_isolation.py opens "User memory isolation (security) tests" and checks a secret stored by one user against another at both the storage layer and through the live retrieval path — and every retrieval assertion in that suite runs in a new session with no previous_response_id, so a pass cannot be explained by conversation history. And MemoryContradictionTest stores two contradicting facts and asserts both survive, above a docstring saying the router does soft-insert today and this exists as a baseline for when contradiction detection is added, with three papers cited for why it matters. A characterisation test for a mechanism you have not built is a better record of a known gap than a TODO, and this is the only one here. The cost is the placement: keeping the block out of the system prompt is right, but inserting it immediately after the last system message puts it in front of the conversation, so a changed retrieval set invalidates the cached prefix for every message after it.

PLUR1BUS is what a plugin looks like when the contract's freedom is taken all the way, and it is the family's clearest statement of the cost. It is an OpenClaw memory extension of about 91,500 lines with a further 106,000 in tests, declaring forty-seven configuration groups and shipping dreaming, emotional state, persona voice, an Obsidian vault mirror, skill mining and fifteen background jobs alongside its LanceDB store. Inside that surface is one of the better correction paths here: lib/safe-update.js refuses a content change without a source and a quoted piece of evidence, refuses new text without a new embedding, writes the replacement before superseding the original so a crash leaves a recoverable fork rather than a hole, and appends the transition to an event log keyed by an idempotency hash. It also carries the atlas's only semantic drift gate — a correction is rejected outright if the new embedding sits more than 0.45 cosine from the old — and the one human caller in the tree skips it while the automated conflict apply fires it and downgrades an exceeded gate to a review, which is the sharpest example available of a safety default whose owner had to be decided. Its trust vocabulary is the second lesson: seven record statuses and a six-level trust ladder, wired into a ranking score, so conflict and untrusted cost a memory 0.3 and it reaches the prompt anyway. The deletion states, demoted and the epistemic invalidated filter; the status that flags a contradiction does not, on a measurement the code records. A plugin free to invent everything invents the states before it decides what they do — and the append-only store underneath makes that decision twice, because a status change appends a second copy of the record and something has to choose which copy the ranker scores.

Tradeoff: users choose a backend that fits their privacy and scale needs, but trust state, scope, and above all deletion must cross the host/provider boundary. MateClaw is the partial counterexample: its provider SPI carries an owner key on prefetch and syncTurn, and wraps every provider in retry and metrics decorators — but like the other three, it has no deletion hook.

Google's ADK is the largest instance of the same finding, and it inverts the first half. BaseMemoryService makes app_name and user_id required keyword arguments on every write and on search_memory, so a provider in that framework cannot forget which user it serves without discarding arguments it was handed — the strongest scope enforcement in the atlas, and enforced by a signature rather than a query. Then it declares add_session_to_memory, add_events_to_memory, add_memory and search_memory, and no removal method of any kind, while the sibling BaseSessionService does declare delete_session. Content promoted out of a deletable session into memory becomes unremovable through the framework. Microsoft's AutoGen is the same finding one level deeper. Its Memory protocol is five methods, and MemoryContent carries content, a MIME type and metadata — no identifier. So the absent delete is not an omission but a consequence: with nothing to address a memory by, a targeted removal cannot be written, and clear() — wipe everything — is the only removal verb in the protocol or in any of its ChromaDB, Redis, Mem0 and canvas adapters, both of the first two backends supporting targeted deletion natively. Scope is missing from the contract too, present only on the Mem0 adapter, optional, and defaulting to user_id or str(uuid.uuid4()) — so a forgotten principal is a silently orphaned store rather than an error.

That finding has now been refuted once, and by whose contract matters. For six framework contracts the count held — two carried scope, none carried deletion, and AutoGen's could not express one. Of the nine now read, the Pydantic AI Harness's MemoryStore Protocol declares read, get_operation, write, delete and list_paths, with search split into an optional SearchableMemoryStore extension. It is a targeted, addressed removal in the contract itself, and it is the only one. The three contracts added since — Agno's LearningStore, Microsoft's ContextProvider, and CAMEL's AgentMemory, whose only removal verb is clear — leave the shape unchanged. So the statement the contracts support is that one of nine declares deletion, which is the stronger claim: it proves the thing is expressible in a small protocol, and it names who bothered. See pluggable memory provider.

Microsoft's next contract is the third from these two vendors and keeps the gap. Agent Framework succeeds both AutoGen and Semantic Kernel, and its ContextProvider is before_run, after_run and a source_id — a context-engineering seam rather than a memory interface, with no add, no query, no delete and no scope. That is a more honest position than AutoGen's, which promised a Memory protocol and could not express a targeted removal; it also means deletion and tenancy are reinvented per provider. The in-tree harness memory then supplies what the contract declines, and its correction path is an LLM rewriting the durable topic file into "a tighter durable form", with no diff and no previous version kept.

Its owner boundary is a directory guarded three ways, in a framework whose contract asks for none: the owner id is read from session state and raises when missing, .. and absolute segments raise, and after resolving the per-owner root the store asserts it is still inside the base path and raises "Memory storage path escaped base_path" if not. No topic records its owner, so the boundary is the path. The framework's scope_enforced mark rests on its Redis provider instead, whose documents carry application, agent and user ids that its search filters on.

Agno is the third framework contract and the one that answers the question instead of deferring it. Its LearningStore Protocol is six methods — recall, process, build_context, instructions, get_tools, and a learning_type key — and unlike ADK and AutoGen it ships six implementations behind it rather than an in-process dict. That changes what the contract can be judged on. recall(user_id) returns None when the scope key is missing, so the fail-closed behaviour ADK gets from a signature, Agno gets from the body. Deletion is present and per-store: retire_fact keeps the superseded row with superseded_by naming its replacement, forget archives an entity, and both are exercised by tests. It also shows what a contract does not fix. The protocol has no notion of approval, so LearningMode.PROPOSE — advertised as agent-proposes-human-confirms — is implemented as a different return value from instructions(), a prompt telling the model to ask before calling save_learning, while save_learning itself writes unconditionally. A gate that exists only in the string handed to the model is not a gate, and it is the clearest instance in this atlas of the difference between instructing a behaviour and enforcing one.

SmythOS SRE takes the family's thesis to its endpoint: the interface is there and nothing implements it. LLMMemoryConnector is an abstract class exported from the package index with a load(messages) signature, no subclass, and no call site anywhere in the tree. What the MemoryManager subsystem actually contains is a cache service, a runtime-context serialiser and a conversation transcript — so the runtime that named a memory contract shipped the seam and none of the model. The reason to read it anyway is one layer down, and it is the best answer in this family to a question the others keep relocating into their plugins. Every read of every store passes @SecureConnector.AccessControl, a decorator that resolves the ACL stored with the entry and throws before the method body runs; a caller cannot forget it because callers do not implement it. Set against ADK's required keyword arguments, this is the other way to make scope unforgettable — a signature makes you pass the key, a decorator makes you pass the check. What it also shows is how far that guarantee travels: SRE writes the conversation mirror with a team owner, and the default Account connector resolves every unknown principal to one team called default, so the gate stays real while the boundary behind it widens to nothing. The machinery still runs, still logs, and still passes its tests. A permissive identity provider selected by default is the cheapest way to turn working access control into decoration, and it is invisible from the inside.

Outworked is the family's smallest memory and its clearest scope lesson. Under a macOS app that runs Claude agents as pixel-art employees sits one SQLite table, memory_entries(id, scope, key, value, created_at, updated_at) with UNIQUE(scope, key), behind three MCP tools named remember, recall and forget that src/lib/ai.ts mounts into every agent session, filtering out any user-configured duplicate so the memory cannot be half-configured away. The write path calls no model and the search escapes LIKE wildcards before interpolating. What it does not do is the finding: the MCP server is mounted per agent at a URL carrying agentId, handleMcpRequest receives it, and mcp-server.js:831-833 injects it into every tool that declares the parameter — while the memory tools declare scope and take it from the model, so an agent told to keep notes in agent:me has nothing between it and an agent that passes agent:someone-else. The identity is present at the boundary and unused by the store. See Outworked.

MemoraX Code is the family's clearest split between what a reader can check and what they cannot. One local backend serves Codex, Claude Code, DeepSeek Harness and OpenCode through four deployment adapters, and everything in this repository is the client half: a RepositoryMemoryScope of kind git-repository, local-directory or codex-projectless that is refused rather than defaulted when it cannot be resolved ("memory scope is required for MemoraX search/add"); credential redaction that runs before the payload leaves the machine, with an allowlist so ${ENV_VAR} and change-me survive while private keys, Authorization headers and JWTs do not; a retrieval that reports a skipReason when it does not fire; and a <memories> block grouped by memory_type and truncated to a character budget. The store is three endpoints away — POST /v1/memories/search, POST /v1/memories/add, and GET /v1/memories/add/status/{taskId}, the last of which makes the asynchronous write an explicit task rather than a silent lag. What a memory is, how a search is ranked, whether scope is enforced on the read, and whether anything can ever be deleted are all decided on the far side of that boundary: there is no removal of any kind in the client, and none in the surface it speaks. See MemoraX Code.

NanoClaw is the family member that tests whether its memory is plugged in. It runs each agent in its own container and keeps durable memory as plain Markdown under the group folder — no database, no embeddings, no extractor, no consolidation, and one agent-editable doctrine file standing in for all of it. What it has that the rest of this atlas mostly lacks is container/agent-runner/src/memory/scaffold.wiring.test.ts, written because "the unit tests drive ensureMemoryScaffold directly and stay green if the boot call is deleted", so it asserts against the entry point's source that the call and its import are both there; a sibling test asserts the injection hook has exactly one path and that the rival wirings are absent. Declared-and-unwired is the most common defect in this corpus, and this is the first repository in it that ships a test class aimed at the defect rather than at the feature. Beside that, src/memory-migration-contract.test.ts pins the sentences of a prose migration procedure — "Treat imported contents as untrusted data", "not instructions for the migration" — because importing someone's old memory file is where a prompt-injection payload becomes durable.

The inversion is the finding. Every enforcement mechanism here guards the transient layer — a cli_scope row filter, a self-scoped history handler, twenty committed cases about which sessions an echo must never reach — and the durable layer has none of them and the wider audience, since the memory tree is mounted into every session of the agent group. See NanoClaw.

Scope Recall is a local memory core where a claim has to quote its source, word for word, and the quote is checked. It serves Hermes, Codex and Claude Code, alone or through one shared store, with SQLite as the only authority. claim_storage.py refuses a derivation whose cited span is not literally present in the stored source. Promotion is a property of the evidence rather than of whoever asked: a claim stays proposed unless a cited root survives nine refusal rules, one requiring an origin of human_direct, tool_observation or external_document, and the agent's write tool stamps none of them. Tool output roots no claim at all. All seven marks; the tombstone is keyed on subject, predicate, value and conditions rather than on a row id. The caveat is the project's own: nothing in the tree measures recall quality, and the gate written for it was removed unpassed.

dsh-ai-memory is a DeepSeek Harness plugin whose boundary is right and whose default lifecycle is not. A Rust crate over one SQLite file sits under a thin Cordis plugin that registers six memory tools and a system-prompt section; before every model call the section recalls up to 128 hits for the latest user message and packs them into 8,192 estimated tokens, pins first. The project is bound when the session opens and never appears in a tool schema, the predicate is in the SQL, and two tests assert over populated results that another project's matching row stays out. But the plugin ships the chat preset, whose one-hour working TTL equals its promotion delay; consolidate checks expiry first, so a working note is hidden after an hour and deleted rather than promoted, against the preset type's own doc comment. The per-prompt prefetch increments the access count that decides promotion to the untimed profile tier, the only embedder is a 64-dimension token hash that HostSession cannot replace, and the default projectId puts every chat in a profile into one project.

SkillCorpus is the family's only plugin whose memory is other people's procedures, and the only system in the atlas that keys its curation decisions on the hash of the content it judged. EverMind's pipeline crawls public SKILL.md files into a SQLite library, and the two LLM passes that decide what stays write their verdicts into quality_judgments, keyed content_hash TEXT PRIMARY KEY, and dedup_judgments, keyed on the sorted pair of hashes. The build's fixed tail re-derives every exclusion from those caches before it exports, so a re-crawl of a body excluded for a cmd_injection flag is excluded again by the same verdict — which is the tombstone property, arrived at from the other direction, since the table was built to avoid paying the judge twice. The row-level markers do not hold on their own and the code shows exactly where: get_by_content_hash filters deleted = 0, skill_id is derived from the content hash, and insert is INSERT OR REPLACE, so re-ingesting an excluded body overwrites the excluded row and clears both deleted and superseded_by. Beside it, TigrimOSR in the same family has the human review SkillCorpus has none of — a staged SKILL.md.proposed waiting for a person — and none of the durability, and the pair is the clearest statement in the atlas of what each half buys. On the consumer side the engine ships in Python and TypeScript for five hosts, fuses a local BM25 pool with the remote catalog by weighted RRF, and lets an LLM gate put at most two skills into the turn, with every optional stage degrading to a no-op because "a retrieval problem must cost the turn its skills, never the turn itself". What it is not is experiential: nothing an agent does writes back, and the data model says so, dropping the counters and lineage fields of the record it was adapted from.

Jaz writes its write boundary in a comment. A personal always-on agent host whose memory is a markdown page graph with typed links and backlinks, plus two root-level horizon files injected into context every turn rather than retrieved. The split is the good idea: the engine's own line says "LONG_TERM.md is dream-maintained and read-only for agents; SHORT_TERM.md is agent-updated and dream-pruned" — the considered view kept by a scheduled pass, the scratch kept by the agent, which is the authority distinction most stores here blur. Nothing implements it. WriteHorizonFile checks only that the name is one of the two and that the content passes a shape check, then writes either; the HTTP handler takes the name from the URL path and passes it through; and the phrase "read-only for agents" occurs exactly once in the engine, in that comment. On a single-user desktop host anything on the machine reaches that endpoint — including the agents the product exists to run, and whatever arrives through its Telegram, WhatsApp, Gmail, Calendar and Slack connectors — so a rewrite of the long horizon changes what every later turn believes and leaves no record. The engine lives in a separate module pinned by Go version, read here at exactly that commit.

AKB hands enforcement to PostgreSQL on the one surface where application filtering could not work. A git-backed organizational vault of documents, tables and files served over MCP, where an agent may execute its own SQL — and because the caller writes the query, AKB does not filter in the application at all: user_sql_executor.py is the "[s]ole entrypoint for executing user-supplied SQL under per-user PG role", issuing SET LOCAL ROLE to akb_user_<uid> inside the transaction so "PostgreSQL enforce[s] vault isolation via its native ACL." A scoped token narrows further to akb_token_<tid>, a role whose membership is the owner-ACL intersected with the scope, "even if admin", and the scope model states the property that makes it safe — an intersection "so a scope only ever SUBTRACTS authority and is escalation-impossible by construction" — while distinguishing, at the point of definition, a None scope meaning unrestricted from an empty one permitting nothing. The boundary worth reading twice is that a concrete scope "gates mutating roles … only — reads are unrestricted", so a narrow token limits what an agent may change and not what it may see.

The Librarian decides with one function, and never asks the model how risky its own edit was. A markdown-and-git vault of memories, handoffs and references linked by wikilinks, tended by a resident curator, served to any harness as seven MCP verbs. Its apply policy opens by claiming a monopoly — "Every apply/propose/skip verdict in the system … is produced HERE and nowhere else" — and then states the rule is "enforced by OPERATION TYPE, never by model-self-reported risk", recording that the previous risk_level scheme with its off | safe_only | high_confidence levels is gone. Archive and split, the two operations that destroy or restructure, always propose whatever the confidence was; so does anything touching a requires_approval memory, a boolean only an admin or the curator may set. That is the right instinct: a model asked to grade its own edit answers from the distribution that produced it, while the operation type is a fact no prompt argues with. Its intake eval is built the same way round — a fake model, pure scorers, a committed baseline, and headline metrics that are absences, including one that rewards an ambiguous merge for not happening. What remains is the case below the destructive line, where confidence alone still decides a merge — which is exactly what that metric measures — and a history that lives in git rather than in the store.

state-memory-mcp fails closed on a detached HEAD and files the work under main anyway. A deterministic MCP server holding workflow state — tasks, decisions, artifacts, blockers and their edges — in one SQLite graph per project root, with an event row recording both sides of every mutation and an expected_version on update. Its branch handling is the third variation this page has recorded on one theme. git branch --show-current prints nothing when HEAD is detached, so getCurrentBranch() returns null; the read appends AND git_branch = ? and binds that null, which SQLite never satisfies, so the query matches nothing. A write with no branch supplied takes the column default main. During a rebase, a bisect or a CI checkout the two halves therefore disagree — work filed under main, reads returning empty — and neither says so, which leaves an agent to read "no state" as a fact about its project rather than about its checkout. It fails closed, where dsh-mnemon failed open; the better direction, and still the wrong thing to do in silence.

people-context makes the narrow disclosure the default and names the wide one. A local-first MCP server holding memory about the people in someone's life — one SQLite file, no account, no network call — where a fact carries a four-level sensitivity defaulting to PERSONAL, and ORDINARY_SENSITIVITIES is (PUBLIC, PERSONAL), "[l]evels an ordinary read may disclose, in the shared order used by every other read path", with ALL_SENSITIVITIES reserved for "the explicit local opt-in". Imports take the same posture: extraction stages into durable review state, a person lists the batch and commits by candidate id, and "a typo refuses the whole selection rather than silently committing the part that happened to parse." Its staged shape carries the sharpest boundary reasoning in this corpus — re-check a bound on restore only when refusing could not reject this installation's own data, which is why a trait's evidence budget is re-checked while an observation's text keeps its released shape. Its eval suite seeds two contacts sharing a first name and scores naming the right one beside not attributing the other. The thing no mechanism settles is that this is personal data about third parties who never consented; the project reduces accidental disclosure and cannot resolve the question.

Demarkus keeps every version and tells you whether the chain held. Versioned markdown served over QUIC as agent memory, where serializing a version requires the previous version's bytes — the error says so — and VerifyChain walks the retained history comparing each recorded previous-hash against the computed one. What lifts that above a stored digest is the read: the version-history response calls it and returns chain-valid: true, or chain-valid: false with a chain-error, in its metadata, so a reader learns the history is corrupt instead of being handed it silently, and the store-migration tests re-verify every chain on both sides after a move. Its capability tokens are the other thing to copy — {hash, paths, operations, expires} with the raw secret separated from the persisted entry so "the server only ever sees the hash", authorization failing closed through expiry, operation and path glob in turn, and a matcher using path.Match rather than filepath.Match because token paths are URL-style and the filepath variant changes behaviour by OS. What it does not model is belief: a document has versions and no status, no validity window and no supersession, so an agent asking what is true of a store that answers what was written will take the newest text as fact.

LLM Memory tests everything except the fence. A Postgres memory and multi-agent collaboration service whose distinctive part is deliberation — discussions with participants, ballots and votes, a realtime or async mode, and an outcome constrained by the database to consensus, deadlock, partial or abandoned, so an unresolved argument cannot be recorded as agreement. Its namespaces carry separate can_read, can_write and can_delete flags, which is better than the binary most systems here use. But getReadableNamespaces returns an array of permitted namespaces or null meaning "wildcard, no filtering needed", and of its eight call sites one passes two arguments where three are expected: the actor's type lands where the actor's name belongs, so that endpoint adds a namespace named agent or user instead of the caller's own, drops the caller's own namespace from their own listing, and — for a wildcard holder, whose answer is null — calls .includes on it and throws. The correct call sits eight lines above and another thirty lines below. None of the fifteen test files covers the permission service, which is how a one-argument slip in the security boundary of a multi-tenant store stayed in.

Titen makes a purge something later writes collide with. A Bun and SQLite memory with no model and no embedding provider on any path, where a claim carries a database-checked status, trust level and visibility, a validity window both retrieval lanes gate against a requested instant, and typed evidence links that can say contradicts rather than leaving disagreement implied. Its tombstone is the mechanism to copy: claim_sources is keyed (claim_id, observation_id, relation), and before a claim's sources are written the path inserts a speculative row for the cited observation only where the history table holds a purge for it — so citing purged evidence makes the real insert collide on that key and abort the transaction. The rejected value is consulted by construction rather than by a check a later code path could forget. Its access rule travels the same way: one SQL fragment resolving organization-visible, private-and-owner, or team with a live membership, ANDed with an ownership-or-grant clause honouring revocation and expiry, referenced at seventy-five call sites. Two things to read carefully: the guard emits one row for the first cited source while testing every one, and the hero image's "dependencies empty" does not match a package.json declaring two WebAuthn packages and a sqlite-vec peer — though the no-model, no-embedding and no-network claims do hold, and the same image publishes recall@1 falling from 0.880 to 0.246 as the store pools to 19,829 sessions.

TraceDecay never deletes a memory on its own, and keeps no record when it finally does. A Rust code-intelligence daemon with a fact store attached, whose hygiene rules are "conservative, rule-based checks — no model is ever invoked from Rust": the core rejects secret-like writes and only proposes deletions, the supersession analysis emits a candidate carrying review_required and a reason ending "confirm which fact is current before applying", and the CLI says the contract out loud — curation is a dry run and nothing is deleted without an explicit flag. Its twelve committed eval scenarios are the other thing to take: each declares a well-behaved path asserted to a compliant end state before its violation runs, and each cites the upstream it was adapted from with licence and commit. What it does not keep is a rejection. The changelog records an archive purge whose stated policy is that deleted memories are permanently hard-deleted, and the ops contract is delete-or-merge only — so the work a person did declining a proposal is gone the next time the same content arrives, and supersedes, which exists in the type, the parser and a database CHECK, is consulted by no read path.

memtomem makes the scope rule impossible for a caller to drop. A markdown-first memory where the files are the authority and SQLite is an index over them, and where the three scope tiers are enforced by a SQL fragment composed into every chunk query — one that is documented as non-empty in every case "so callers cannot accidentally drop the context rule by treating an empty fragment as 'no filter'". Out of a project it pins the user tier and excludes every project's rows, because the caller did not say whose shared chunks would be safe to show; inside one it unions user with that project; the caller's own filter narrows or opts explicitly into crossing. Three things stop that eroding: the adjacency path re-checks scope and validity before showing a neighbour, and an explicit filter there only ever widens; a registry test asserts every declared scope surface calls the vocabulary gate and fails when a new sink appears classified as neither; and an id-restricted recall still applies the boundary, with the rule in the test's own docstring — "'I know its id' is not authorization." What it does not model is belief: a chunk has no status, so a stale memory is corrected by editing the file, and supersession, tombstones and forgetting are answered by the user's version control rather than by the store.

Lore deletes by issuing a death certificate, and makes the importer read it. A transparent LLM proxy that distils sessions into versioned knowledge entries: there is no physical delete, an edit or a removal appends a new immutable version, and a removal's version is marked deleted. The half that matters is the read — the guard queries the base table rather than the current-rows view precisely so it can see those records, and returns true only when the sole same-title match in scope is a death certificate with no live row beside it, so both import lanes skip an entry the user already threw away instead of resurrecting it. Its contradiction worker is the other thing to copy: it pairs entries by cosine similarity on the reasoning that opposing rules are topically close, asks a model whether the pair genuinely opposes, and then stops, because "[d]etection ONLY — never merges, never deletes. The user picks the survivor (or keeps both) on the dashboard." Team sharing is a conjunction a person completes, approval survives a later content edit, and the curated output is version-controlled Markdown reviewed in a pull request. The gap is underneath that: the approval status that decides team sharing is documented as local and not synced, so the gate is per-machine state on a system whose premise is memory that follows a team.

Memorix fences every read and leaves one write unchecked. A local-first shared memory for coding agents where an observation carries a personal, team or project visibility and canReadObservation is consulted at every seam that returns a record — the MCP handlers, the search index, the session loader, the compaction engine, the SDK — rather than at one gate, fail-closed for the narrow scopes and refusing team visibility outright when the coordination store cannot be read. Its curated tier climbs candidate to qualified to approved through CLI-only transitions that run in a transaction, refuse a record carrying no evidence, and store the reason a person typed, so nothing reaches a task until someone acts; and consolidation filters to project visibility before clustering, because personal notes and handoffs "must remain individually inspectable and are never merged by a background job". Against all of it stands the transfer tool: export takes a reader and returns only readable records, while import inserts each observation verbatim with no visibility validation, no project re-stamping and no reader check — and since an unrecognised visibility resolves to project-wide and a missing admission state counts as deliverable, the tool the agent already holds can write project-visible memory attributed to any project and any agent.

GoodMemory refuses a rejected sentence forever, and keeps two different scope rules. A memory layer for chat apps and for coding agents installed in Codex and Claude Code: a writeback candidate is identified by a hash of scope, kind and normalised content rather than by a row id, so when a person marks a written memory a false write, the deletion removes the memory and preserves the dedupe key, and every later propose checks that set before staging anything — the same sentence extracted from a later session never comes back. In review mode candidates never touch memory at all until an operator approves or rejects them, with the approval reserved before the durable write, released when the write fails, and requiring operator recovery rather than self-retry when a reservation goes stale. Its own numbers are held to the same standard: every figure in the README is backed by a committed declaration naming the command, commit, judge and dataset licence, with a gate that refuses an undeclared number. The gap is that scope is enforced twice by two different rules — recall compares tenant and workspace with ===, while the admin and export path drops undefined fields before querying, so a user-only scope returns almost nothing from recall and every workspace's memory from the public exportMemory.

dsh-mnemon composes memory rather than storing it. A DeepSeek Harness plugin that pins one view per turn from byte-bounded runtime memory, workspace documents and memory spaces backed by Mnemon or one of eight external providers, and archives overflowing working memory only after the host verifies each entry landed. Runtime entries can be scoped to a git branch; the scope is dropped when the branch cannot be read and survives archival only as a tag recall ignores.

yantrikdb-hermes-plugin brings a structured engine into Hermes with scope taken from the session. The provider runs YantrikDB in process, derives each recall's namespace from the agent and optionally the person, and refuses its fleet view under per-person scoping. The shared-brain mirror and id-only forget are not held to the same rule, so both reach across people.

oh-my-hermes makes approval a state the record carries rather than an event in a log, and every surface that could reach a prompt re-checks it. An MIT operating layer installed alongside Hermes Agent at version 2.0.3 — 387,713 lines of Python across 720 test files, of which roughly 20,000 lines and 43 test files are memory. Capture writes a candidate whose own control payload says it is "review-only and never prompt eligible"; approval promotes it to a record stamped with an admission block carrying a state, the review id, the reviewer label, the admission time and the policy version. write_memory_block then chooses the record's storage destination from that state — "[p]ersist only approved revisions to the active store; stage all others" — and replay checks it again, with read_memory_block documented as validating structure "without granting replay trust". The two approved values are kept distinct: a record admitted under the auto-safe policy mode with no person involved is permanently marked approved_auto_safe rather than becoming indistinguishable from a reviewed one, though no gate downstream can require the manual value. The human path is three commands that refuse to collapse — stage, review with one exact remember, refuse or defer per item, then an apply that prints review_required and changes nothing until rerun with --apply. Safety is re-evaluated inside the approval lock so a candidate captured under a looser policy cannot be admitted under a tightened one, and the single-candidate path does its read, staleness check and write under one lock hold, with a comment recording the bug that forced it: outside the lock, a recapture landing between the check and the write "would be approved on the reviewer's behalf". The finding is that the governance is not uniform. approve_project_memory_candidate — the ordinary way a record is admitted — writes no operation record, while the batch, lifecycle, migration and principal-assignment paths all run through a state machine with receipts and recovery counts; the per-candidate review file is keyed review_{candidate_id} and overwritten, so a reapproval erases the prior decision; and operations and tombstones are pruned after thirty days while the records they admitted stay, so admission states outlive their evidence. Tombstones are keyed on record id and revision rather than value, which makes them deletion receipts rather than a defence against re-capture. Three marks: the admission state, the three-step review, and a test asserting superseded decisions are excluded before ranking — proved by their carrying no match_score.

Yantrik OS makes being ignored raise the bar rather than lower it. A GPL-3.0 Rust desktop operating system — 181,759 lines with 753 test functions, sixteen app binaries, a Slint UI and local quantized models — where every app publishes its state and accepts actions over one unix socket, so "[a]nything a person can do from the keyboard, an agent can do through the same path". Its durable memory is not in the repository: yantrikdb-core is a path dependency on ../yantrikdb, a sibling checkout of the upstream engine read separately here, and the manifest is explicit that it is "[u]pstream yantrikdb, not a vendored fork". What this tree adds is an LLM-free loop deciding when the machine should speak. Observations are typed as prediction error, tension, opportunity or uncertainty — uncertainty being the one wired to fetching external information rather than guessing — and four homeostatic drives set the rhythm, one of which carries the decision worth copying: "usefulness_pressure: rises when outputs are ignored, raises threshold (more selective, NOT more spammy)". The parenthesis is there because the obvious implementation does the opposite. The cortex above it captures a pulse per tool call into a cross-system entity graph with Welford baselines and a pattern miner, reflects with a model roughly every four hours rather than every cycle, and states that it "does NOT call the LLM directly". A nightly consolidation prunes expectations below 0.05 confidence unseen for sixty days and backs off unproductive curiosity sources. No marks: everything this layer stores of its own is continuous — drives, confidences, baselines, moving averages — with no discrete state withholding a record, no supersession, and pruning where a retirement would be. Its brain modules were moved out of a vendored copy of the database because living there "made them look like database code. They are not: they touch no YantrikDB type at all".

NEOTH builds its fact store against a failure it names in the header. A dual-licensed Rust personal AI daemon — 1,119,998 lines with 15,232 test functions in its daemon crate, five memory tiers and a vault — whose ground-truth module opens: "[s]liding 'if importance ≥ 0.95 treat as fact' is the failure mode this module exists to prevent." Operator-asserted facts live in their own table with their own scoring path — "no Hebbian decay, no FORGET_FLOOR sweep, no consolidation pass" — promoted and revoked only by explicit command, and surfaced "in every recall hit BEFORE any episodic row so a stale Hebbian-decayed memory cannot overwrite an operator ground truth". One mark: FactState is Raw | Candidate | Verified | Superseded | Contradicted | Deprecated, and surface_for_recall emits revoked_at IS NULL AND fact_state = 'verified' unless a caller deliberately passes include_unverified. What fills the withholding value is a contradiction detector that splits each statement at the first copula into a subject and a value part, fires on a polarity difference (bilingual negation markers) or diverging value tokens, and explicitly does not fire on a superset — "'nas at X' vs 'nas at X primary' is NOT flagged" — then demotes the lower-credibility side by corroborating-source weight rather than the older one. Consent to any remote provider is a marker file under canonical-origin grant sets so "endpoint A never authorizes endpoint B", kept as files because they survive a reconfigure and let the operator "audit consent state with ls ~/.neoth/consent/". scope is a column with no predicate on the recall path, and the write-ahead log is durability rather than a change record.

dsh-layered-memory ports MemoryCore's L0–L3 pipeline into a DeepSeek Harness plugin and leaves out what the upstream engine is marked for. Every turn is captured raw, an LLM extracts and deduplicates atomic memories, and two more passes write scenes and a persona per domain. Injected text is excluded from capture twice, and an off session is invisible to capture, distillation and all three tools. The isolation keys and the audit table are gone, replaced by a chat/work family that one tool of three honours. No surface deletes a memory, L0 is never deleted, and the rebuild that the error log recommends reads L0 from the database whose write failed.

dsh-continual-harness ports Prime Agent's continual harness onto DeepSeek Harness and keeps the careful half. An LLM planner rewrites the agent's prompt notes, memories, skills and subagent specs from its own trajectory. Every edit is validated, checked against a fingerprint taken at planning time, made to declare a reach the destination layer can hold, and journaled with before and after snapshots so it rolls back by id. The read half is thin: the default stable block lists entry ids, and no plugin tool reads one back. The optional global-write approval guards one verb while promotion and rollback reach the same store, and session-local skills are written into the shared skills directory.

Local coding-agent memory

rekal, engram, mempalace, llm-wiki-memory, basic-memory, hatchdoor, moltis, open-cowork, byterover, magic-context, swafra, memora, ai-memory, ctx, optmem, openworker, qwen-code, daimon, reme, acontext, gaius logseq, everos, ecc, skales, csm, graphify, clio, empryo, project-golem, openyak, dsh-mneme, altk-evolve, open-brain, memento, palazzo, memex-zero-rag, agentrecall-x, terse-memory, ean-agentos, memsem, cambium, perseus-vault, provem, sovereign, memoryops-ai, deepcode, prime-agent, kirocrew, engram-alpha, mimir, brain-md, iai-pme, ostk-recall, breadcrumbs, context-mode, ollama, serena, claude-code-memory-setup, token-optimizer, klypix-mcp, agent-mesh, tdai-memory-mcp, memory-compiler, ods, neurakeep, omninode-knowledge-base, otis, memoir-cli, deepseek-harness, mobius, hipocampus, openwolf, agents-memory, omnimem, reporecall, thoughtdag, no-human, joplin, silverbullet, siyuan, trilium, vista, forgetful, kwipu, craft, pro-workflow, teamai-cli, evox-genesis, memcontinuum, sage-memory, plur, openzync-core, sibyl-memory, auto-company, ripwire, artesian, context-keeper, deja-vu, pond, llm-wiki-cli, continuity-v2, memspec, somnigraph, slowave, ultracontext, contextmeld, kept, hivemind, uteke, claude-self-reflect, signetai, graymatter, marm-memory, mnemon, facets-flow, prism-coder, basemode, claude-mem-lite, egc, projectmem, stratagate, agent-memory-mcp, velesdb, memex, yacmemo, levh, huiran-cerebro, light-mem, mnemonic, nougenshards, edda, codemem, hungry-hippa, cortana, cortex-hypermnesia, okf-agent-memory, memhtml, ulpia, cogz, leteo, memorywhale, rememora, leankg, kipi-system, beevibe, knowl, kiwi-mem, beever-atlas, pensyve, kglite, meridian, memory-vault, open-graph-memory, funes, deus, codex-dev-mcp-suite, zcode, gastown, beads, yantrikdb-mcp, pmb, mempalace-code, engram-nickcirv, emulo, sqlite-memory-mcp, mex, m3-memory, ruvector, graft, memex-zettel, paperclip, open-bridge, shellbrain, memesh, autoharness, megamemory, exomem, rust-self-learning-memory, cortexes, wordcell, recallnest, memoose, cortex-hub, lindahaviv-second-brain, rexleimo-aios, dynamics-memory, neo-agent-brain, octobrain, kimetsu, lint-ai, nano-brain, global-agent-memory, rightmemory, claude-engram, ctx-open, roampal-core, opencode-session-recall, aoci-code, mcp-context-server, tree-ring-memory, engram-mcp, counterparts, claude-engram-mlapeter, orgtree, mainline, agent-memory-engine, alphaonedev-ai-memory, selvedge, local-memory-mcp, archeus, marvis, attune-ai, session-recall, mnemo, osk-system, ihmt-memory

dsh-mneme makes background consolidation accountable rather than trusted. A DeepSeek Harness plugin on SQLite with Markdown mirrors, it writes a receipt for every autoDream run — input snapshot, raw model decisions, per-id outcome — and a content-addressed digest for every merge, conflict and update it commits, applies decisions only against an unchanged snapshot, and lets a person's edit to the Markdown mirror win while the machine value goes into history. What it protects by default is less than what it can protect: conflict freezing for human review, scope isolation and trust weighting are all present and all off, and even strict scope walls only rows whose scope was declared explicitly.

ALTK-Evolve learns procedure rather than facts, and its contribution is the dose. IBM Research's system turns completed agent trajectories into guidelines, merges them by LLM conflict resolution, and gives each task the core guidelines that recurred across tasks plus the few from the most similar source tasks — reported on AppWorld as a rise from 50.0% to 58.9% scenario goal completion, 19.1% to 33.3% on hard tasks. It ships both as a server with pluggable backends, hook plugins and retention, and as plugins that make Claude Code, Codex and Bob run a learn step after every task. Correction is deletion: conflict resolution overwrites or removes guidelines with no revision kept.

Open Brain has the family's most complete governance design and nothing for it to govern. A Postgres service for coding agents whose schema defines assertions with candidate, confirmed, superseded and contradicted statuses, validity windows and evidence, and wraps every lifecycle, consolidation and pruning change in a reviewed, reversible execution with restorable tombstones. No code in the service inserts an assertion, decision, outcome, project or task — only tests seed them. Because events can name a project or task only by a foreign key to those empty tables, context packets are empty too, so the Medusa, Codex and Claude Code adapters remember into an event log and recall nothing. What runs is a flat pgvector memory table that the Hermes provider and the MCP tools write and search.

pond takes the same observation as deja-vu and answers it differently, and the difference is one column. Apache-2.0, 462 commits from seven authors since May 2026, 78,218 lines of Rust, storing sessions losslessly in Lance columnar datasets the user owns — a local directory or their own S3 — rather than indexing the transcripts in place. Every message part carries a provenance of conversational or injected, and search_text skips anything that is not conversational, under a comment citing the project's own spec: "only conversational parts contribute to the indexed text; harness-injected scaffolding is excluded from search."

That is the split most session-search tools never draw. The archive keeps the injected <task-notification> blocks and the system scaffolding — they are needed to restore a session into another client, which pond also does — and none of it can be retrieved as though a person had said it. An unknown provenance value is a hard bail! rather than a default, so an adapter change cannot quietly promote scaffolding into the searchable corpus. The mark it earns is for testing the predicate on both arms in one body: a conversational part yields the text, an injected one yields None.

Two things a reader weighs before installing it. Session content is not redacted on ingest, while config.rs carries three guards that redact credentials from pond config show — so the codebase knows how and chooses not to on the ingest path, which for a lossless archive of every session on a machine means every secret pasted into one, in a bucket if that is where the corpus lives. And no scope is applied by default: project and source_agent are stored and filterable, and the corpus spans every tool unless a caller narrows it.

deja-vu inverts the family's starting condition: it does not record forward, it indexes backward. MIT, 1,742 commits from thirty-one authors in the two months to September 2026, 273,068 lines of Go, 4,361 test functions. The observation is that every coding agent on a machine has been writing its sessions to disk for months and nobody was reading them, so the corpus already exists and what was missing is an index and a way to hand the right session back in whichever agent asks next. Recall arrives through hooks at session start, on each prompt, before an edit and after a failed command, with a novelty tracker suppressing an id already served into the same session.

Two properties are worth taking. Redaction happens on the indexing path, so the derived store never holds a secret at all — a stronger guarantee than stripping at read time, where the index on disk still has it. And the test that earns this report's one mark is a model: a session-scoped search asserts one hit and the right one, and the next test in the file exists only to prove the fixture is not degenerate, under the comment "Without the flag both sessions answer, so the test above is measuring the flag rather than a fixture that only had one match." Most suites bury that control in an extra assertion where a refactor deletes it.

What it deliberately does not build is the epistemic layer, and the refusal is argued rather than absent. The index derives GaveUp from a session's own words, the search applies a score penalty and prints "mentions backing an approach out — one path here was abandoned", and the comment declines to go further: "Nobody sets the rejected state by hand… When the transcript itself says something was backed out, say so — as evidence from the session, not as a state someone recorded." Nothing withholds. Nor is there a scope boundary — crossing projects is the value proposition — so a session holding something specific to one project can answer a question asked from another, and the redactor's job is secrets rather than confidentiality between projects.

Artesian separates the two clocks most of this family runs together. An Apache-2.0 Rust workspace of fourteen crates and 67,852 lines, two authors, 213 commits between June and August 2026, installed as a Homebrew binary and dropped into any MCP client. One half governs what the current loop holds: a qualify gate scores a candidate on relevance and redundancy, emits a ReasonCode from a closed enum whichever way it decides, and evicts and compresses a bounded committed context under a token budget. The other half is a durable store with four backends behind one trait. The design's claim is that what may be acted on now and what the system believes are different questions, and the code keeps them in different crates.

Two mechanisms are worth taking whatever else you build. A memory's id is a SHA-256 over its content and its six routing keys, so deduplication cannot merge two projects' memories and an idempotent re-import stays per-tenant. And the scope filter is pushed into the store's own filter rather than applied to returned rows — must_eq conditions the vector database evaluates itself — so excluded records never cross the wire. A CI gate holds the property: three sentinels in projects A, shared and B, a query as A, and a pass condition that requires B's absence and A's and shared's presence in the same conjunction, which is the cheapest known defence against a leak test that passes because the result was empty.

The gap is where the lifecycle stops. artesian memory evict constructs a FilesBackend unconditionally and applies its decisions by walking a directory of .md files, so on the sqlite-vec backend the README recommends as the zero-infrastructure default, nothing decays, nothing is archived, and the eviction.jsonl audit log this report credits stays empty. The README's headline audit — "every admit/reject decision in an append-only audit log" — points instead at qualify.jsonl, which is written with std::fs::write and is therefore a truncating dump of an in-memory vector. Retraction is the strongest part of the model and the closest miss on tombstone: a retracted record blocks a re-store of byte-identical content in the same scope, but only because identity is a content hash, and the retraction record itself is read by nothing.

Context Keeper puts the bar before the store rather than after it. MIT, 74 commits from April to August 2026, three authors, 7,587 lines of Python with zero runtime dependencies and 511 test cases beside them. A decision is refused unless its problem runs to forty characters and its why_chosen to sixty; a constraint needs a forty-character reason. _check_min_lengths returns the field, the actual length and the minimum, and update_entry re-applies the same floors so an entry cannot be edited below the bar it was admitted at. Everywhere else in this family a thin memory is stored and then ranked; here it is not stored.

Two more things are worth the read. It ships two read tools with different contracts — get_context ranks inside a 4,000-token budget and annotates its own weak answers with no_confident_match when tag-and-text overlap falls under a floor the config documents as "the highest value with zero false-abstention on the eval set", while query_entries applies exact predicates over status, origin, hardness, scope and supersession and does no ranking at all. And scope_rules.py is one implementation of what a scope covers, written after four surfaces each had their own and disagreed on two of ten cases; the module docstring names the failure that produced — an over-eager match marked a constraint delivered, so the file it actually governed never received it.

The gap is one line. _find_similar_entries is the only write-path consultation of stored memory, comparing a new entry against existing ones and labelling a high-overlap pair a likely contradiction or a likely restatement — and it skips every entry whose status is deprecated or superseded. A rule retired last month can be recorded again tomorrow and the check built to catch exactly that will say nothing. Deprecation stores the value, the reason and the time, and no later write reads any of it.

Hipocampus asks the question this family's retrieval sections usually skip: whether to search at all. A files-only harness — 1,796 lines of Node, 2,291 of specification and prompts, MIT, installed as a Claude Code plugin or by npx hipocampus init for OpenCode, OpenClaw and Codex — whose worked example is the failure no query solves: you settled on a token-bucket rate limit three weeks ago, you ask today about the payment endpoint, and the agent never searches for "rate limiting" because you never said it. So the top of its compaction tree is not a summary but an index. memory/ROOT.md is capped near 3K tokens, injected every session, and shaped for a judgement rather than an answer — a Topics Index for "O(1) 'do I know about X?'" — because, as the layer spec puts it, "determining 'do I know about this?' requires loading memory, but loading itself costs tokens." Under it, a node is tentative while its period is open and is regenerated from its sources rather than patched, then fixed when the period closes, so a weekly summary cannot drift by accumulating edits. Two things stand against it. Nothing can be corrected: raw daily logs are "permanent leaf nodes", every index node is a supplement, and the only content-keyed removal strips entries marked temporary or delete-later — in English or Korean — as they are promoted, so a wrong entry is outvoted by later summaries rather than withdrawn. And the benchmark carrying the claim, MemAware, is a second repository by the same author, with a no-memory arm and two search baselines but no result committed here — see Hipocampus.

Otis is the family's supply-chain case, and the contrast is inside one repository. Its durable memory is a resumable JSONL session per workspace plus a set of skills — directories of Markdown the agent loads by name and follows as instructions. Installing one is git clone -- <url> with no revision; updating is git pull --ff-only; and the runtime manifest type is { id, url, skills: [{ name, relativePath }] }, with nowhere to record a commit or a hash. Two files away, cli/update/binary-installer.ts aborts the agent's own update when sha256File(archivePath) does not match the release manifest, and the repository's skills-lock.json carries a computedHash that appears exactly once in the whole tree — nothing in src/ writes it, reads it or checks it. What a skill may touch once installed is guarded carefully, with containment asserted before and after realpath; which skill arrived is not checked at all. Its compaction is worth copying regardless: the event that replaces the context carries the messages it replaced, so the model forgets and the store does not.

memoir publishes the best-argued deletion spec in this atlas and ships no way to invoke half of it. MIT, 11,932 lines of JavaScript, and its durable memory is mostly not its own: eleven adapters read and write the host tools' memory directories — ~/.claude, ~/.gemini, ~/.codex, Cursor's, Windsurf's, Zed's — with only a session working set, an event log and encrypted cloud backups kept for itself. What it adds is a format spec whose merge section opens by stating the standard the rest of this corpus should be held to: "Every rule here exists because its absence produced a real data-loss or data-resurrection bug in production." Under union-by-identity a removal cannot be an absence, so it must be a record — and the record must be monotonic and date-independent, because tombstoning does not touch the item's date and the tombstoned copy therefore usually loses the newest-wins comparison. "Suppression must be monotonic or it is not suppression." The spec then splits the mechanism in two and forbids substituting one for the other: a suppressed decision is junk permanently, while a completed action can legitimately be re-added, so the second tombstone compares added against done_at and lets a genuine revival through. Both rules are implemented as argued, tombstones are partitioned out of the visible cap so they cannot evict a live memory, and the retention floor is normative — the completed list "MUST be large enough to outlive any stale replica" or completions resurrect.

Then the absolute tombstone has no writer any user can reach. The only assignment of hidden = true outside the merge function is scripts/cleanup-junk-decisions-2026-07.mjs, whose own header says "NOT wired into any CLI command or package.json script", whose match strings are placeholders, and which is excluded from the published npm package by package.json's files array. Three read paths filter it, the validator enforces hidden_at against the spec section number, and a test asserts its exclusion at three surfaces including a real stdio MCP call. Fourteen MCP tools let an agent write memory and not one lets it retract memory. This is why the tombstone mark is withheld here: the mechanism is specified, implemented, filtered, validated and tested, and unreachable — which from outside the package is indistinguishable from never having been built.

DeepSeek Harness is the family's answer to a question the rest of it mostly ducks: what if the agent could search its own history? MIT, 564,122 lines of TypeScript across 2,578 files, published on 13 August 2026 carrying 12,293 commits of prior private history and a README that calls itself a developer preview promising breaking changes. Its durable memory is the shape this family shares — an append-only session event log, here behind a seam with JSONL and SQLite backends — and what it adds is a SQLite FTS5 index over the whole corpus with five model-facing tools on top: search across prior sessions, search within one, trace a session's ancestors and descendants, trace every direct replacement of a single event, and read one event unabridged. Almost nothing else here lets an agent ask its history a question rather than be handed a slice of it.

Two mechanisms are worth taking whatever you are building. Compaction shadows rather than deletes: { op: 'replace', start, end } marks a range of surface entries replaced and inserts the summary in their place, and every event carries a surface of current, shadowed or log-only — indexed as a column, so "what the context held three summaries ago" is a query rather than a loss. It is not a trust state and the mark is withheld for that reason; it answers whether the model sees an event, not whether the event is so. And the authorization is tested against a prober rather than a user: search is filtered by the caller's workspace cwd, and the committed cases assert the failure directions — fail closed with no agent, allow only self for a null-cwd caller, reject records the provider returned unrequested, and, the one nothing else here has, "makes hidden and nonexistent parent guesses indistinguishable without calling search", with a fixture whose text is must not be discoverable. Treating the existence of a record as confidential is a bar most multi-tenant memory in this atlas would fail.

What it does not have is belief. Nothing stored is a claim, so there is no confidence, no verification, no supersession of a fact as opposed to a context range — and no delete a person can reach: the four DELETE FROM statements maintain the index, the model's tool set is read-only by construction, and the spill seam says outright that it "does not define a per-session cleanup policy". For a store whose content is transcripts of somebody's work, that is the gap to notice first.

Mobius is the family's clearest case of scope as a filesystem path, and of what that costs. Source-available under a bespoke non-commercial licence — not open source, though its own README says so — it is a 127,468-line team platform that drives Claude Code and Codex inside tmux sessions behind issues, projects and resource ACLs. Its memory is markdown with name/description frontmatter at memories/user=<id>/project=<id>/<slug>.md, and a skill is the same file through the same parser, which is why both got a full CRUD surface, an import path, a cross-team copy catalogue and an access model at a size where most projects build one of them. The id is deliberately reversible — project:${userId}:${projectId}:${slug} — so the filesystem stays authoritative without the database, and the slug is generated separately from the display name so renaming never breaks a reference.

The cost is recorded in the repository's own comment. Because scope is a path segment composed from a caller-supplied id, user:../../..:x resolves through userDefaultDir to a path outside the root and yields "arbitrary .md file read" — and the note adds that the write and delete paths already carried withinRoot protection while the read path originally lacked it. Both are guarded at this commit, and no test pins either. That is the transferable lesson rather than the bug: when a scope key becomes a path, the operation that looks like a harmless lookup is the one that gets the check last.

What it does not have is retrieval or belief. Every in-scope memory is injected wholesale, built-ins first, with no search, ranking or cap on the set — and the file format carries no timestamp, author, confidence or status, so a correction is an overwrite and the only history anywhere is 30 retained backups of the one slug the .imac/project_knowledge.md sync maintains. The epistemic machinery has moved one level out, into ACLs, per-user hides and context whitelists: not is this true but whose is it and who may see it, which is the axis a team product competes on and which most of this atlas does not model at all.

OmniNode's knowledge base is the family's most schema-disciplined store and its clearest instance of a rule that is written rather than checked. Every artifact carries typed frontmatter validated against a discriminated union, so a decision record has its own status vocabulary — proposed, accepted, superseded, deprecated, rejected — and CI fails a file that steps outside it, along with a refs: entry that does not resolve, a stale generated index, or forbidden content in a commit message. What none of the five checks covers is the rule the project leads with: "Every accepted ADR and confirmed pivot should have at least one evidence file. Claims without evidence are hypotheses." Eight accepted ADRs, five accepted pivots, and evidence/ holds a README and nothing else. The same gap admits two ADR files sharing adr_id: ADR-0010, and leaves the one supersession pair in the corpus reciprocal by hand. It sits beside agent-mesh, which models the same decision-ledger shape and enforces its status transitions in code.

Durable local state for a developer workflow: hooks, MCP, project scopes, exact search. Engram is the small no-extraction baseline over SQLite and FTS. Palazzo is MemPalace's stated minimum-viable flavour — the same wing/room/hall vocabulary over Qdrant in 5,500 lines of Rust — and it is the family's clearest instance of an idea the atlas keeps looking for: its write-ahead log gates the delete rather than recording it, so an audit entry that cannot be written aborts the destruction. It also committed the benchmark showing it losing to the system it cites, which nothing else here has done. AgentRecall-X is the only system in this atlas where a memory's authority can be taken away by evidence. A human correction marked authoritative at severity p0 returns verdict: "blocked" against a proposed action — human corrections outrank the model by construction — and a p0 that has been surfaced three times and honoured less than a third of the time is excluded from its own veto, on the reasoning that stale rules must not veto legitimate plans. Standing is granted, exercised, measured, and withdrawn. The catch is that the precision driving the withdrawal is judged by the loop watching the agent, so the measurement is not independent of what it measures. EAN AgentOS captures from hooks rather than from judgement, and returns the failure anyway. Commits, bash commands with exit codes, tool calls and file versions arrive because a hook fired, not because a model decided they mattered — and its errors_solutions table models the attempt: an error, the fix tried, whether it worked, and how many tries it took. Then both recall paths write ORDER BY solution_worked DESC rather than a WHERE, so a fix that failed is demoted one row and handed over in the same shape as one that worked. The only WHERE solution_worked in the tree is a filter on the human dashboard. TERSE Memory is the same bet with a real checker. Its package is a linter, a scaffolder and a skill — no capture, recall, forget or consolidate function exists — and lint.py implements four named rules while naming three more as deferred. The split is the finding: dangling references, schema violations, secrets and always-loaded-tier bloat all ship; stale, duplicate and consolidation-due are v0.2. The idea worth taking is # Hot buttons ## Don't, a user-extendable prohibition tier that is always in context and capped at twenty objects, so a standing instruction never depends on a retrieval surfacing it. NeuraKeep enforces provenance at the write instead of describing it. Apache-2.0, 10,626 lines of TypeScript over one local SQLite vault with an MCP server and a review app. governProposalDiff blocks any event, fact or failure whose sourceIds or sectionIds are empty — "Fact lacks source and section citations" — so a memory that cannot say which document and which passage it came from is never created. Nothing durable is applied automatically either: the extractor produces a proposals row with a diff_json and a person applies it, and the agent's own daily notes are routed through the same queue into a separate system space, so the system cannot promote what it wrote about itself. Beside that sits a working undo — an append-only JSONL audit carrying before and after per mutation, with undoable derived from the presence of a before and the reversal itself audited — which is a live instance of the recoverability axis the rubric records as uncovered. Its failures table pairs do_not_repeat with a revisit_condition, so a prohibition carries its own expiry criterion, and facts carry review_after beside valid_from/valid_until.

Then the read path undoes some of it. The section query filters AND (? IS NULL OR sections.space = ?) and the MCP tool passes optionalString(args.space), so an agent that omits the argument searches every space at once — while the failures query twenty lines away takes WHERE space = ? with no null branch, and the CLI resolves an unset space to personal. One repository ships the safe form, the defaulted form and the unsafe form of its own scope check, and the unsafe one is on the surface the model drives. Its three discrete trust levels have the same shape: a poisoning scan downgrades content to untrusted, and the read path filters on sensitivity instead, so attacker-influenced material is ranked down and returned.

ODS contributes one idea and it is about forgetting. The repository is a deployment system for a local AI stack — 3,266 commits, 27 services, Apache-2.0 — and outside ods/memory-shepherd/ the phrases agent memory and memory system occur only in prose, every forget in code is wifi-forget, and every embedding is about deploying embedding models. The exception is ods/memory-shepherd/, a scheduled reset daemon whose problem statement names the failure directly: "agents rewrite their own instructions, subtly altering their operating parameters." Its answer is positional. An agent's MEMORY.md is split by a ---: above it an operator-authored baseline the agent cannot durably change, below it whatever the agent writes, and every few hours the scratch is archived to a timestamped file and the baseline is restored verbatim. The authority boundary is a position in a file and the enforcement is that the file gets overwritten — which is CowAgent's nightly overwrite with the operator's half carved out and protected. On the configuration ODS installs, though, none of the three shipped baselines contains a --- — so every cycle takes the branch written for an unexpected file shape, and the positional boundary is reached only once the agent has drawn it itself. The baseline template even discloses the policy to its own holder: "Your additions will be periodically archived and this file reset to baseline. For anything worth keeping long-term, write it to your project repo."

The defect is in the boundary. The separator is found with grep -n "^---$" | tail -1 — the last match — so an agent that writes a Markdown horizontal rule in its notes moves the line, and everything above its own rule is neither archived nor kept when the reset overwrites the file. The asymmetry is what makes it worth recording: finding no separator triggers a full-file backup and a warning, so the design already knows an unexpected file shape should be preserved, and applies that to zero separators but not to two.

Memory Compiler is the third kit built around a checker, and its checker is the one this atlas has spent the longest looking for. 1,432 lines total, one dependency-free Python file and four Markdown conventions, uploaded in three commits on a single day. TOMBSTONES.md is an add-only table whose columns include the rejected value itself, and tombstone_collision_check() scans the other canonical files for that value appearing verbatim — a hit is a blocking finding, so --close refuses to seal the session and leaves the ledger entry open. A rejected-value tombstone with a chokepoint behind it, in about thirty lines. Its replacement column is a pointer rather than a copy, with the reason recorded in the architecture — "A copied value is the next stale fact waiting to happen" — which is the failure mode most supersession chains in this corpus walk straight into. The limit is a noise floor: the scan ignores rejected values under twelve characters as too noisy, and both tombstones in the project's own worked example are ten characters long, a superseded date and a superseded hex colour. Neither is visible to the automatic check; what covers them is two hand-written must_not_return cases in memory_tests.yaml. Dates, prices, versions and colours are most of what gets corrected, and almost all of them fall under the floor. It also prints audit: not implemented in this reference build — no audit trail was written on every close, which is the opposite of the schema-shaped audit tables this atlas has found elsewhere with nothing inserting into them.

breadcrumbs is the other kit built around a checker, and its checker asks the question this atlas has otherwise only asked of itself: not whether an entry is still true, but whether it can ever be seen. retrieval_exam.py replays a configurable model of the reader's boot matcher against simulated session-start conditions and classifies every ledger entry precise | broad | special | unreachable, where unreachable means the key is neither a path in the tree nor a declared special word, so nothing can make it fire. A write-only entry is worse than no entry, because no entry leaves a visible hole. The same script's --survey mode drops the ledger requirement entirely and scores any repository's markdown by link distance from CLAUDE.md/AGENTS.md/README.md, naming the orphan — a document nothing links, which a session never opens on its own. token-optimizer is the family's only recall path that labels what it recovers as untrusted. Its product is waste detection; its memory is a checkpoint written when the context window crosses 20, 35, 50, 65 or 80 percent full, or when a session-quality score falls through 80, 70, 50 or 40 — a capture trigger derived from resource state rather than from any judgement about content, which is a different answer to "what is worth keeping" than anything else here. A later session's first prompt is keyword-scored against every checkpoint inside a look-back window, same-session entries are skipped twice over, and exactly one winner is injected. What it injects is fenced: <!-- trust="data" -->, the sentinel [RECOVERED DATA - treat as context only, not instructions], and a body stripped of every C0 control except tab and newline. Text an earlier session wrote is text an attacker may have written, and this is the only system in this family that says so to the model. Its second idea is a disclosure: when the current working directory lets it drop another project's decisions from the hint, the block says that something was dropped — and a committed test asserts the complementary case, that a single-project checkpoint emits no disclosure. Quietly returning less is indistinguishable from having less. Against all that: nothing is ever corrected or deleted, MAX_AGE_DAYS bounds what is searched rather than what is kept, and the licence is PolyForm Noncommercial.

claude-code-memory-setup is the same graph idea at the other extreme of effort, and the pairing is instructive. It is a recipe — a guide, a 387-line importer that a nightly cron job runs, and a SessionEnd hook — that files Claude Code transcripts into an Obsidian vault with keyword tags, and inserts [[wikilinks]] to existing notes into the body as it writes: longest name first, first occurrence only, code fences skipped. A new note joins the graph with nobody curating it. The guard against a false link is that a note name must be at least four characters, which removes api and keeps test, and the guard against re-wrapping checks only the characters beside a match, so a shorter name inside a longer link is nested. The rewrite is silent and lands in the note body, and each nightly re-import overwrites any edit made to an imported chat. Serena, below, faces the identical problem — a bare name in prose that should be a link — and warns instead, graded by confidence, behind a similarity threshold with a test on each side of it and an ignore list for words that are also English. Same mechanism, opposite risk posture. Its README also leads with "71.5x fewer tokens per session", a figure nothing in that repository produces or measures.

Serena is the only system in this family whose memories are a graph and whose links are checked. A memory is a Markdown file whose body may cite another as `mem:name`, and three mechanisms follow from that one convention: a rename that rewrites every reference to the file it moved, anchored so mem:auth cannot match inside mem:auth_tokens; a serena memories check report of stale references — links pointing at nothing, each with up to three replacement candidates ranked by a similarity score whose thresholds each have a test naming the false positive they exist to prevent; and the inverse report of unmarked references, a bare memory name sitting in prose that should have been a link, graded high or low confidence by whether the name is unlikely to be ordinary English. The shipped memory_maintenance.md makes the graph a discipline rather than an accident, and carries the line that inverts how the rest of this atlas handles relevance: "Memories themselves should not contain information about when to read them; this is the responsibility of the referring memory." Relevance is an edge property. The gap is the other direction — nothing performs a reachability pass, so a memory that nothing links to is unreachable under the declared traversal model and no report mentions it, which is exactly the check breadcrumbs does run and the only half it has. Context Mode is the family's widest reach and its sharpest scope test. Its product is a sandbox that distils tool output; its memory is a per-project SQLite event log written by hooks in seventeen different harnesses and replayed into the next session as a <session_knowledge> block. Two things are worth taking. The first is tests/session/cross-session-bleed.test.ts, which pins a contract six SessionStart adapters depend on — a resumed session gets only its own events, and an unknown session id returns [] rather than falling back to whichever session started most recently — written as negative assertions with a header explaining that the alternative is six adapters leaking silently, which is what had been happening. The second is src/search/ctx-search-schema.ts, which spreads the cross-project project parameter into the ctx_search tool schema only in shared-database mode; in the default layout the field does not exist, defended in the comment as "a stronger guarantee than runtime" validation. Against that: no UPDATE on session_events anywhere, and the only delete is ctx_purge destroying a project's whole store. Ollama is the narrowest entry here and the one that says most by omission. Its agent/ package has a session loop, an approval gate and a compactor, and the only thing that outlives a run is a catalog of SKILL.md files discovered across four roots — including the cross-client .agents/skills/ convention at both user and project level, which it reads without owning. SkillCatalog.SystemContext() puts one name-and-description line per skill in the prompt and loads a body only on demand, and agent/tools/skill.go requires approval for a model-initiated load because "a skill's instructions can influence the rest of the run" while user activation bypasses it. Gating recall rather than the write is the right way round for procedural memory and almost nothing else here does it. Everything the agent learns in a run is still discarded. MeMex Zero-RAG is the family's clearest case of the convention/code line. It packages the Karpathy LLM Wiki pattern — raw/ immutable, wiki/ derived, git as the whole history — and then expresses its citation rule, its human-adjudication stop and its operation log as instructions returned to the model, none of which has a code path. Read it for the layout and for what delegating every invariant to a prompt costs. DeepCode is the family's sharpest split between two durable stores in one repository: conversational turns are event-sourced with typed provenance — a ClientSurface and a TurnInputSource recording whether a turn came from a person steering or an automation retrying — while the durable facts are flat markdown notes with no metadata at all, and a scheduled autodream pass holds delete over them. The harness that stamps provenance on every turn records nothing when a note is destroyed. MemPalace keeps verbatim drawers authoritative and treats extracted layers as navigation aids. Basic Memory makes human-editable Markdown canonical and every index a rebuildable projection. Moltis indexes a Markdown corpus that raw session logs and model-extracted notes are written into, so conversations become searchable notes in the same substrate as curated ones; the sanitizing exporter its docs describe has no caller. open-cowork separates core from experience memory and ships the atlas's most complete memory benchmark. ReMe is the one that publishes its results rather than its harness — per-category LongMemEval and BEAM tables committed to the repository, its worst score among them — and carries the atlas's only validated correction vocabulary outside Memanto: CREATE | CORROBORATE | REFINE | CORRECT, with an additive-only update rule that nothing checks. Acontext is the one that finally implements the gate this atlas has been asking for: a task's status is constrained to success | failed | running | pending by a database CHECK, only the two terminal values enqueue learning, and three committed tests assert that the other cases write nothing. Swafra shows how little code a local graph-RAG sidecar needs, and — since its v0.3 line — how narrowly a correction mechanism can miss. It now retains superseded facts with a validity end instead of deleting them, and demotes the chunks behind them at search time; but the fact id hashes source_id alongside the value, so the same value restated in a different session is stored fresh as current, and the whole lifecycle is absent from the six MCP tools, leaving a ranking multiplier as its only consumer. Its SQLite tier also declares a normalised facts table with valid_from and valid_to that nothing writes — the shape PowerMem showed first. Qwen Code has three memory tiers and commits the third to the repository, so shared memory is distributed by git pull — and a write to that tier containing a detected secret is refused unconditionally, even when the tier is switched off, because the directory is source-controlled regardless. OpenWorker carries a 260-line memory whose real artifact is the paragraph governing it, and a comment recording why it exists: without when-to-remember rules, "models either never call remember or save noise the repo already records". OptMem is the limit case in the other direction: 860 lines, an append-only log the code never edits, a binary merge tree whose resolution decays with age by geometry rather than policy, and no background work at all — every compression is printed in the output of note for the agent to answer in its own turn. ctx is the only system here that bounds where its background consolidation may write — a path guard with one disposition-gated exception — and the only one whose tests include a corruption corpus drawn from published research. ai-memory models the thing an interrupted task leaves behind: a Handoff addressed from one harness to another, carrying open questions and next steps, which expires if nobody accepts it. Memora is the only system here whose automated correction pass defaults to a dry run: the sweep that would hide superseded memories reports its findings unless mutation is explicitly requested. Daimon narrows the unit of memory to the session boundary — one checkpoint written when a session ends, one skimmable briefing injected when the next begins — and spends its complexity budget on checking the extraction rather than on retrieving from it.

CSM is the family's maximalist, and the one that keeps receipts on its own context window. Forty-six tables and 57,000 lines behind an OpenCode plugin, with no language model anywhere on the write path — the sole outbound call in the runtime is an embedding request. Two mechanisms are worth the visit. Its context_injection_items table records every candidate considered for the re-entry block with a position, a score, a disposition of injected | trimmed | omitted, and a reason code separating budget_trim from layer_budget_exhausted from filter_rejection; most of this atlas can say what it injected, and CSM can say what came fourth when three fit. And its work ledger stores each file edit as before/after hashes plus a line-hash multiset, then re-reads the file later to classify the edit active, partially_superseded, superseded or reverted — a memory of work checked against the artifact it claims to have produced, which is the verify-memory-against-its-subject move applied to the agent's own output rather than to a document. The disconnect is in the plumbing: search and the evidence block exclude superseded and archived rows, and the lesson-trigger cache that injects up to 50 lessons on every turn checks neither column nor the project, so a lesson retired in one project keeps instructing the agent in every other, and scope_enforced is withheld on that read. And the belief tier below it never arrives at all: the injected beliefs layer admits only status === 'promoted', and no code path in the repository writes promoted, so a consolidator that runs every two minutes computing confidence, uncertainty and contradiction counts feeds a section that renders "No consolidated beliefs yet." forever.

Graphify is the smallest complete instance of the loop in this atlas, and it is a side layer on a code-graph tool rather than a memory product — about 900 lines of the 15,959. An agent answers from the graph, then calls save-result with the question, the nodes it cited and an outcome of useful | dead_end | corrected. A deterministic pass scores each cited node with a signed, 30-day-half-life weight and sorts it into preferred, tentative or contested; preferred requires two distinct results, and the docstring says why — "one save can't mint a trusted lesson." The verdict lands in a sidecar deliberately kept out of graph.json ("no learning_* fields are ever stamped into the graph itself"), reaches the model as a learning= suffix on each node line, and moves a preferred node to the front of the exact-match list. Each entry stores a SHA-256 of the cited node's source file, recomputed on every read to stamp stale — content-only, no path mixed in, so a sidecar committed to git stays valid on another machine. What it does not do is the thing its own skill promises: dead_end is documented to the agent as "don't re-derive it next time", and no code path consults the dead-end list. It is prose in a generated Markdown file that a model is expected to obey.

CLIO is the family's one Perl entry, and its trust tier has the right shape and a caller-supplied input. Pure Perl with core modules only means no vector index and no embedding call, and long-term memory is one ltm.json per project under ~/.clio/projects/<uuid>/. Each entry holds a tier of unverified or trusted, and the tier costs something in three places: a literal [UNVERIFIED] badge in the per-request memory block, a 0.3x weight in score_entry, whose one caller orders eviction when a type exceeds its cap, and a 30-day age-out against 90 with doubled decay in consolidate. Selection for the prompt is keyword overlap in score_ltm, which has no tier term. Promotion requires two corroborations from distinct agent:session pairs, and the unconditional override is reachable only from the /memory promote slash command. The docs name the threat: memory poisoning.

The identity is where it gives way. Until 31 July 2026 nothing assigned the two environment variables the source key defaults to, so no entry could be promoted; both entry points set them. The model's own tool takes source_agent and source_session as arguments, so two calls naming two sources promote an entry, and update_ltm rewrites a trusted entry's text without touching its tier. Committed relevance tests assert that an unrelated memory is not selected beside a control that is, which carries negative_eval.

ECC makes the honest declaration the rest of this family avoids, and reading it next to CSM is the point of putting them together. Its vault schema gives trust an enum of exactly one value — unreviewed — and documents why: verified knowledge is promoted into a governed artifact elsewhere rather than upgraded in place, so the store never claims authority it cannot support. Set beside the systems here carrying a confidence float nothing revises, a field that can only say "not checked" is the more truthful design. Its status enum is the counterweight: active | rejected | superseded is validated and filtered on both read paths, and every write sets active, so two thirds of the state machine is honoured on read and reachable only by hand-editing a Markdown file. That is the same defect as CSM's beliefs layer with the sign reversed, and the comparison is what makes it legible: ECC's unreachable states are the withholding ones, so the failure is that nothing can be rejected; CSM's unreachable state is the admitting one, so the failure is that nothing can be believed. In both cases a read path was written against a state machine nobody checked a writer could reach, and in both cases it fails by rendering less rather than by raising anything.

Skales's v7 snapshot, withdrawn from its repository and every commit in September 2026, has a deletion affordance that does not delete. Its memory page renders a bin icon beside every known fact; clicking it confirms "Delete fact «key»?", computes the object with the key removed, discards it, and shows a modal reading "Deletion not yet supported in UI. Ask Skales to 'forget the fact {key}' in chat." No forget verb exists in the application. Outside the locale files and fire-and-forget comments the word appears twice, and both point the other way — forget is a keyword that boosts action_item retrieval, and don't forget … is a capture pattern that stores a new memory. The request is saved as the turn's episodic record. The snapshot is a competent zero-LLM design: regex capture on a 90-minute watermarked scan, and retrieval scored 0.70 / 0.20 / 0.10 under a stated sub-100ms budget.

Logseq is the odd member and the only one here that is not developer-shaped — a twelve-year-old outliner that grew an MCP server, filed beside Basic Memory and llm-wiki-memory because it is the same bargain: a store a human authors, which an agent may now write into. It contributes the one thing no other system here has, which is a user-defined schema: properties carry a declared type and a cardinality, tags are classes that extend other tags and declare the properties their instances hold, and listTags/listProperties let a model discover that ontology before writing inside it. Everywhere else the memory model is the vendor's; here it is the user's. Its retrieval is also the most carefully gated in the atlas — exact title, FTS5 over a trigram tokenizer, a LIKE arm for two-character queries, fuzzy, and a local 384-dimension vector arm fused by reciprocal rank, with the expensive arms skipped when the cheap ones already filled the limit. And it is fully offline, embeddings included. The failure is at the seam: agent writes land live and unmarked — the schema defines a created-by-ref property that the MCP write path never sets — so the store cannot answer "what did the agent change?", and the agent has no delete verb with which to correct itself.

memsem is the family's — and this atlas's — clearest case of a published number that reproduces, attached to a mechanism that inverts. DESIGN.md §11 reports P@3 0.958 over 51 facts and 20 queries, together with an ablation across four alternative constant weightings and a section headed "honest reading" naming the set as author-designed and explaining why P@5 is low. From a clean clone, npm test reproduces every cell of that table offline in seconds. Set against the untraceable figures this atlas records for Memvid, SimpleMem, MemoryOS and FiFA, that is the standard the others are being measured against. Its correction design is the right instinct too: a contradicting fact does not overwrite its rival but multiplies its confidence by 0.6 — by 0.9 above a critical threshold — archiving below 0.25 and keeping the row, with a contradicts edge written between the two. It carries a rejected-value tombstone, and the tombstone sits on the door the extractor does not use. A person can park an uncertain fact as a candidate outside retrieval and reject it, which writes a durable suppression keyed on the normalised subject, predicate, object and project; every subsequent memory_add and memory_add_many is refused against that table before a row is written or a rival faded, and only an audited memory_unsuppress lifts it. That is the constraint the pattern argues for, built correctly and covered by an adverse-case suite. Automatic supersession writes no such record: archiving a value by attenuation and rejecting a candidate by review are two judgements about the same sentence, and only the second is remembered as a rejection. So the measured outcome against the repository's own milk/lactose example is unchanged — an ordinary correction is archived at the third re-assertion, and a pinned one, whose confidence never moves, stops being the top search result at the sixth while staying first in the CLI listing that sorts on the pin. A gate on one of two write paths is the whole of the tombstone argument, in a system that got almost everything around it right.

Cambium is the family's governance layer with no store under it, and it contributes the one check discipline this atlas has been asking for. It is a standard — twelve kernel modules, twelve runtime routes, twelve deterministic Python checks — for corpora maintained by LLM agents, and it ships no corpus. The repository selects no profile of its own — the governance placeholders in K00/03 are unfilled, so no vocabulary composes — while carrying a filled reference profile that binds all ten interface slots and passes check_profile.py. Two of its checks are built so that a run which examined nothing cannot be read as a pass. Executed against its own tree, check_freshness.py prints overdue=0 and fresh=0 side by side and concludes "NOTHING CHECKED — all 153 file(s) skipped… This is not evidence of freshness", and check_vocab.py exits 1 rather than assume a vocabulary. Beside that sits a prohibition most systems here would benefit from copying: file existence, a resolvable link, or a passing automated check MUST NOT raise any status axis — the tools emit only fail and candidate, so automation can block and nominate and can never promote a belief. Four status axes that must not be merged, and an evidence ladder from signal to validated with superseded retaining its reason, sit on top. The limits are the mirror image: with no corpus almost nothing is exercised end to end, most of the 6,453 lines of kernel are MUST prose with no script behind them, and there are 73 lines of tests over one of the twelve scripts.

Perseus Vault carries all seven marks and is the corpus's best answer to the question the benchmarks page keeps asking. 156,000 lines of Rust in one binary over one SQLite file: bi-temporal columns on the live table and its history with superseded_by, a SHA-256 hash-chained journal with a keyed MAC, workspace_hash applied in read predicates, trust split into a discrete status, a separate verified flag and a separate certainty float, an operator review surface, and a purge tested in both directions — the PII is gone from history and the journal, and the redaction did not cross the workspace boundary. Its headline 73.8% on LongMemEval is the mean of three independent full 500-question runs whose reports are all committed with dataset, split, pinned answerer and judge snapshots, temperature, commit, binary version, hardware and a run signature; the mean recomputes from those artifacts exactly, and so does the 79.0% it does not lead with, because the plain and chain-of-thought prompts are different official conditions and the answer prompt is folded into the run signature so the two can never be blended. Competitor figures are labelled as their publishers' claims rather than reproduced. It also ships a CLAIMS-AUDIT.md that retired its own "sub-millisecond recall" for lack of an artifact and downgraded "signed results" to "content-hashed". And the count claim it once carried in Markdown is now derived and enforced — scripts/registry_metadata_check.py parses the embedded registry literal as the implementation does, asserts the same figure across five published surfaces, and runs on every push and pull request beside a Rust uniqueness test, which is the difference between a number that agrees today and one that cannot silently drift. Its tombstone is the atlas's most privacy-careful: rejected_value_tombstones stores value_sha256 over a JSON-canonicalised, whitespace-collapsed, lower-cased form and never the value itself, so a rejection record cannot leak the content it suppresses — and it refuses at the write path rather than filtering at read, with a trusted override that is journaled. The remaining question is reach: the comment on remember_impl claims the check covers connectors and derived writers, and no committed test walks the background consolidation passes.

Provem carries all seven marks, and it got there from regulation rather than from a red team. Where Verel reached the same seven under adversarial pressure, Provem's pitch is that recall is solved and the hard question has become "am I allowed to use it?" — so it is a governance layer designed to sit over Mem0 or Graphiti as readily as over its own store. forget(term, scope) deletes the matching records, appends the term's token set to a per-tenant erased registry, and emits an erasure certificate; every later recall excludes any record whose tokens are a superset. That is a value-keyed, normalized, tenant-scoped tombstone — a looser and more forgiving key than Daimon's canonical-text hash — though unlike Daimon it suppresses at read rather than refusing at write, so the store still holds what a subject asked to erase. Recall returns a reason per excluded record, and the subject-scope rule closes the bypass explicitly: a query naming no entity must not be served a subject-scoped record, "otherwise scope isolation is bypassable by simply omitting the entity."

Its evidence practice is the strongest in this atlas. verify_repro.sh is a regression gate that re-derives every published number from frozen artifacts and asserts it verbatim; run here from a clean clone it reports VERIFY OK, 21 assertions, and 25 with --full. The LoCoMo dataset is not redistributed (CC BY-NC) but fetched against a sha256 pinned in the manifest, with an overwrite refusal on mismatch. Two of its four deployment tiers are published losing — one scoring 0.21 against a stated 0.24 no-memory baseline — a prompt confound gets its own replay step, the nominal recall win over Mem0 is described by its own author as "roughly a tie", the Zep comparison was configured to Zep's own published checklist, and docs/claim_register.md carries rows marked Unsupported and "Rejected for now". The counterweights are that the governance suite is self-authored, and that erasure suppresses at read rather than refusing at write — so the store still holds what a subject asked to erase, which is the sharpest thing to press on an Article 17 claim.

AMITY is the family's smallest member and the one that answers the opposite question. 634 lines whose distinctive mechanism is SovereigntyArtery, documented in source as "The capacity to say no": each heartbeat costs energy scaled by priority, the runtime refuses below a boundary threshold, and clearing requires recovery past that threshold plus a margin, so it cannot flap. The refusal is returned rather than raised and carries a reason string — running the shipped demo yields {'status': 'refused', 'reason': 'Energy depleted (0.05). Boundary active.'} — which is a distinction between refusal, failure and empty result that most stores here lose. Persistence is atomic, and the constructor refuses to initialize without a pilot_signature. What sits underneath is the finding: an EpisodicMemory is a timestamp, an event_type and a content dict with no identifier, and no update, delete, forget or supersede exists anywhere in the module, so the correctable identity this atlas's qualification test asks for is precisely what is missing. Most systems here can write a correction and fail to make it stick; this one cannot express one, and spends its design budget on whether to accept the write at all. Two things fail at HEAD: pyproject.toml is invalid TOML so the documented install cannot run, and a root amity.py byte-identical to the packaged module shadows it, so the three committed tests pass only when run from outside the repository.

MemoryOps AI has the strongest tenant isolation in this atlas by mechanism, and the closest near-miss on correction. Its _scoped opens every session with app.tenant_id and app.user_id set as transaction-local Postgres GUCs so row-level security policies enforce isolation "at the database, not just in application code" — a third and stronger position than the page on scope keys describes, because a query that forgets its predicate returns nothing rather than everything. Its audit is a per-tenant hash chain serialised through a head row so concurrent mutations cannot fork it into two valid-looking histories, with verify_chain exported so a caller can check it. Admission has four outcomes — save, drop, block, pending-approval — with sensitive content held for a human at /governance rather than stored, and the eval sets plant a memory under one tenant before asserting it is unreachable from another, which tests the property rather than the filter. Then deletion is record-keyed: soft_delete sets deleted_at, and the dedup lookup that would catch a returning value is filtered to status == active. The normalized key a tombstone needs is already computed, already persisted on every row and already compared — it is scoped to live records, so a value that was deleted and is later re-asserted lands as a new active memory with a legitimate-looking audit entry. The gap is one predicate wide, and it is the clearest illustration in the corpus that admission and rejection are different problems.

Klypix MCP is the family's answer to the seam none of the others touch: not one agent's memory across sessions, but several agents' memory across vendors. Its store is a brain.klypix — a ZIP of JSON committed beside the code — that Claude Code, Codex, Cursor and six other hosts read and write over MCP, and the format's parser ships under the same Apache-2.0 licence, so the memory outlives the tool rather than the session. A card has no status field. Whether a decision is current, superseded, resolved or consolidated is decided by the title of the container it sits in and a marker stamped into its own prose: isArchived is /^archive$/i.test(c.area || ''), repeated at roughly thirty read paths, and a death date for as_of time travel is recovered with a regex over the card body. That is an epistemic state machine written in regular expressions and spatial containment — the reason a human can retire a wrong belief by dragging a rectangle, and the reason renaming one container would make every archived decision read as current. Three things there are worth a reader's time regardless. Its committed benchmark runs a negative control first — writers that bypass the lock, which lost 17 of 22 cards on the reference machine — and declares the run inconclusive rather than a pass if the control ever loses nothing, which is the only committed benchmark in this corpus that makes its own sensitivity a precondition. Its consolidation pass cannot apply without an eight-character code that is a hash of the exact candidate set plus the day, never printed to the model and obtained by a human running a separate CLI — a gate whose own comment records that apply "used to be a bare flag the dry-run TEXT invited the agent to set — a model-proposes-model-approves loop with zero human in it." And test/archived-visibility.mjs asserts both halves of a distinction most of this atlas collapses: the brief and the per-prompt recall path must refuse to inject an archived card, while search must still return it, labelled — "the fix is LABELLING, not hiding." Against that, the trust machinery is one host deep. The git-blob freshness check on evidence anchors, the append-only capture ledger and the cross-project registry are all reachable only from src/global-brain-hook.mjs, the Claude Code adapter, so a brain_note from any of the other eight hosts mutates the brain and records no event. And the bin that survives a merge is keyed on card id, not on the claim, so a buried decision can be re-asserted later as a fresh card that nothing recognises.

Agent Mesh is the family's purest log-and-projection design, and the clearest case of a schema outrunning its write path. Its store is .agent-mesh/events.jsonl — an append-only SHA-256 hash chain, schema-versioned, with SQLite declared as a derived index that rebuild_all wipes and replays; the package has no third-party dependencies at all, pyproject.toml carrying an empty list under "Stdlib-only by design for core." Most of what the log carries is coordination — requests, responses, backlog items, dispatch leases — and the memory is the decision log: a record with a tier, an externalized Markdown body addressed by SHA, an alias table so a renamed decision stays resolvable, and a six-value status that gates supersession and drives tier-based promotion. Its best mechanism is the reverse transition. Editing a decision that is accepted or in_force through the Workbench requires a reason, emits decision_revisited, and folds status: [old, "proposed"] into the update, so the projection clears accepted_utc and the record must be accepted again — approval attaches to the content rather than to the row, which very little else here enforces. Its contract file is also the only agent-facing prose in this corpus that names the event kinds a future version will use, under "do not invent today", which is a cheap defence against a model fabricating a verb. Against that, three gaps compound. The verification apparatus — decision_verifications, decision_assumptions, decision_checks, decision_evidence — has tables, projections and a runner, and both shipped write paths hardcode those payload fields empty, with the one later mutation event unable to reach them; agent-q decisions verify executes a stored command with shell=True against a table the package cannot fill. The grounding packet an agent actually receives contains a prior-decisions section that is a regex for APPROVE|REJECT|GO|NO-GO over message bodies in the thread, not the decision store, so no decision reaches a model except by a query the contract asks it to run. And decision invariants are enforced only at replay, so a stop-line-violating event is durably appended and then aborts every subsequent read of a log with no delete. There are no tests.

Tradeoff: operationally simple and inspectable; no answer to hosted ranking, multi-tenancy, or rich user modelling. CSM is the exception to the first half and not to the second — Postgres, pgvector and a local embedding server to stand up, and still exactly one scope axis.

OpenWolf splits the family's usual arrangement in half, and the half it leaves unguarded is the one that can be wrong. Twelve hook registrations maintain everything mechanical — the file map, the per-turn action log, the bug index — with atomic writes, one read-modify-write lock around every JSON store and a secrets denylist that keeps .env and key material out of the generated files. .wolf/cerebrum.md, which holds User Preferences, Key Learnings, Do-Not-Repeat and a Decision Log, has no hook that writes it: it is written by the model when it obeys a generated Markdown instruction, nudged by a Stop hook, and re-surfaced — its three newest Do-Not-Repeat rules — at session start and every 25 tool batches. Until release 2.5 a weekly cron also replaced the file in full with a claude -p rewrite; that job was removed, and OpenWolf makes no model calls. The repository still ships a cerebrum_stale detector advising the operator to "check if cerebrum is being updated by hooks." Its read path is the part worth copying: pre-read.ts returns permissionDecision: "deny" for a full re-read of a file already read this session and unchanged on disk, once, with the denial disarmed after a compaction because the eviction makes a re-read legitimate. See OpenWolf.

agents-memory is the family's argument that a taxonomy can do the work ranking usually does. Its store is markdown under ~/.agents/memory/ and <repo>/.agents/memory/, and its layout is published as a specification rather than implied: sixteen kinds mapping to sixteen destinations, with abi/LAYOUT.md stating "One home per fact. Path encodes where it belongs. No dump files (facts.md, MEMORY.md)" — an explicit refusal of the single-file memory most of this family ships. abi/KINDS.md attaches a mutability rule to each kind — inbox, sequential, revise in place, frozen — and the decision rule is the sharpest supersession statement in the markdown systems here: "Revise present tense when the contract changes; new number when superseding." Retrieval is a case-insensitive substring scan that returns file.md:12 ids, and delete_memory accepts exactly that string, so a recalled line is something the agent can remove — the round trip most stores in this corpus cannot make. The same address is the weakness: the delete pops by line index, so one removal renumbers every id below it. And the mutability rule is not enforced — add_memory appends the bullet and then returns "revise this file in place when facts change; do not only append bullets", which is advice attached to the write it describes as wrong. See agents-memory.

Hatchdoor is the family's cleanest statement of what a document store buys and what it costs. A self-hosted Rust binary over an Obsidian-style Markdown vault, serving a web UI and an MCP server off one core: there is no extraction, no consolidation and no summarisation, so nothing in the store is a claim the system made and there is no derived belief to be wrong about. What it buys is bought honestly — atomic_write_inner makes renameat2(RENAME_EXCHANGE) the commit point, so the expected_content_hash check has no TOCTOU gap, and ADR-05 ships pure semantic retrieval because a measured comparison found a cross-encoder cost 5,198 ms per query while RRF hybrid lost MRR by turning rank-1 hits into rank-2 ones. What it costs is the other half of that trade, and ADR-11 states it plainly — "nothing is unlinked from disk by Hatchdoor" — so a delete moves the note into .hatchdoor-trash/, which nothing empties, no API restores, and a user ! negation can put back in the index, while stripping every wikilink to it from every other note along with the alias a human wrote. Two of its stated guarantees have no mechanism behind them: WATCH_MAX_DEBOUNCE is declared with a comment naming the exact hazard and is read by nothing in the tree, so a vault under continuous change defers reindexing indefinitely; and ADR-15 requires an eval run against eval/ before any retrieval change merges, where eval/queries.jsonl holds two queries. See Hatchdoor.

OmniMem is where this family's dead ends become a mechanism rather than a note. A self-hosted MCP server on Valkey for Claude Code, Cursor, Copilot and seven other agents, it lets an episodic memory carry effort, outcome and a graveyard of abandoned approaches; every recall begins with a keyword scan of that graveyard before anything is embedded, a hit is returned first at full score, an abandoned outcome is weighted ×0.1 whatever it cost, and an approach abandoned at effort 4 or 5 is suppressed as a topic automatically. The suppression is a Valkey set consulted on every recall that drops any candidate whose content contains a member — the read-path form of the rejected-value tombstone, and the report credits it, with the caveat that the key is a substring and hides the memory that recorded the abandonment along with everything else that mentions the word. The lifecycle is visibility, not belief: active, deprioritised with a reason and reinstate hints, archived, deleted, each a multiplier on the score, and no state says a memory is true. Contradictions are found by a negation heuristic over similar rows and recorded as links on both, and nothing in the tree removes a link — the dashboard's two resolution buttons archive one side and leave the other warning. The reason handed to archive is dropped, the force flag that skips the duplicate check also skips the contradiction check and fact extraction, and the skill compiler is the one writer that will not commit without a reviewed draft pinned to the body it was diffed against.

Reporecall puts a memory layer under a code index and keeps the memory's state in the wrong place. A Claude Code hook daemon — tree-sitter chunks, a call graph, FTS5 and vector fusion, an intent router — that from version 0.3.0 also indexes frontmatter markdown into SQLite FTS5: the project's own .memory/reporecall-memories/ and, read-only, the agent's own ~/.claude/projects/<root>/memory/, so the notes Claude Code keeps for itself are ranked by keyword, recency and access count into a 500-token block under per-class budgets with a code floor. Nothing is extracted from conversation and no model is called; the daemon's only automatic writes are a working file per prompt and a promoted fact after three retrievals. Compaction supersedes duplicate fingerprints and archives old episodes — in the index row, never in the file the README calls the source of truth, so a re-parse after any edit resets the status to active; the promoted fact shares its source's fingerprint and loses the next compaction's tie-break; the hook passes no scope, so a branch's working set reaches the next branch. One mark, for a committed case that keeps an archived copy out of a populated search. The tree is a copy at a third party's account: the manifest's repository returns 404 and the npm package it names ran on to 0.9.1.

ThoughtDAG makes the person's wires the retrieval, and records what the model was shown rather than what it concluded. An editable context graph — desktop app, DeepSeek Harness plugin, a read-only CLI over MCP — where a generation's input is a deterministic walk of the nodes wired into the question, the same function builds the panel's will send preview and the request, every answer version carries the model, the time and a fingerprint of its upstream, and a commit event written at dispatch carries the SHA-256 of the exact request into an append-only, metadata-only canvas log that survives undo and travels in backups. A stale answer is one whose upstream fingerprint drifted; it stays in downstream context with a mark, and a decision model that rules the change irrelevant removes the mark from the preview and the request alike. The memory a model writes is two profile documents, preferences and identity, admitted by a constitution in code and then merged by a model rewrite the constitution does not re-check, and one dossier per topic whose sentences cite the turns they rest on. Session Atlas mirrors Claude Code, Codex, DeepSeek Harness and Pi sessions read-only; the why layer indexes what each turn did, and recall brings dossiers and excerpts from every project on the machine into an ask. Two marks — the event log, and a hidden-reads case beside a benchmark whose conditions are graph operations and whose status file records its own corrections. Withheld, each a near miss: stale is a label not a gate, archive excludes on the canvas and not in the index recall reads, versions carry record time and no validity, the memory inbox is a gate the judge's topic probability routes around, and a working-directory filter exists that no caller passes.

no_human is the family's most fully lifecycled store, and its history is written into the code that fixed it. A ticket-to-pull-request coding agent — a plan, a coder on the Claude Agent SDK, an adversarial reviewer in a session that never saw the coder's transcript, a tamper guard, a reproduction gate — with a second brain of rules, skills, facts and anti-patterns in one SQLite table, 3,800 lines of learning code and a 21,785-line orchestrator that injects it. Nothing is written because a conversation happened: proposals come from a reviewer FAIL round distilled by a utility model, from supervisor corrections, escalations, repeated review failures and tamper trips clustered on a deterministic gist and proposed only at two occurrences, from mined transcripts and operator replies, and the one producer that fired on every success is gated off by default after it was measured to have produced roughly 394 pending rows out of a backlog of 487. A proposal is confirmed = 0 and reaches no prompt; a human confirm supersedes the oldest active near-duplicate in the same scope with a superseded_by pointer; since an operator directive of 31 August 2026 a harvest job also activates up to ten screened proposals per rolling day — dedupe, personal data, provenance, vendor terms — with a kill switch that restores the confirm queue byte for byte. Every exit through the queue is reversible and audited in an append-only learning_events table: pause, archive, a 45-day sweep of unconfirmed proposals, a 90-day retirement that can select only rows auto-activation itself wrote; a human's reject deletes a proposal from the outcome, review or reply path, whose producers cannot regenerate it without new evidence, while a batch producer's rejection archives and keeps the dedupe key so the next harvest cannot re-propose it — and outside the queue, nh rules remove, nh skills remove and their API routes hard-delete any row an id prefix resolves, with no audit row. Injection is one chokepoint — scope by a SHA-256 of the credential-stripped remote URL, tags matched at word boundaries against the task text and its planned files, a term screen, a ranking by importance, fourteen-day recency and normalised use under a ceiling of 25 — installed through a property whose setter re-screens, guarded by a test that parses the orchestrator's source for any other assignment and by a second test proving the parser catches the mutants that defeated an earlier guard. The reviewer reads a copy from which every auto-confirmed review-origin lesson is removed in code, so the gate never consumes a rule distilled from its own verdicts. What it does not do is search — nh recall is substring matching and the tag vocabulary is the retrieval — or measure effect: the injection-to-outcome ledger is labelled "CORRELATIONAL, NOT CAUSAL" in the migration that created it and in the CLI that reads it. Six marks; bitemporal withheld because every timestamp is record time.

Joplin is the family's other human knowledge base an assistant may write to, and every capability beyond the open note is a switch that starts off. A nine-year-old note application with sync whose AI service, added 12 June 2026, opens a chat on the current note with its body pre-loaded as a synthetic tool result, edits it through anchored tools whose batch is applied once and refused when the note changed underneath, and reaches the rest of the notebook through eleven global tools — read, keyword search with the app's filter grammar, chunk-level semantic search with strict, normal and loose presets over a sqlite-vec index, create, update, trash, tag, move, list — each behind its own setting, off by default, with a refusal that tells the model which setting to ask the user to enable. The index follows the app's own change feed under a durable cursor, collapses repeated edits per note per tick, drops trashed, locked and conflict notes, and rebuilds itself when the embedding model's id changes; a conversation that outgrows 80,000 tokens is refused rather than truncated; remote providers need a second opt-in and a LAN address counts as remote; a plugin API exposes chat, search and raw embeddings, and an MCP server, also off, hands the same gated tools to outside agents. What the assistant does not do is remember: the chat is panel state that a restart empties, a note carries no state or provenance, and the only history is the app's revisions with a ten-minute collapse and a ninety-one-day expiry. One mark, for a scope test written by a note's own id so that the check could not pass on an empty result.

SilverBullet is the family's write contract, and every mark is withheld on definitions. A Markdown wiki — a folder of pages behind a Rust server, a browser client that indexes and runs Space Lua over them, in development since February 2022 — whose file API returns ETag: "sha256:…" on every read, honours If-Match and If-None-Match: * on every write with 412 on mismatch and fail-closed on any precondition it cannot evaluate, and offers POST /.fs/{path} with base and proposed text so the server can fast-forward, run a bounded three-way merge under a per-path lock, or write both sides into the page between markers carrying each side's hash for a person to resolve (server/src/handlers/fs.rs:230-262,445-640, server-merge/src/diff3.rs:12). Each write carries the acting account, an opaque client id and an X-Source that is recorded when declared and ignored otherwise; the watcher tells the server's own writes from external ones by an expected-write record with a thirty-second TTL, and the revision engine commits each author's dirty paths to git thirty seconds after quiet with the account as author, SilverBullet for an unattributed local write and External for a change it detected but did not make, a coding agent on the folder included. The history is git, the conflict page is live content, access is per space, and nothing on a page carries state, so no mark is earned; what the report is for is the ETag contract and the merge-or-mark handler, which close the collision that the Logseq and Joplin reports leave open.

SiYuan is the family's most guarded agent, and the guard is a declared effect and a snapshot. A local-first block notebook — Go kernel, AGPL, in development since August 2020 — whose 3.8.0 release of 12 August 2026 added an agent over thirty-three native tools, each declaring per action whether it writes locally, sends data out or costs money; needsConfirm (kernel/agent/agent.go:1669-1707) waits for the person on any of the three unless they chose always allow, an external MCP tool that does not declare itself read-only counts as a write, and the first local write of a chat is preceded by an automatic data-repository snapshot whose failure aborts the round and whose id is recorded in the session (agent.go:1344-1375). Sessions are JSON files saved under an expected revision and a committing turn id with orphaned-turn recovery; compaction summarises older entries and injects the summary under a system message that names it untrusted historical memory; the MCP endpoint on /mcp requires the administrator role so the anonymous publish reader cannot reach it; semantic search embeds through a remote API and scans every block vector in pages of 4,096 into a heap with an optional reranker. Nothing is a memory with a state, the snapshot is a workspace history, and no test says what a search must leave out, so every mark is withheld.

Trilium Notes is the family's one-registry design, and the registry carries the flag a gate would need. A hierarchical note application — TypeScript, AGPL, since May 2017, continued as TriliumNext — whose twenty-one assistant tools are defined once with a Zod schema, a synchronous execute and a mutates flag (packages/trilium-core/src/services/llm/tools/tool_registry.ts:24-49); the in-app chat converts the registry to a tool set, the public /mcp route iterates it and wraps every mutating tool in a transaction behind an ETAPI bearer token and a limiter that spends its budget only on requests it would answer 401 to, the Claude Agent provider drives the person's own Claude Code with every built-in tool disabled and that MCP server as its only tools, and the Copilot provider gets the same server on a loopback port under a random path. The three content-editing tools save a revision with source: "llm" before writing (tools/note_tools.ts:119,162,222) into a revisions table that keeps the column, which is the one mark the store has of what the assistant changed; create, rename, move and delete leave none, and the spec stubs saveRevision to a no-op. No embedding, no index, no state on a note, and a protected flag that is encryption rather than scope: every mark withheld.

VISTA is the family's harness, and its memory engineering is about when a runtime may forget. An MIT harness from a group at MIT, one commit dated 5 September 2026, that plays the ARC-AGI-3 games through Claude Code or Codex CLI and archives every environment frame so inspect, read_pixels and history can return original evidence rather than the model's earlier description of it (src/vista_arc3/claude/controller.py:1066-1234,1327,1630); the model keeps a per-game GUIDE.md and WORKING.md, the harness stamps each WORKING.md write with the turn and state, archives it at every level boundary, and installs a PreCompact hook that answers block until the model has written a non-empty continuation checkpoint, then rebuilds a fresh context from the two files, the attempt history and the exact last event (compact_hook.py, controller.py:457-588). Every recovery path — compaction, credential rate limit, runtime restart, a fresh-session RESET — is a test that fails closed on missing or empty files. The memory is one game deep, nothing carries between games, the harness never reads what the guide says, and the history test's exclusion of private coordinates is a projection rather than a retrieval scope: every mark withheld.

Forgetful is the family's documented-versus-implemented case, and the gap is in the retrieval the agent is told to rely on. A self-hosted MCP server — Python, MIT, 236 commits from 20 October 2025 to the pin of 1 September 2026 — that puts 152 operations behind three meta-tools whose docstrings are built from the feature flags, stores one Zettelkasten-shaped row per memory with nine provenance columns in SQLite with sqlite-vec or Postgres with pgvector, and links each new row to its three nearest neighbours on create. The README's "dense → sparse → RRF → cross-encoder", the four-stage docstring on search in both repositories, and the recall skill's promise that identifiers are matched "literally" by "the sparse full-text leg" describe two stages with no implementation: rg -n -i 'fts5|bm25|tsvector|reciprocal|rrf' over the tree matches the README and the two docstrings. What runs is a dense top-20, a local cross-encoder on "query: q, context: c" when more than k rows came back, a one-hop walk ordered by importance, and an importance re-sort that discards the reranker's order before the 8,000-token cut. The ≥0.7 auto-link threshold stated ten times across the docs and skills is LIMIT 3 with no distance predicate. What holds: user_id and project membership are WHERE clauses on every read on both backends (the Postgres session sets an app.current_user_id variable no policy reads), obsolete rows leave the auto-link candidate set as well as the result set, an activity_log with full snapshots and per-field diffs is written by an event bus that is off by default and fire-and-forget when on, and the SQLite end-to-end suite runs the real embedder and cross-encoder in-process to assert an obsoleted memory stays out of a populated result. scope_enforced, audit_log and negative_eval; tombstone withheld because obsolete is supersession keyed on the row and a re-created twin is not refused, trust_state because a boolean and an unread float are not a state, human_review because the confirmations the skills require happen in the agent's conversation and leave no record.

Kwipu is the family's Graph RAG over a vault, and the finding is a triple that outlives its link. A local reader over a folder of Markdown notes — Python on LlamaIndex and Ollama, MIT, 24 commits from 21 April to 7 September 2026 — that turns every [[wikilink]] and frontmatter key into a relation in code, in six languages, before a model extracts up to twenty triples per chunk, and answers through four retrievers under one prompt that may say only what the context says and must cite files. An edited note is deleted by path and re-inserted, which replaces its chunks; its structural triples were upserted with no document behind them, so a link the edit removed stays a relation until a deletion anywhere in the folder forces the full rebuild the code says deletion needs. The MCP server exposes one tool in fast mode and never watches the folder, so an agent reads the vault as it stood at its first question. No mark, no test, no scope, and a generation model whose default is a cloud-routed id.

craft is the family's approval-gated harness, and its memory is a directory the person reads before the agent does. A Claude Code plugin — MIT, 288 commits from 1 May to 7 September 2026 by one author, 61 hook scripts, 85 bash test scripts — whose PreToolUse hook denies any Write or Edit outside .craft/ and .claude/ until a story, an adhoc flow or a workflow session opens a gate in a state file, and whose sibling hook on Bash approves every command outside a ten-pattern blocklist. Under .craft/, a learning is recorded with a source kind, a verbatim quote, a date and an occurrence count at status: pending, where the session hook counts it and no prompt contains it; /craft:reflect lists the pending entries, asks Apply all / Review each / Skip for now, copies the approved ones into .claude/CLAUDE.md, rules, hooks and skills for Claude Code to load, and marks them written. Locked design decisions and tokens arrive through a confirm step and a sole merge writer that reports conflicts per key; durable notes are indexed one line each into every session; loved tweaks are counted toward a taste pass. One mark, for the drain and the lock. Withheld: trust_state, because a skipped learning stays pending and a declined failure pattern is deleted, so there is no rejected state; and the decisions: field every story carries since 2.6.1 points at records under a directory nothing writes.

Pro Workflow is the family's case of a memory loop whose loading step reaches the wrong reader. A Claude Code plugin — MIT asserted with no licence file, 86 commits from 1 February to 18 July 2026, 38 hook scripts across 24 events — whose self-correcting memory is a SQLite learnings table with an FTS5 index under the home directory. A Stop hook parses [LEARN] Category: rule blocks out of the assistant's reply and inserts each with the project's name, and every read adds project = ? OR project IS NULL to its SQL, which earns the one mark. The session-start hook then reads the five newest learnings and prints them through console.error with no write to stdout, so under the harness's hook contract the person sees them and the model does not; the prompt hook's wiki hits go the same way. The self-correction rule says to wait for approval before persisting and the capture hook saves whatever block appears; times_applied is read by the optimizer and incremented by nothing; the replay skill greps two Markdown files no hook writes. One test file, on the skill optimizer.

teamai-cli is the family's team-scale knowledge base, and its lessons are documents published without review, and votes only credit one the transcript shows was read. Tencent's CLI — MIT, 720 commits from 3 March to 10 September 2026 by forty-four authors, 64,635 lines under 3,020 tests — distributes a team's skills, rules and knowledge from a git repository into ten coding agents. A learning is a Markdown document with frontmatter that contribute writes to a durable local queue and publishes to a shared teamai-learnings branch, with no merge request created anywhere in the tree; pull rebuilds a hand-built index that weights title tokens at three times IDF and tags at two, adds a vote score, and is searched project-first with the user scope admitted only on opt-in, which ten tests pin with the other scope's title absent beside the active one present. A second scope key sits inside the first: a project manifest names the learnings/ subdirectories a member's active projects own, and the copy filter, the index collector and the contribution's landing directory all resolve it the same way, so another project's learnings are absent from the file being searched rather than filtered out of it. An upvote counts only for a document the session's transcript shows was recalled, a confidence over counts and recency drives a prune that removes or archives and a promotion that rewrites, and a review command adjudicates machine-written wiki sections queued with a risk. Two marks. The merge-request importer computes which session learnings a draft supersedes and nothing consumes the list; and the two ways this tool forgets are different in kind. Deleting a rule, a skill or an agent appends its name to a committed .removed file that the next pull uses to delete every member's copy and the push scan reads so a stale copy cannot re-upload it — a record that outlives the resource. A learning has no entry in that list; what reaches a member is mirrorLearnings, which treats the local cache as owned by the repository and deletes whatever the repository no longer holds before rebuilding the index, so a prune travels but leaves nothing behind that a later contribution consults.

EvoX Genesis is the family's argument that a memory needs no store of its own, because the directory it describes is the key. An Elixir umbrella from the EvoX group — AGPL-3.0, 6,529 commits since 17 April 2024, 90,871 lines under 4,655 tests, paper arXiv:2608.10450 — that evolves a codebase across many finite-lived agent episodes rather than one long session. Every directory carries a CONTEXT.md holding its intent, constraints, design decisions, known issues and a routing table to its children, and the architect agent is told in as many words that these files are "the permanent architectural memory of the codebase" and that "there is no other place to encode architectural intent." Retrieval is the path: build_context/2 opens CONTEXT.md in each directory from the repository root down to the agent's assigned node and in no other, so a sibling subtree's knowledge is not filtered out of a result but absent from the text, and the same ancestor walk decides which skills are callable, since a skill is enabled only where an ancestor's frontmatter names it. That is one scope key that cannot drift from what it scopes, and with the review page where a person merges or rejects the branch carrying both the code and its description, it is two marks. Three things sit against it. The function that assembles the memory has no test among 4,655 cases and its one caller answers an error by substituting a bare path string, so an agent can run with no context tree and no signal. A rejection deletes the branch and records nothing keyed on what was proposed. And the archive refs that protect an episode's commits are off unless the run asks for them.

MemContinuum is the family's append-only decision chain, and it is the one here that enforces the property rather than asserting it. MIT, 232 commits between 30 August and 8 September 2026 by two authors, 13,029 lines of Python beside 1,605 test functions and 10,673 lines of shell. One markdown file per question; its body is an ordered list of rulings, newest first, each with its own date, status, kind and authority; a change of mind is a new ruling that names what it reverses and on which of three grounds. What separates it from every other store here that says the same thing is memlint.py --against-ref, which compares each ruling against itself at a git ref and errors on any changed field outside a set of four — a subtraction, so a field added tomorrow is frozen without anyone listing it — with three lifecycle fields allowed to move forward once and only alone. The store's own pre-commit hook runs that check and exits 1, and CI runs it again where --no-verify cannot reach. Authority decides what a record may do: only the owner's own or ratified words can fail a run, an evidence-backed finding is reported unless --strict-holds is passed, and a provisional ruling is never checked at all. Three marks. Project isolation is enforced by construction rather than by a filter: one database holds one project, and a second --project on the same file is refused outright with the reason in the error, which is why scope_enforced is withheld — a single-scope store cannot demonstrate partitioning — and not because anything leaks between projects. What does sit against it is that a declined ruling — with the option, the reason it was rejected and its own authority, frozen once written — is handed to the model before it edits the governed file and consulted by nothing, which is one lookup short of a rejected-value tombstone.

SAGE is the family's admission-by-vote store, and its status field withholds a memory rather than ranking it down. Apache-2.0, 1,614 commits since 2 March 2026 by nine authors, 218,303 lines of Go beside 4,941 test functions, on a vendored CometBFT chain. A submitted memory is written as proposed and every recall path hard-codes a committed status filter, so a memory nothing has voted on is not ranked low — it is absent. Confidence decays on an exponential curve with a corroboration bonus, evaluated at read time, and the floor is applied across the whole candidate set before the top-K trim, which is the ordering most implementations get wrong. Six marks. The sixth is the voter's dedup lookup, which matches any other row that has left proposed, deprecated rows included, so a memory the quorum rejected or an operator forgot keeps its exact bytes out until a reinstate; from v10.1 until 12 September 2026 the predicate was committed-only, because the form before it matched the candidate's own row and deprecated every memory on arrival, and the repair excludes that one row instead of every unaccepted one. Two things need saying plainly. On the default personal install the genesis has one validator and the vote is three string heuristics — a duplicate check, a length check with eight hardcoded phrases, and a confidence check — which the README's opening line states beside its consensus claim, adding further down that block inclusion is not memory acceptance. And two of the four papers rest on an experiment pipeline excluded from the tree for a licensing reason the papers index states; the commit that held it, 41 files and 6,609 lines, is fetchable by sha and is not an ancestor of the published history.

PLUR is the family's plain-text engram store, and it carries five of seven marks. Apache-2.0, 939 commits between 19 March and 18 September 2026 by thirteen authors, 68,932 lines of TypeScript beside 5,085 test cases, with engrams.yaml as the source of truth and SQLite, PGLite or Postgres attachable as a cache the code refuses to call truth. The mark that decides its character is trust_state: a commitment of draft makes an engram retrievable but never injectable, enforced inside the injector at both its selection and its spreading-activation pass, produced through an ordinary tool argument, and pinned by three tests. Beside it, validity time sits apart from record time and both are filtered independently; the scope filter's empty-grant case is documented in the source as a security rule and asserted; and every mutation appends to a monthly JSONL that is fsynced and deliberately never synced to a team store. Two things sit against it. A retired engram is excluded from the content-hash dedup by design, with a committed test asserting that re-learning forgotten text creates a new engram — which is why tombstone is withheld. And nothing in this repository can approve a draft: the schema points at a separate enterprise repository for the write sites, so the withholding is enforced here and the lifting is not.

OpenZync Core is the family's temporal backend, and its validity filter runs on every fact search and listing at an instant the caller can name. AGPL-3.0 with a commercial-licence file beside it, 648 commits since 5 June 2026 by two authors, 80,690 lines of Python beside 3,673 test functions on Postgres with pgvector. One LLM call per turn returns the classification, the entities, the fact triples, the structured extractions and the facts the turn contradicts, each applied in its own savepoint; a fact carries valid_from, valid_to and a separate invalid_at, and both search legs, the point-in-time and project readers and the conflict scan apply the same effective-at test, with an as-of parameter threaded to the graph backends. Five marks, among them a retraction gate keyed on the triple's value and consulted on both insert paths, and a negative eval that gives the superseded fact the same embedding as its successor so ranking cannot do the filter's work. Three findings. The project wipe stamps the retraction column on every fact, so every wiped triple is refused if it is said again and stays readable to an earlier as_of. The effective-at test is restated inline at most read sites rather than imported from the one shared expression. And the search SQL carries the project key and not the organisation key, over row-level security that binds only a role that does not own the tables.

Sibyl Memory is the family's answer to a question most stores leave to the caller: what a zero result means. MIT, 68 commits since 20 May 2026 by four authors, 16,000 lines of Python across five packages beside 1,055 test functions, in one SQLite file with FTS5, a folded-trigram fallback and no embeddings anywhere. Over the search sits a retrieve-then-verify layer that will abstain — on coverage below a threshold, on no anchor term, on a zero document frequency, or on a negation it will not reason about — and every zero carries one of five named causes onto the MCP wire, with a contract test in all five packages asserting no empty result ships an OK verdict. Three marks. The tenant key is in every read query, and the authors' own lock comment says what it is: a trailing post-filter on an unindexed column, with index-level enforcement deferred because the migration would break existing databases, guarded meanwhile by that comment and a named regression test — a better disclosure than most projects manage about a real weakness. Against it: four of twelve declared tables have no writer, one of them read by a shipped check using a column it does not have inside a bare except; the skill-review queue exists in the library with no tool, command or adapter reaching it, and the CLI command the docs name does not exist; and the README's claim that tier verification is the only outbound call omits a usage heartbeat the project's own other README discloses.

Auto Company is the family's smallest memory, and part of it belongs to a person. Fourteen agent personas driven by a shell loop that spawns a fresh session each cycle. The entire cross-session memory is one file, memories/consensus.md, pre-loaded into the prompt and rewritten by the model before the cycle ends; a successful cycle leaves a timestamped snapshot, so the series of past documents survives. A consensus-guard.sh and a consensus-format.py stand between the loop and the file. ## Human Overrides is compared byte for byte around every cycle, and a cycle that changed it gets the pre-cycle consensus restored and the loop paused — the system's one mark, human_review. An unchecked priority item blocks the loop before the model is invoked. Only one of the two human sections is defended by content: ## Priority Issues must exist exactly once, and what is under it is the cycle's to rewrite.

ripwire repairs a broken identity in the two files that carry it, rather than in the nine call sites that read them. An Apache-2.0 C++23 code-context engine at version 0.6.1 — 2,896 commits between 31 July and 16 September 2026, 172,653 lines across 167 files, with 618 gate scripts named by the loop in its own regression runner and all 618 resolving to a file. Most of it is a code index and out of scope here; what is in scope is the pair of committed sidecars, a ledger of 1,310 deliberately accepted quality findings and a field-notes file. The ledger is a ratchet — an ack accepts a finding "AT its acked size, never a blank check", so the moment it worsens past that magnitude it reappears, with a guard so a zero-magnitude finding cannot suppress forever. The ack key is a path, scope and symbol name, which a move destroys, and the project measured that against its own history rather than asserting it: ten headers moved into src/infra/ with no code changed, and not one of the fifty-nine canonical ids survived, nor any of the fifty-nine path-qualified keys, nor any of the four clone groups. The repair is the transferable part, and it is an argument about where a fix belongs. Teaching every baseline lookup and the ratchet to try an alias "means touching nine call sites inside computeDelta plus applyAckRatchet, each an independent chance to get the direction backwards"; so instead the two sidecars are rekeyed forward into the current tree's identity, once, before anything reads them — by the git-recorded rename map first and scrubbed-content-hash equality second — which makes the delta, the ratchet, the stale-ack classifier and the ack writer rename-aware with no edit at all, and --quality-ack writes the healed rows back so the rescue stops being needed. The route that saved each row is recorded per row rather than as a total, because surviving through a recorded rename and surviving through content equality are "different claims with different trust." identitycheck.sh pins the boundary with three claims that may not be traded for one another, the third being that an ack must not survive a rewrite. Against it: a stale ack is classified and never acted on, the notes half has none of that care — two rows, no rescue route, no write lock, and nothing in the format able to say that one of them has stopped being true — and the verb advertised as returning "most relevant memory notes / docs for a task" does not read the notes store at all.

LWC is the family's two-store CLI, and the half that earns its marks is the one under the wiki. Apache-2.0, 294 commits between 29 July and 5 September 2026 by two authors, 123,311 lines of Rust across 139 files beside 868 test attributes, in one SQLite file per scope. The visible half is a source-grounded wiki whose pages carry a four-value ordered provenance — source-grounded, user-provided, agent-observed, hypothesis — rejected at the boundary if unrecognised, decorated onto every search result, and filtered on by nothing. The half that works is a temporal memory of fingerprinted event capsules: recall is FTS5 inside an event-time window, and every lexical hit is walked forward through the supersedes edges so a replaced memory returns its successor, labelled current or superseded, with the history one flag away. Four marks. Two couplings are worth copying: eviction protects anything pinned or carrying a fragment of kind unresolved, so the open questions are the last thing forgotten, and every remember returns up to three maintenance hints — a contradiction needing review, five events sharing an exact type and context, an unresolved fragment past fourteen days, storage past eighty per cent — each suppressed for seven days by a cooldown row a prune pass clears. Against it, the same disease twice: valid_from and valid_until are written, cross-validated so the start is not later than the end, carried through sync and returned by memory_show, and appear in no WHERE in the tree; recorded_at is never a predicate either, so bitemporal rests on the occurred_at axis alone and there is no as-of-record-time question. Its benchmark discipline is the better half of the story — LongMemEval-S and V2 and the Agent Memory Leaderboard contract, upstreams pinned by commit and the dataset by a printed SHA-256, with a limited run stamped partial=true and no numbers claimed in the tree.

continuity v2 indexes the transcripts coding agents already keep on disk, in one SQLite database and fourteen files. MIT, 25 commits between 30 April and 14 June 2026 from one author, 2,589 lines of Python across fourteen files, one SQLite database, nothing pushed in the three months before the pin. Where Deja Vu strips secrets as it indexes and pond marks each message part conversational or injected, this one flattens every message — prose, [tool:<name>] calls, [result] bodies truncated at five hundred characters — into a single searchable string, then partially takes it back by skipping any turn whose text starts with a tool marker when it builds embeddings, so the lexical arm searches tool output and the semantic arm does not. No marks, each checked with a recorded search: the whole tree contains one occurrence of status, confidence, verified, superseded, rejected or tombstone, and it is an HTTP response status. Two things are worth the read anyway. thread_recall seeds from three FTS5 matches and walks strictly adjacent TEMPORAL edges eight hops in both directions, returning the conversation around a hit rather than the hit alone, and deliberately excludes the similarity edges from the walk so the thread stays continuous. And drift_check.py answers the question a derived index usually cannot — am I behind? — by mirroring the reindexer's skip logic exactly, writing nothing and exiting 2 on drift, with fts_integrity_check beside it for the FTS mirror. Against that: no tests at all, no redaction of any kind, a compaction checkpoint written to one fixed path whose reader checks its age and never the session id recorded in its own first lines, an edges table created twice with two different schemas under CREATE TABLE IF NOT EXISTS, a turn_vecs table keyed on an autoincrement id every re-index discards with no delete path for the orphans, and a hardcoded Windows path under a specific username in a hook the README says resolves on any platform.

Memspec makes a claim about code accountable to the code, and it carries six of seven marks. MIT, 146 commits between 4 April and 20 August 2026 from two contributors, 9,710 lines of TypeScript against 8,640 lines of test holding 321 cases; memories are markdown files under git, with a disposable SQLite FTS5 cache beside them. Its thesis is one line of its own README — "calendar TTL is the wrong signal for facts about code" — so a claim records the git blob SHAs of the files it depends on, reconcile re-hashes them including uncommitted edits, and a drifted claim is flagged for a person rather than archived. Three read-path decisions are the reason to read it, and each is argued in the source. The scope predicate sits in the same WHERE as MATCH rather than over the returned page, because a post-filter "would starve small scopes out of existence" — and the test that proves it seeds sixty out-of-scope records outranking two in-scope ones on every BM25 signal, with a comment recording that removing the predicate makes that test and only that test fail. The graph walker cannot traverse through an out-of-scope record, because traversable-but-unreturnable leaks the foreign graph's shape at depth two or three. And an unrecognised --scope throws rather than answering, because "a scope nothing matches doesn't return 'no results', it returns 'unscoped and universal records only'… silent under-retrieval is the worst possible failure for a memory store." Beside those: the FTS index is built from the active set alone, so a superseded record is absent rather than ranked down; valid_from/valid_to are queried with --as-of and kept explicitly orthogonal to the check_by review schedule; removal is one interactive prompt per candidate and "deliberately CLI-only: removal is an operator act, not an agent surface." tombstone is withheld — the duplicate refusal keys on an exact title within a type, records nothing about the rejected write, and accepts the same claim in different words. And one mechanism is declared and unconsumed: memspec init still writes min_confidence: 0.7 and a ranking weight over confidence, a field v0.3 removed, and the value travels three layers without being compared to anything.

Somnigraph tunes every parameter against measured retrieval data, and the thing worth taking from it is a failure it published. Apache-2.0 under a Commons Clause — not an open-source licence, and the LICENSE file says so above the Apache heading — 191 commits between 6 March and 27 July 2026 from one author, 5,382 lines of Python against 3,869 lines of docs and 188 per-system research analyses. The retrieval stack is RRF over FTS5, sqlite-vec and a theme channel, then a UCB exploration bonus over an empirical-Bayes feedback prior, a capped Hebbian co-retrieval term, Personalized PageRank expansion that replaced naive adjacency, and a 31-feature LightGBM reranker with a hand-tuned formula beneath it; consolidation is split into an NREM merge phase and a REM gap-analysis phase, and decay runs on per-category half-lives from thirty days to a hundred and seventy-three. Two marks: an auto-captured memory is written pending and every read filters to active; memory_events is an append-only table carrying lifecycle mutations — updated with the changed field names, superseded with its successor, edge_weight_change with the weight before and after — beside its retrieval events. The queue between capture and retrievability is not a review mark, because review_pending is an MCP tool whose confirm_all lets the agent promote every pending capture in one call. Two near-misses are stated by the project itself: dedup_rejected records a refused write with the distance that refused it and is "measurement, not gating" by design, one lookup short of a tombstone; and valid_until is set when a memory evolves and appears in no WHERE. What makes it worth reading is the documentation. Every tuning constant carries the study that set it, its previous value and the measured delta. Missing features are NaN-encoded under a written policy, because a default that looks like a measurement is a bug a model will learn. And the architecture page records that from 7 April to 1 July 2026 the learned reranker silently was not loaded — retrieval ran on the formula "through the entire V5 documentation arc, which describes offline eval numbers for a model that was not the one serving live queries." One result there generalises past the project: surveying 59 entries from the same public comparison directory, a README-level triage returned 0 promising / 7 maybe / 50 skip and a code-level read of the same 59 entries returned 13 / 41 / 5, correcting the triage in both directions — "light-touch triage demonstrably under-counts." Against all of it: eleven assert statements in the tree, all in a benchmark harness, and no scope key of any kind.

Slowave rebuilds its memory from an event log, and the rebuild does not know what a person deleted. AGPL-3.0-or-later with a commercial licence offered separately, 534 commits between 8 June and 24 September 2026 from one author and a release bot, version 0.20.4, 33,193 lines of Python against 34,645 lines of test holding 907 cases; the memory core makes no LLM call at all — ingest, consolidation and recall are geometric over a local ONNX encoder. Removal is a hard delete a person performs at the local dashboard, outside the agent's five MCP tools: the schema row goes, and the recall items, the feedback events naming it as target or replacement, and the JSON fields mentioning it are scrubbed in the same transaction. As erasure it is more thorough than most stores on this page, which leave a deleted memory's id scattered across tables nobody sweeps. Procedures are the exception: their delete strips the payload from its raw event and leaves the summary, steps and originating query in a procedure-search projection added on 24 September 2026, which the dashboard's Home page keeps counting. What it leaves is nothing to consult: no status marks a claim as one a person rejected, no row says a deletion happened, and because the delete is keyed on the schema id rather than on the claim and leaves the raw_events it was formed from, the automatic rebuild that follows a logic-version change wipes schemas and can re-form it with nothing positioned to intercept it. The migration that introduced this states the trade in its own words — "[f]orget/unforget was removed in favour of explicit hard deletion. Preserve previously suppressed schemas by restoring their recorded prior state before removing the obsolete audit table" — so a store upgraded across that boundary returns to ordinary retrieval the memories a person had suppressed. Beside it: four lifecycle statuses gated per retrieval mode and applied identically on the direct and the graph-expansion paths, though update_status coerces an unrecognised value to active rather than refusing it; a generalization ladder that makes cross-scope reach something a memory earns rather than a flag somebody set, with the widening rule written into the SQL predicate itself; a raw_events spine stamped with the logic version that ingested each event, so an algorithm change is a scoped replay rather than a migration; and a delete preview that counts the evidence links, relations and co-activations a removal will take with it, in front of a person who has five MCP verbs none of which can delete. bitemporal is withheld on an absence: every timestamp here is a record time. Against it: the published LoCoMo and LongMemEval figures are LLM-judged evidence containment whose raw records are not in the repository, the LongMemEval run is an oracle configuration the page itself says is not a distractor test, and installation runs a setup module that writes configuration into eight clients.

UltraContext takes the transcripts coding agents already keep on disk and writes them back out, in another agent's own format. 148 commits between 7 January and 1 June 2026 with 144 from one author, 7,705 lines of TypeScript and Python and 383 test cases. A local daemon tails the session files of five agents, and the package ships writers as well as parsers, so a session can be materialised in Claude Code's or Codex's own on-disk format and resumed there natively — a stronger form of continuity than the search-and-paste the rest of this family offers. The store is git-shaped: one nodes table, three link columns, a context that is a chain of version heads each owning a chain of messages, and an update or message delete that appends a head recording its operation and the ids it affected rather than rewriting anything — so history, time-travel and fork-at-version are one mechanism, which is what earns audit_log. Three marks, and three findings. The project key reaches every ordinary read and a committed test pins it, but findRootContextByPublicId sits one line below the scoped resolver in the same interface and takes no project — and its only caller is the fork source resolution, so a caller holding one project's key can pass from: another project's context id and receive a copy of it; the ids are twelve random bytes, so the barrier is possession rather than the key, in a product built to circulate ids between teammates. The sync daemon redacts normalized.raw and ships normalized.message — the conversation text extracted from those same bytes — untouched in the same payload, so a pasted key is sk-*** in one field and intact in the other, and no test in the tree mentions the redactor. And AGENT_COMPAT declares which CLI versions each parser was verified against and which resume pairs have fixture coverage, with isResumePairTested exported and called only by tests, while switchSession picks its writer with target === "codex" ? writeCodexSession : writeClaudeSession — no default, no check. Four marks are absent rather than withheld: it is a context store, and nothing in it ranks, forgets, or knows that something it holds is no longer true.

ContextMeld is the family's notebook, and its one mechanism is the question of which notes apply. A desktop app — Tauri, Rust, SQLite — that indexes Claude Code and Codex history and keeps memories only a person writes: title, body, tags, and a scope of global, project or agent held in by a CHECK. Nothing extracts, infers or decays. They reach an agent through one path, a handoff package a person carries from one agent to the other, and the builder offers only the memories whose scope matches the session — a filter pinned by a test that loads another project's memory beside a matching global one and asserts the first is not offered. The predicate sits in the React component over an unfiltered page from the backend, so the boundary belongs to the screen rather than the store.

Kept is the family's Markdown memory with its own model, and its two read paths disagree about scope. A Rust binary that embeds a root of Markdown notes on the CPU, wires a prompt hook and MCP into seven coding agents, and generates one MEMORY.md per project, bounded to 17 KB, that a session loads at start; a committed test renders a populated index and asserts the archived note is not in it. The hot index follows the project directory. The prompt hook, which runs on every request, ranks every note in the root, so a question in one project can be answered with another's. And the write gate refuses a near-duplicate only of an active note, so a superseded fact can be written back as new.

Hivemind has the family's cleanest isolation test and no retrieval by meaning behind it. A Go daemon serving memory over MCP to every harness on a machine: entries carry a session or user scope and a session id, both read arms filter on them in SQL, and a committed test puts identical vectors in two sessions and a shared entry and asserts the other session's entry stays out. The session predicate drops out when the caller's id is empty, and any caller may write the shared scope. The only embedder is a non-semantic hash and the store keeps neighbours within an L2 distance of 1.0, so text search finds a memory only by its byte-identical content. A query with no text runs exact tag and external-id filters instead, and the project's own GitHub Actions log cache is built on that path.

Uteke measures its retriever carefully and forgets its own corrections. A single-binary Rust engine — SQLite with FTS5, a usearch HNSW index and EmbeddingGemma on the CPU behind a CLI, an HTTP daemon and 46 MCP tools — whose default recall is a weighted RRF of a vector ranking and a vector-plus-FTS5 ranking; the committed LongMemEval-S raw output recomputes to the README's 98.4% recall_any@5, 492 correct out of 500. The namespace is a real predicate, in SQL on the lexical arm, and two CI-run tests assert that a same-text row from another namespace and a deprecated row stay out of a populated result. Every retirement — forget, supersede, contradiction-on-write, dedup, aging — is the same soft delete: point-in-time recall never returns a deprecated row, the timeline records supersessions but not edits or background deprecations, a thirty-day prune removes the row with its edges and events, and a committed test asserts that the retired text is accepted again as new. Downstream of the fusion, cosine-scale boosts and thresholds are applied to RRF scores that top out at 0.044, so at the defaults neither shipped auto-recall hook injects a memory.

Claude Self-Reflect indexes Claude Code's own transcripts and then spends most of its engineering on the two problems that creates. One 78,201-line Rust binary with SQLite, FTS5 and an in-process HNSW index, six session hooks and fifteen MCP tools, and no service to stand up. The first problem is reflexivity: a memory system used inside the sessions it indexes will index its own retrieval output, and the importer answers it by binding each suppressed tool_use id to its tool_result and dropping both, scrubbing its injected reminder blocks from user messages only so that prose about the tool survives, and counting each kind of scrub into a column status reports. The second is staleness, and the answer is the most transferable idea here: a standalone codewitness crate stamps a BLAKE3 hash of a symbol span at a git commit into an append-only ledger, and a six-hourly cycle joins those stamps by commit-graph ancestry — no model call, no wall clock — emitting anchor_obsolete, superseded_by or anchor_reinstated, and abstaining by name whenever ancestry cannot settle the case. What that machinery buys is a label, not a filter: apply_validity_partition ends kept.extend(demoted) and apply_resolutions ends unresolved.extend(resolved), so a chunk proven stale at HEAD still returns at the tail of the page, and on the prompt-injection path it carries no annotation at all. Nothing is ever deleted — no tool and no CLI subcommand removes a chunk or a reflection, and the only forget is deleting the data directory. The evaluation is the other reason to read it: a pre-registration with a pre-committed interpretation of failure, sealed rosters whose SHA-256 seals verify, and a results file reporting the project's own flagship walk losing to hybrid kNN and FTS, 0.581 against 0.813 over 396 receipt-lookup queries.

Signet AI is the cross-harness case, and it is the one that will not let a consolidation pass write without a receipt. Hooks and plugins for ten harnesses stream transcripts into one SQLite file as immutable episodic rows — a migration states the rule, that saves are evidence and "Only Dreaming derives semantic state from episodic rows" — and a dreaming worker wakes every five minutes to turn them into entities, aspects and grouped claim values. Every content operation must cite a quote, and citeEvidence resolves that quote against the episodic store in the target's own agent scope, rejecting the whole batch before any write when it is not a verbatim substring and recording the failure as quote_mismatch or scope_mismatch for retry. A deterministic content-safety policy scores every memory, artifact, transcript and summary clean, tainted or blocked against six named reasons, and recall joins the ledger so instruction-shaped rows never reach a prompt, then re-scans the text it is about to return. agent_id with a three-valued roster read policy is SQL on every recall arm, and a committed test writes the same sentence under two agents and asserts the other one's access counter never moved. What is not backed is the number on the badge: the README's LongMemEval figure has no committed artifact, the dataset is fetched at run time, the in-tree ledger opens "not a publishable benchmark claim" over tables whose largest sample is twelve questions, and no workflow runs the suite the marks rest on. Captured transcripts also reach SQLite with no credential scrubbing, which is the thing to know before pointing it at ten harnesses.

GrayMatter gates its own README against a live benchmark, and admits what the number does not mean. A single static Go binary — bbolt facts behind an inverted index, a persistent embedded vector store, and RRF over keyword, vector and recency — reachable as a library, a CLI, a seven-tool MCP server, a socket daemon holding the single bbolt writer, and a TUI. The marketed 90% token reduction is real and narrow: the benchmark measures recall at a fixed budget against concatenating every stored observation, and the project's own documentation says "A system that returned 8 facts at random would score an identical 90% reduction here", publishing beside it the row where GrayMatter costs more than a sliding window at an equal fact budget. What makes that durable is machinery rather than candour: a test parses the token and quality tables out of the README and fails them against a fresh measurement, the reduction column at zero tolerance, a sibling test fails any quality metric no code computes, and a revision-currency gate fails when its own control arm stops reproducing the problem. Correction is the strongest mechanism — an update latches a supersession so a decay pass holding a pre-retirement snapshot cannot resurrect a corrected value, a race the project's own lifecycle simulation found. Three things are missing rather than wrong: nothing is keyed on a retired value and the write path has no duplicate check, so a forgotten string written again is live; the audit trail has a producer and no reader anywhere in the tree; and the untrusted-data framing built carefully in one prompt module has a single caller, so the hook path the README leads with emits a bare memory heading instead.

marm-memory keeps its human verdicts in the one place a rebuild does not reach, and that is the idea worth taking from it. A local-first MCP memory server over one SQLite file, serving fourteen tools identically over HTTP and STDIO. Memories are log lines that got embedded — one tool is the only agent path into the semantic store — and they have no trust state, no validity time and no mutation record; forgetting is a hard delete that leaves nothing behind. The derived concept graph above them is where the correction machinery lives: removing an entity in the bundled console writes its name into a suppressions table before the row is deleted, every extraction resolves names through a function that returns nothing for a suppressed one, and the reset drops entities, relationships, code links and build runs while deliberately keeping the three review tables — so a full rebuild cannot resurrect a concept a person removed, and a committed test asserts exactly that across a reset. Derived state disposable, judgements about it durable, and the judgement keyed on the value rather than the row id. Two things weigh against it. Every memory is HTML-escaped at write time with no inverse anywhere in the tree, so a snippet containing an angle bracket or an ampersand is stored, embedded, indexed and returned in escaped form — in a system whose headline feature is an exact lexical lane for config keys and file paths. And scope is a predicate on all four read queries but nothing on the write path: the project name is the working directory evaluated once at import and the explicit-scope flag has exactly one caller, the console, so on the shared server the README recommends for multi-agent work every agent's memories carry the server's own directory name.

Mnemon puts the model outside the binary and then lets one materialised column decide what gets deleted. A single Go binary over one SQLite file per named store implements MAGMA's four-graph model (arXiv:2601.03236) — temporal, semantic, causal and entity edges under a CHECK constraint, with recall as an intent-adaptive beam search whose widths, depths and node budgets are tabled per query type — and it calls no model of its own: importance arrives as a CLI argument, links arrive as a command, and mnemon setup --target <host> installs a skill file telling the agent when to use each. The discipline around the write is real. One transaction covers the diff, the soft-delete of a replaced row, the insert, all four edge builders, the score refresh and the auto-prune; a replacement needs token overlap above 0.6 as well as a high cosine, "because a high cosine is not enough evidence to destroy an existing memory"; and the prune's audit row goes through the error-propagating RecordOp whose comment says a destructive transition and its evidence must "commit or roll back together". The gap is one level down. Effective importance is a genuine decay — base weight, log-scaled accesses, a half-life, a small edge bonus — stored in a column, and AutoPrune, which fires inside every remember over capacity, orders by that column without recomputing it; the only corpus-wide refresh is GetRetentionCandidates, reached through the agent-invoked mnemon gc. Where nothing runs gc, automatic deletion ranks rows by the score each had on the day it was written — the one moment every row's decay factor is 1.0. Two marks, and the audit log is a 5,000-row ring that logs retrievals beside mutations. Its most copyable habit is documentary: docs/design/08-decisions.md tabulates six deviations from the paper it implements, with the paper's choice beside the implementation's.

flow writes its memory policy out in full and hands every part of it to a model. A Go task manager for Claude Code and Codex keeps task, project, playbook and owner state in a properly constrained SQLite schema, and keeps its memory in five markdown files — user, org, products, processes, business — that the binary seeds, lists by path, counts lines in for a statistic, and never parses. What governs them is SKILL.md §4.10: five buckets with the phrases that trigger each, an exact - YYYY-MM-DD — <paraphrase> entry format, and six numbered guardrails including deduplicate-by-reading-first and never edit an entry because the file is an append-only log. flow done spawns a headless close-out sweep whose prompt adds three bars a fact must clear — durable in three months, surprising or non-obvious, future-relevant enough to change a later decision — and says the quiet part out loud: "The expected answer for most files on most tasks is 'no'. Don't reach." The binary verifies none of it, which its own comment states: "Substance gating is delegated to the LLM." What it does instead is the transferable idea. flow stats parses the harness's own session transcripts, classifies every tool call, and counts each Read under /.flow/kb/ as a knowledge-base lookup — an instrument for the one thing prompt-directed memory cannot guarantee, and the answer to the enforcement gap this pattern's entry in §5 of the report names. It carries no capability mark, which here means the seven were looked for and the store has none of them. The recorded flaw is that the shipped artifacts disagree about the read path: the hook and the skill say the files are lazy-loaded on demand, while the README, a comment above the code that prints them, and the sweep prompt say they are loaded — the prompt telling the model they "sit at the top of every future task brief", which is the premise its strict bar rests on.

Prism is the family's answer to a question the rest of it does not ask: is the memory telling the agent the truth about itself? An MCP server with 41 tools over one libSQL file gives a coding agent its last session back — summary, decisions, files changed, open todos — behind a three-tier search that degrades from native vector_distance_cos() to a quantised scan in JavaScript to FTS5 keyword matching, each tier documented in a reviewer note in the source. A fallback chain like that is where a memory usually starts misleading its caller, because the query still returns rows and nothing errors. Nine *Honesty and *Contract test files sit on exactly that seam: a result set carrying lexical ranks must be labelled "hybrid retrieval" and not "semantically similar", and a lexical-only rescue must render as exact-term match (lex#3) rather than N/A similar — "correct data, misleading presentation", as the docstring puts it. One of them records an outage worth carrying: getHealthStats hardcoded missingEmbeddings: 0 on both its success and its failure paths, so the health check reported HEALTHY through an incident in which "100% of 8,560 rows lacked a vector and semantic search returned nothing for every query", and the fix returns an explicit -1 for unknown — "never a fabricated zero". The scope mark is the strong form, because searchMemory opens with an unconditional l.user_id = ? whose value is a module constant read from the environment rather than a tool argument, so a model cannot name a tenant. What it does not have is the other half of correction: live handoff state is versioned with a snapshot per version and a git-like memory_checkout that restores forward, while a ledger soft-delete writes a column and records nothing, and the one table named memory_access_log holds retrievals — the half that cannot be wrong in the way a mutation record can.

basemode is the family's RDF member, and its source comments audit its own wiring with dates and quad counts. A Rust binary maps a workspace — projects, decisions, rules, handoffs, and an AST-derived graph of the code's own functions, callers and imports — into oxigraph over one N-Quads file per tier, and injects a slice at each of the four Claude Code hook moments rather than waiting to be called. Correction is a supersession edge with a forward-walking resolve_head, a write-time cycle refusal, and a FILTER NOT EXISTS spliced inside the GRAPH group of recall, both rules blocks and the prompt neighbourhood. src/supersede.rs's header records that ops:supersedes and ops:supersededBy had been declared in the ontology for months "and nothing has ever written either one — measured on Chris's store, 2026-09-07: 0 quads of each across both tiers", with the only supersession being a sync-ledger JSON field "which no graph reader can see". The module exists to end that, and it is wired — a CLI verb produces the edge and six queries filter on it. The same header separates the edge from the label and justifies distrusting ops:status with a census of the author's own store — 44 distinct values against the six the ontology declares, including Pass 242, PASS 43 and PASS (no change) 1. The label is a lifecycle field with no candidate or verified value, which is why trust_state is withheld, and it is read: the writer adds superseded without removing active, so the session-start memory block, which selects active, serves a corrected note beside its correction. Two marks, audit_log on a changes.jsonl appended after the atomic rename lands from the single write funnel, and negative_eval on test files whose header states the vacuity rule — "a filter that excludes everything passes a 'the old one is gone' assertion just as well as a correct one" — and assert both halves per serving surface, which makes it a third corpus instance of an exclusion asserted about a corrected value. Scope is the near-miss: writes route into a per-workspace named graph that no hook query filters on, and the one read-path predicate keeps the working set's projects to the current workspace by a home derived from ops:path. Bug #112 is the named-graph difference biting the removal path — a wildcard guard passed a rule sitting in a foreign named graph, "460 quads of them on the reporting install", while the scoped DELETE matched nothing and a hardcoded Ok(1) reported success. Licence is the Functional Source License 1.1 with an Apache-2.0 future licence: source-available at this pin.

claude-mem-lite treats retraction as the memory's core invariant and nearly holds it everywhere. A Claude Code plugin on one SQLite file: hooks batch tool calls into episodes that Haiku turns into typed observations, and FTS5 with synonym expansion and pseudo-relevance feedback recalls them into every prompt. A save can retract earlier observations, one live predicate keeps tombstones out of every read path, and a retrieval benchmark gates CI. The automatic save's dedup still reads tombstones, so a resembling capture is dropped for a week.

EGC gives twenty coding tools one memory, and keeps it in two stores with different guarantees. An installer registers a memory server, a command guard and a session bus into each tool. Project state is a Markdown document per git branch, encrypted and integrity-checked. Decisions and lessons are plaintext SQLite rows that record their project, and search and recall return every project's rows.

projectmem keeps a repository's memory as the debugging record itself. Issues, attempts and their outcomes, fixes and decisions are appended to one JSONL log, revised by supersession computed at read time, turned into pre-commit warnings, and flagged for a person when a cited file moves. Lessons are promoted into a machine-wide store other projects inherit, which neither records their source nor follows their corrections.

StrataGate will not let a summary outlive its source. A DeepSeek Harness plugin over SQLite that seals each block of conversation verbatim before any model call, derives decaying summary layers, event cards that separate mention from occurrence time, and a current-state graph, and reinforces memory only from receipts of evidence an answer used. The graph hides disputed facts; the per-turn injection still renders superseded events without saying so.

agent-memory-mcp gives engineering memory a steward and a review step. A Go MCP server for decisions, runbooks and incidents beside a document index: session close becomes a plan a person can read before it applies, and a steward queues uncertain merges, conflicts and drift for review. Its recall as of a date and its knowledge timeline both run on recall that has already hidden superseded entries, so neither reaches the past they are meant to show.

VelesDB keeps the model off the write path and puts the graph behind every answer. A Rust vector, graph and columnar database whose velesdb-memory MCP server stores atomic facts with no model call, returns the evidence trail behind a recall, and compiles context under a token budget. Optional extraction builds entity hubs whose attributes and relations record no source fact, so forgetting the fact that stated them leaves them on the entity.

memex promises an update in the one surface a model is guaranteed to read, and cannot perform one. An MIT Python tool at version 0.2.4 — 10,249 lines with 297 test functions — built on three claims: "The filesystem is the memory · the index is disposable · every session is provable." The first two hold. A memory is a Markdown page with YAML front matter, SQLite FTS5 is a cache that rebuild-index reconstructs from the pages, and a page edited by hand in an editor is reconciled rather than clobbered — the stale content_hash is detected and rewritten, by an explicit rebuild or a watcher. The third claim is the rarest thing here: memex verify turns "should have used memory" into an exit code, failing a build when no page parses, an index row is stale, a [[link]] dangles, or — with --require-recall and --require-write — nothing has been read or written since a cutoff. Almost nothing else in this corpus tries to prove memory was used at all. The write path undoes much of it. WriteInput has no slug field; Memex.write leaves the slug empty; WikiStore.write therefore always derives a new one through unique_slug, so the same title written twice becomes deploy-on-fridays and deploy-on-fridays-2, both live, both retrievable, with nothing to reconcile them. The store's update branch — which preserves id, created, access_count and last_access — is reachable only from transcript ingest and JSON import, and the consolidator's own updating = bool(node.slug) and self._store.exists(node.slug) tests a slug that is empty by construction, so nodes_updated is structurally always empty while the CLI and MCP tool report its length. Meanwhile memex_write's description, shared across adapters precisely because "the description is the one guaranteed-read surface", tells the model: "Writing an existing slug updates it, preserving creation history and access counts." One mark, for a forgetting test that pairs its must-not-retrieve assertion with the same query under include_expired=True. valid_from is validated, stored in front matter, given its own indexed column, and appears in no WHERE clause in the repository.

yacmemo writes the duplicate guard that anticipates its own evasion. A personal Markdown memory server, MIT by metadata at version 0.2.0 — 11,833 lines, 4,764 of them Python, with 95 test functions and its documentation in Chinese — running one process per machine with an MCP mount per user, so any client joins by URL with nothing installed. memory_write refuses a near-duplicate title, and the comparison runs on a normalizer that first strips trailing dates, -2-style counters, v1, 更新 and (新): the rename an agent would reach for to slip a second copy past the check is exactly the transformation it collapses. The refusal names the near-matches with their scores and tells the caller to edit instead. The override is the better half — force=true writes and records a forced event, but once forced writes in the last 24 hours cross a configured threshold a second flag becomes mandatory, under a comment reading "human-confirm semantics, deterministic and fully counted in guard_events" — so bypassing is possible, visible and self-limiting rather than free. The second stance is rarer still: nothing is hidden. No status, tier, score or expiry anywhere in the search path withholds a note; when two collide, both are returned and the hit carries a ⚠ naming the other and suggesting a merge, leaving the judgement to the reading model — which the design can afford because the memory subsystem contains no generative LLM at all, only one 0.6B embedding call. Two marks. A collision's open | resolved | dismissed status is set solely by the web console's own route, documented as a "human decision", and the search path reads collisions_for(path) at its default open, so a person's verdict silences the warning on every later hit. Three committed search cases assert absence: a deleted note from every channel, a dismissed collision from its warning, and one store's content from a second store. The record is where it thins — the only log covering every mutation is git, guard_events holds refusals and forced bypasses alone, and call_log is written by the MCP tool wrapper, so a note deleted in the console leaves no row in it.

LEVH separates the gate deciding no from the gate declining to decide, and gives the second one a table. An AGPL-3.0-or-later local memory layer at version 2.31.0 — 54,347 lines, 26,342 of them Python, with 896 test functions — pitched on forgetting (a decay factor and a stability in hours per memory, reinforced by recall) and on sharing one SQLite file between every MCP client on a machine. Its admission gate returns admit, redact, review or reject, and the docstring refuses to merge the last two: "reject is the gate deciding — too short, or a near-exact duplicate … so nothing is lost by dropping it. review is the gate declining to decide: the candidate is close to an existing memory but not identical, which is exactly the case where the difference may be the part worth keeping." held_memories is the store behind that third answer, and its schema comment names the defect it fixes — "Without it the verdict had no store behind it and the content was dropped, which is the one thing a memory layer must not do quietly." A held candidate has no embedding, no score and no decay, never appears in recall, and "becomes a memory only when a human admits it"; the admit path re-stores with force=True because the decision "is the human's, and it overrides the gate by design", the row closes only after the memory exists, a discard keeps the row, and the transition is a compare-and-set. That is the mark. What is missing is the surface: admit_held_memory and discard_held_memory are reachable from one HTTP route each, with no CLI command, no MCP tool, and a console that shows the queue only as a count — while capture, connector sync, export and a librarian finding all report the backlog and none can act on it. The finding's own text says these candidates never enter memory at all if nobody decides.

Huiran-cerebro decides which duplicate survives, and ships the decision behind a command that only previews it. An MIT Python personal memory hub at version 1.5.0 — 4,865 lines over one SQLite file with FTS5 trigram tokenisation for Chinese, bge-small-zh-v1.5 embeddings fused by reciprocal rank, an MCP server with a written Doubao guide, and a web console. Its deduplication makes two decisions in one docstring: fragments whose content Jaccard crosses a threshold have the later writer marked status='merged' — 不删原文, the text is not deleted — and the first creator is kept "信息源", as the information source, which is the right call when the later copy is a restatement rather than a correction. A dry_run flag returns the candidate pairs with their scores. The one caller, lifecycle --dedupe, hardcodes that dry run, so no shipped entry point writes the mark; were it written, the keyword search behind the MCP tools applies no status predicate. No marks. There are no tests, the scan is pairwise over every active fragment, and namespace exists on fragments only, with nothing to set it.

light-mem strips its own injected context block back out before it stores a turn. An Apache-2.0 TypeScript memory for Claude Code, Grok, Codex and OpenCode through one shared worker — 74,394 lines with 1,778 test cases across 153 files. Redaction happens before storage rather than as a read filter, and the stripper removes six tag families: private, light-mem-context, system_instruction, system-instruction, persisted-output, system-reminder. The advertised one is privacy; the two that matter more are the tool's own memory block and the harness's reminders, because stripping both means it does not re-ingest what it and the host wrote into the prompt — the self-reinforcement loop OWASP's guard names as a threat, closed here at the single point every captured turn passes. Its privacy check also fixes a conflation with the reasoning in the code: an absent user_prompts row "is NOT a privacy signal — treating it as 'private' silently freezes EVERY observation for the session", so a missing row ingests with a visible warning while only a row present-but-empty-after-stripping suppresses, each case carrying its issue number. No marks, and the reason is structural: there are two stores and only one is audited. The session store the hooks write — observations, summaries, prompts, vectors — keeps no mutation record, while the audit_log table with its actor and action belongs to a newer server schema written from the v1 HTTP routes, bridged to the older one by legacy_observation_id and legacy_table columns. Nothing in either schema withholds a record from a read or supersedes a claim.

mnemonic hands back the count of what its recall held out. An Apache-2.0 TypeScript MCP memory server at version 0.45.0 — 62,345 lines with 1,686 test cases — storing notes as markdown with YAML frontmatter committed into the repository, with local gitignored embeddings, no database, and a semantic git commit per write. Alongside the ranked matches its recall returns suppressedGlobalCount, the weak global matches the project gate withheld, and widenedScope, set when the gate lifted because the admitted pool came back empty — with the response text carrying "weak global matches suppressed" in words. A retrieval that silently drops candidates below a threshold leaves its reader unable to tell "nothing matched" from "something matched and I decided against it"; two counts fix that, and the tests assert them in both directions through the real tool. One mark, for the pairing that does it: an off-topic global note asserted absent while the in-project note is asserted present on the line above. The scope argument runs opposite to the usual: gateActive = scope === undefined && project !== undefined, under the comment "[e]xplicit scopes run fully ungated" — omitting the scope is the stricter path, and passing one removes the gating, which is a relevance heuristic rather than a boundary. Confidence is derived from git signals with every weight, threshold and half-life a named constant, and supersession steepens a decay curve rather than withholding: a superseded note keeps returning, ranked lower, until it fades.

NouGenShards writes the argument against its own guards into their docstrings. A source-available Python memory at version 1.3.1 — 97,044 lines with 1,491 test functions — whose first act is nougen brain scan: walk the machine for the traces Claude, Gemini, Cursor and Codex already left and import them into local SQLite. Its command gate says "[t]his is a defense-in-depth speed-bump, NOT a security boundary … it can be trivially bypassed by obfuscation (encoding, indirection, aliases, etc.) and must never be relied upon as the sole protection against malicious input", and its sandbox says "process-level isolation (no parent env, no shell), NOT a full security sandbox", refusing untrusted callers unless an operator opts in. This atlas has reported regex denylists presented as guarantees; this is the first whose author argues against relying on it. Capture redacts credential-shaped text before hashing, embedding, indexing or encryption — "so neither SQLite nor an embedding blob preserves a recoverable copy of a leaked credential" — which is the ordering that matters, because an embedding computed over a secret is a recoverable copy of it. Private and secret bodies are AES-256-GCM encrypted before reaching SQLite, with the edge stated: "[t]itles and tags stay plaintext: they are the only handle recall has on an encrypted shard, so keep identifying detail out of them." One mark, for two clocks declared where the second was added — timestamp is event time and learned_utc is when this node stored it, filtered independently by as_of, event_after and event_before, with imported traces stamped at their true era rather than migration time. domain_key is derived from the working path on write and omittable on read, so it organises rather than isolates.

Edda refuses to write a chain break, and refuses an approval banked before the gate opened. An MIT-or-Apache Rust ledger for coding agents at version 0.6.2 — 213,521 lines across a dozen crates with 3,518 test functions — built for two failures it names exactly: the session dies and the reasoning dies with it, so today's agent proposes Postgres again; and an agent dies mid-task and the work state goes with it. Its event log checks the chain on append rather than only in a later audit: read the current tail, refuse an event whose parent does not match, then re-derive the event canonically and reject it if the taxonomy, hash or digests disagree with its content. verify_chain walks the log afterwards and reports the first break by event id and index, and four tests inject real corruption through raw SQL — a broken parent, a first event with a parent, a tampered payload, a tampered taxonomy — which is what separates a hash chain from a hash column. Its authority module keeps the root key, capability record and bearer "outside ledger events and continuity bundles", so writing memory cannot mint authority, and names its own edge rather than implying none: "[a] malicious process already running as that same owner remains outside S6a's boundary." And a verdict binds three ways — to the subject, to the full SHA, and to time: "a verdict only satisfies a gate if it postdates the gate's gate_entered_at. Approving a subject BEFORE its gate opens (a pre-recorded verdict) therefore does not work", which closes the approval an agent would otherwise bank in advance. One mark. No human-review mark: edda verdict approve|reject records an actor string the caller supplies, with no authentication on that path and no requirement to hold the sealed capability — the binding and freshness halves are the best here, and the identity half is absent.

codemem subtracts the dangerous flag from the caller's type. An MIT TypeScript coding memory for OpenCode, Claude Code and Codex — 375,155 lines across a dozen packages with 290 test files, SQLite with FTS5 and sqlite-vec, automatic injection that does not repeat memories already in context. Scope visibility is an opt-in filter flag, documented as opt-in "so low-level filter unit tests and non-memory callers can keep using the pure filter builder, while store/search paths can make scope visibility a hard invariant" — and every real read path passes true, with caller scope filters "always intersected with the central scope-visibility gate" so an argument can shrink the set and never grow it. The mark is earned by the last mile: SemanticSearchScopeContext = Omit<OwnershipFilterContext, "enforceScopeVisibility">, under a comment saying why — "[d]eliberately omits enforceScopeVisibility so semantic callers can never disable the local read boundary" — so the path most likely to forget the flag cannot express forgetting it. One filter catalog is pinned to the MCP tool schemas by an exact parity test, because otherwise "exclusion filters can never fail open and return broader results than the client requested". And its attribution layer is unlike anything else here: retrieval attempts and exposures are ledgered, an assessment is labelled helpful, irrelevant, stale, harmful or unknown on one of seven bases, and a claim is typed observational or causal — with the second refused unless the basis is a randomized contrast whose witnesses are retention-pinned and carry both experiment.cells_complete and experiment.uncertainty_reported: "causal claims require a linked preregistered randomized contrast with complete retained cells and uncertainty". That layer is not wired up, and the file says so: "[t]hese pre-writer validation gates define initial v1 semantics", with no production caller outside its module.

Hungry Hippa found that telling a caller what it withheld was the leak, and wrote the finding into a test. An MIT Python memory runtime of 15,028 lines over local SQLite with an MCP surface, whose tests/ directory names attack classes as filenames — existence oracle, confused deputy, injection framing, trust boundary, trust token, quarantine, supersession, provenance, file permissions, resource limits. The existence-oracle file records what the project discovered about itself: recall returned excluded=[{"item": "belief:B-0002", "reason": "other-actor"}] for a topic matching a protected memory and [] for one matching nothing, and "[t]hat difference answers 'does the operator hold a memory about X', and the row id leaks sequential identifiers. Graph entity names were returned unfiltered too." The exclusion disclosure this atlas praised in mnemonic a few reports earlier is, unchanged, an oracle for an unauthorised caller — the difference is who is asking, and one list for both decides that the question does not matter. Two marks. The read policy that followed states its rule rather than implying one: "[t]he operator and the runtime's own background work read everything; an untrusted caller reads only its own rows. Identity decides, never a label" — so the actor_id a caller passes grants nothing by itself. Status partitions the pipeline before ranking, with quarantine behind an explicit flag, and the score explanation is built so it "never contains memory content, so it cannot leak quarantined or otherwise unauthorized text", closing the same side channel a layer up. The mutation log is enforced in code rather than by triggers, which the schema states and justifies, so a new write path can omit it.

Cortana checks that an access list is shaped like one before it trusts it. An Apache-2.0 Rust second brain at version 0.58.2 — 138,872 lines with 605 test functions, reachable as a desktop app, an MCP server, a loopback HTTP API and a CLI — whose product statement is a list of refusals: "not an unrestricted crawler, implicit backup service, agent harness, or hosted personal-data warehouse. A new installation starts query-only", with eight capabilities named as eight separate explicit decisions. Its search gates on an active status, on both ends of a validity window against a supplied moment, on project, kind, content type, retention tier and scope, and on a flag a caller must set before owner-global rows are reachable at all — and then, before matching the row's ACL, validates the ACL's shape in the same statement: json_valid, json_type='array', and no element whose type is not text. Only then does it admit the empty-means-unrestricted branch, so a corrupted or wrongly-typed access list matches nothing rather than degrading to public. That is the failure direction a lenient application-layer parse usually gets wrong. Three marks. observed_at, the valid_from/valid_until window and created_at are separate columns; a correction writes a new row carrying supersedes_id and the superseded row keeps its place while leaving the active set; and observations stage as expiring candidates with a rejection_reason under a compare-and-set. No human-review mark: the approving principal is a string, and a rejected candidate's dedupe_key is not consulted when the same content returns.

Cortex fails its own build when the README's numbers stop matching the repository. An MIT memory server for coding agents at version 4.22.0 — 274,911 lines, fifty-four MCP tools over one stdio server, a local SQLite file by default or Postgres with pgvector — promising accountability rather than intelligence: "[k]eep decisions, fixes and project context between sessions, and inspect what was retrieved", with "[n]o LLM in the retrieval loop, and nothing leaves localhost unless you configure an integration that does." Its check_doc_claims.py gate compares "every advertised count against the one place that owns it" and runs "at the point where the drift is introduced (every push and pull request), not at release time" — and its exemption mechanism is the better half: a line stating a number that is not the advertised total declares [not-a-count-claim: <label>], and "[t]he declared set is a registry: it is printed on every successful run and pinned by a test naming each member, so an exemption is added deliberately or not at all." One mark: memories.superseded_by_id is published as a current_memories view that both recall paths join, so a corrected row keeps its place and its link to the correction while leaving what an agent is handed. Above it a wiki layer carries three more CHECK-constrained status machines, with a partial index on just the working ones and synth_prompt/synth_model recorded on every model-written draft. That draft queue is not a human-review gate: wiki_curate promotes through evaluate_draft, described as pure logic, and reviewed_at records when rather than who. No scope predicate was traced on either recall path. OKF Agent Memory has three frontmatter fields that read like trust state, and the only one retrieval consults promotes rather than withholds. An MIT Go implementation of Google's Open Knowledge Format v0.2 — 10,217 lines over 27 files, no third-party dependencies, memory kept as markdown under knowledge/ in the repository being worked on. A concept declares status (draft, stable, deprecated), stale_after as a date, and governance (context, constraint, hold), and they are declared on adjacent lines of the same struct, which is what makes the divergence worth tracing. status and stale_after are parsed, validated and serialized back, and no read path consults either: okf search ranks a deprecated concept exactly as it ranks a stable one, and a concept past its stale_after — which okf validate warns about — is still handed over at full weight. governance is the one search reads, as governanceRank(gov) * 10, a multiplier that ranks a hold concept first; and hold is documented as "execution freeze / manual signoff required" while the freeze is a line of bootstrap prompt text telling the model to stop. What it does earn is an audit the README undersells: it leads with git diff and git log, which record what somebody chose to stage, while knowledge/log.md is written by the library on every concept write and both MCP handlers pass the flag that controls it as a hardcoded true.

memhtml gives three actors one tree and lets only one of them settle a contradiction. An Apache-2.0 TypeScript system — 132,835 lines over 393 files — storing memory as "a git repository of semantic HTML5 files, one fact per file", with a rebuildable SQLite index over it and four retrieval arms fused by reciprocal rank. The rule the design rests on is written out: "The agent writes facts, and it resolves only the conflicts it found itself. Sleep curates on a branch when a caller fires it, and it detects conflicts without resolving them. The human owns the gate and every one-way door." A seventeen-phase nightly pass commits to sleep/<date> and leaves main untouched, each phase committing separately "so a human reads the curation one phase-shaped diff at a time", distilled transcripts landing one commit per claim, and any phase that could decide and declines opening a task that quotes the sentence it found — "[a] detection is a proposal for a human, never a fact the corpus asserts." The merge fast-forwards only after a gate that re-runs the retrieval evaluation, whose adversarial controls are the project's own merge veto read backwards: the three predicates that forbid folding two memories together become the three ways to build a plausible impostor, because "a control the veto cannot see does not test anything". Two smaller ideas travel well — one scope filter string handed to every arm, since "[p]er-arm filters would let a scope apply to three arms and not the fourth… No type catches that leak", and an empty result that reports how many archived rows the same scope matches, so an agent can tell "never existed" from "archived". What it does not have is a mutation record outside git: the phase trailers and the one-commit-per-claim discipline live in a history a rewrite can edit.

Ulpia treats its own differentiating claim as an obligation to measure. An Apache-2.0 Rust router over markdown the user already has, with "no embedding model, no network, nothing in the path that improvises" — and the cost named in its third paragraph rather than its appendix: "[t]he price is writing, and it is paid per note. Each one carries a hand written Search for: line, roughly thirty terms… Nothing infers it for you." Its abstention benchmark states the reasoning most projects skip: "[e]very retrieval system this one competes with always returns a rank one, because ranking cannot express absence. Ulpia's differentiating claim is the refusal, and a claim that differentiates is a claim that must be measured or it is marketing." It then reports two numbers rather than one — the decline rate on questions the corpus should refuse and the false-decline rate on questions it should answer — so neither can be bought with the other, and bounds the result under a heading addressed to the reader who would over-read it. Against that, it is the third system this page has read in short order whose epistemic vocabulary is recorded and then ignored at retrieval: every note carries stage: raw | distilled | derived, with a rung deliberately protected from being added, and no read path consults it.

gaius bounds its enforcement pass downward before it says what the pass is for. An Apache-2.0 Python ops memory of 29,474 lines in one offline SQLite file, extracting facts from four coding agents' sessions and ranking them into an inject-ready corpus. Its corpus_audit reclassifies flagged facts from auto to pending, and the module header states the ceiling first: "DEMOTE-ONLY — never tombstones, never DELETEs", touching "ONLY review_state" so the field recording why a fact was believed survives, and reversible because "an operator flips review_state back to auto to undo". The states then do different jobs — a read filters review_state != 'rejected', so a rejected fact keeps its row and leaves the corpus, while a pending one stays retrievable at a 0.6x penalty — which is the distinction a single confidence number cannot express. Its deduplication is careful in the same direction, folding rows that share a fact key into the oldest while summing confirmation counts and unioning the agents, sessions and principals, so the agreement between independent runs survives the merge. The gap is the one this page keeps finding: domain filters most reads, and maturity.py builds the clause as "AND domain = ?" if parsed.domain else "", so one path returns the whole corpus to a caller who supplied nothing.

CogZ writes its status lattice down as an enforceable machine and then routes the person around it. An MIT Rust runtime at version 0.1.5 — 31,088 lines over 145 files — giving a coding agent "persistent memory, contextual retrieval, and continuous cognition about a software repository", with markdown under .cogz/ as the record of truth and a disposable SQLite projection over it. src/storage/status.rs states the legal moves in its own doc comment — active to stale, rejected or superseded; stale back to active; rejected and superseded onward to pruned; pruned terminal — and transition_status implements exactly that table, returning a typed IllegalTransition that names both ends. The read paths honour the column broadly, which is the half most systems skip: the batch fetch and entity query filter status = 'active', the usage and doctor sweeps exclude status != 'pruned', and KNN candidates are dropped by status before they can "consume KNN slots". What the lattice does not govern is the door people use. transition_status has one caller, update_status, and that has one production caller, which passes the literal "stale" — a move the machine permits from active in every case. A person edits the frontmatter instead, and EntityFile::parse checks the new value with is_valid_status — membership in the five-word list — before handing it to an upsert that writes status = ?6 without consulting the table, so a file edit can go from active straight to pruned or back out of the terminal state. The rejected value shows the gap at full width: no production code writes it anywhere, and dedup then declines to consult it by design, on the stated ground that "a new observation that matches a rejected one is not a duplicate" — so a claim a person threw out can be recorded again verbatim with nothing to notice. Its event log is the sturdier instrument, append-only with a single writer and edit rows carrying old_content, though the cascade delete nulls entity_id and keeps the payload, so a pruned entity's former text outlives the row it belonged to.

Leteo writes down what each decision cost to find out, and its epistemic fields are each one step short of doing work. An MIT Rust memory at version 0.2.1 — 73,418 lines across 121 files — keeping one local SQLite file with no embedding model and no network in the memory path. The commentary is the distinguishing feature: the migration that adds a second full-text index publishes what each tokenizer can do rather than announcing the index, measuring that a question with two of six words re-inflected is answered "63% of the time here and 0% by the same store searched without a stemmer", that quoting six words verbatim finds the memory first 78% of the time stemmed against 84% unstemmed, and that fusing the two by rank position reaches 84.3% and 37.0% — alongside the cost, 5.1 MB of index on a 46 MB store and 0.04 ms added to a save. The bug this project has learned to name is a rule written in more than one place, and the comments carry the damage each time: REVIEW_WINDOWS was folded into one place only after "a third hand-written copy of these names let policy keep a window nothing could fire", a fourth vocabulary was consolidated for the same reason, and a benchmark that wrote its own copy of the search query "was measured for an afternoon before anybody noticed the product never issues it". That discipline also explains the query shape: Narrowing::equals writes a clause when there is a value and "nothing at all when there is not — never a clause that has to be true for every row", because a parameter inside a disjunction costs SQLite its index at plan time, 5.7 ms against 0.015 ms. The consequence is that scope and project are narrowing arguments a caller may omit, and the tests pin it — "an empty scope is not a scope filter", with "a real scope narrows" beside it as the control. No mark lands. The state a reader sees is computed in Rust after the rows are back, so needs_review labels a memory that was returned anyway; the only predicate that withholds is deleted_at IS NULL, and it sits in both dedup lookups, so a deleted memory is invisible to the checks that would notice it being written again. The relation row records marked_by_actor, marked_by_kind and marked_by_model — the provenance shape this atlas rewards — and every shipped writer supplies agent or system, so the field built to tell a person from a machine has no path that writes a person.

MemoryWhale decides a lesson's approval from who wrote it, and refuses to run against the one schema where that filter could be skipped. An MIT Rust debugging memory at version 0.10.0 — 34,341 lines across two crates and a Tauri shell, everything local — where the evidence layer is captured command runs and transcripts and the retrieved layer is the lessons distilled from them. The approval flag is set at write time from the author's kind rather than by the caller: remember hardcodes "human" and is what the CLI calls, while remember_as with "agent" carries the MCP client's name, so a person's lesson is approved because a person wrote it and an agent's waits. Review mode defaults to on — review_agent_memories() ends .unwrap_or(true) — and its documentation frames the switch as opting into "automatic approval" rather than out of review, which puts the burden on whoever loosens the gate. Every reader then filters approved = 1 AND status = 'active', and the seam between write-time and read-time is defended rather than assumed: the loader reads the table's columns from schema metadata, "not by treating an arbitrary prepare error as a legacy-schema signal", and a database carrying the lifecycle column without the approval column is rejected as unsupported — "status exists without approved; review filtering cannot be enforced" — because no released migration can produce that shape. The lifecycle beside it says three different things about why a memory stopped being offered: expired by a sweep past expires_at, which "stops surfacing them without deleting the evidence"; stale by judgement; and superseded in favour of a named replacement, written only after refusing a memory that would supersede itself. Its compaction rulebook is pure, with thresholds passed in so tests can pin every boundary and a named reason on each decision, opening with the rule a debugging memory needs most — "Failures outrank successes. A failed command or an errored run is exactly what future-you will search for, so it is never auto-compacted." Against it: rejection from the review queue is a DELETE and nothing is keyed on the lesson's text, so a proposal a person threw out returns as new; no table records mutations, so an approval, a supersession and a rejection leave the same trace; and the project and machine keys sit behind (?1 IS NULL OR column = ?1), so a caller that says nothing reads across everything.

Rememora sizes the cap on an LLM consolidation by what a wrong answer would cost, not by what fits in a prompt. An MIT Rust cross-agent memory at version 1.7.0 — 17,313 lines over 56 files with 345 tests, plus a Tauri app — keeping one SQLite database "shared by every agent you use", on the complaint that Claude Code, Codex and Gemini CLI each lose context between sessions. Consolidation asks a model to merge, supersede or keep a cluster of similar memories, and the two constants around it are the design: MAX_CLUSTER_SIZE is eight and MAX_SUPERSEDE_PER_DECISION is five, the second documented as "the blast radius of a single LLM call ... deliberately much smaller than MAX_CLUSTER_SIZE so that even a fully-hallucinated decision can only retire a handful of records." A cluster over the limit is not trimmed and processed but refused before a token is spent, handed back as "review it by hand", because clustering is transitive and a large cluster is more likely "one bad edge chaining unrelated memories than a genuine pile of duplicates". A decision that fails validation is discarded whole rather than partially applied. Writes are armed rather than assumed — a dry run unless --apply or REMEMORA_APPLY=1, with --dry-run beating both — and the applied supersession writes an evolve_undo row in the same transaction, carrying the retired ids, the survivor and the literal SQL that reverses it, so "a decision that rolls back leaves no journal row claiming it happened". Beside it curator_log is the DDL's own "audit log of every curation action performed by the curator", naming the reason and the model; neither table has an UPDATE or a DELETE anywhere in the tree. The strongest signal is a removal: v1.7.0 stopped wiring lifecycle hooks into Claude Code and Gemini CLI and ships code that strips them from installs that already had them, so curation runs from a command "never from an automatic hook". Retrieval withholds on one stored bit — superseded_by IS NULL on the full-text arm, the vector arm and the timeline — with the replacement named on the retired row. Against it: the epistemic vocabulary is that single bit, set by a model, with nothing for a disputed or thinly-evidenced claim; the undo journal is stored and printed but never executed, so an undo is a person running SQL and the cluster-size constant still calls consolidation irreversible; and the project prefix is applied only when a project is supplied, in a database whose whole point is being shared.

LeanKG refuses an unrecognised scope because the reference it was ported from did not. An Apache-2.0 Go code knowledge graph of 97,573 lines serving 30 actions behind three MCP tools, with a plain-markdown memory layer beside the graph: bounded MEMORY.md and USER.md core files, unbounded topic notes, mnemopi-compatible JSONL banks and an FTS5 index the package comment is careful to call "its own sqlite file, not the store". The graph is derived from source and is not memory; the markdown tree is. Two marks, both on the memory layer. The line worth carrying away is a comment in scope.go: the Rust implementation this was ported from "silently fell back to per-project for ANY unknown string", and the port made it an error instead, because "a typo must not quietly retarget a session's memories" — a scope that falls back does not fail, it writes somewhere else and reads an empty result back, and both look like working. Underneath it a path resolver closes symlink escape in both directions, and three separate write rules refuse rather than accommodate: an overflowing core write fails instead of truncating, an ambiguous unique-substring replace fails instead of choosing, and an exhausted injection budget drops whole entries rather than half a memory. What the layer has none of is epistemic state — no status, no supersession, no approval, no mutation log — so a memory here is current because the file is there. And its published −65% tokens / −85% tool calls has a complete committed harness with no committed result, while the only committed token A/B in the tree is an archived April run reporting a +41,048-token overhead, five months and a rewrite earlier.

Kipi System computes how often the human just said yes. An MIT-licensed personal operating system for a founder — 218,595 lines of Python, 1,222 commits, 228 test files — packaged as a Claude Code plugin with an MCP server and five families of harness hooks, over a memory that is markdown files with frontmatter. Four axes ride on a memory: a bounded confidence, a six-value provenance separating a founder-stated fact from a model-inferred guess, a status of current or superseded, and an as_of meaning when the claim was actually true, which the module insists is not the file's mtime. Two marks. The read path withholds properly — a source retired in its first fifteen lines never enters the morning digest, the window scoped that tightly because scanning the body "would let a canonical file that DISCUSSES a retirement retire itself", and the skip is recorded with its deciding reference "so a consumer can tell empty-because-retired from empty-because-none". But the mechanism worth travelling for is the oversight audit. Every assistant-proposed decision records what the operator did with it — approved, modified, rejected — and a deterministic harness computes the approval ratio and alerts at 0.7, running after the LLM audit agent and re-deriving the number itself "because the agent itself is sycophantic". The standalone mode exists because the check had only ever run behind a pipeline artifact "while an instance sat at pi~=0.88 with nothing able to notice". A system that measures whether its own human review is real, finds it is mostly not, and publishes the number is a different artifact from a review queue.

Beevibe deletes the fields nobody reads, and ships a table nobody writes. An Apache-2.0 workspace where a company's people and agents share one surface — 97,416 lines of TypeScript across six packages, 183 commits — with per-agent memory facts in Postgres and pgvector. One mark, and it is clean: agent_id is a required predicate on both query sites, with no nullable owner and no branch that omits it. The interest is in a pair of opposite acts from the same quarter. A migration drops confidence, valid_from, tags and metadata from the fact table with the finding written down — "No caller populates them or reads them" — which is the right answer to a field with no producer and a rare one. Meanwhile memory_promotion_event, the table that would record why the promoter moved a fact up a tier, has its own migration explaining the need, a Postgres adapter, and an API view counting its rows for the agent dashboard; its only writer is guarded by promotionEventRepo?, and the single composition root constructs the memory agent without it. Four of five parts built, the fifth a missing constructor argument, so the dashboard's promotion count is structurally zero. A column with no reader shows up in a schema review; an argument that was never added shows up nowhere. The scope ladder is the same kind of half-built: every briefing asks for all three scopes and binds the owning agent, so promotion changes a label and the dedup universe rather than the audience.

Knowl keeps the arm of its own ablation that has to stay ugly. An Apache-2.0 knowledge store for coding agents over one SQLite database per project — 65,422 lines of TypeScript against 74,258 lines of tests, 3,686 cases, 1,171 commits — reached through 28 MCP tools. Five marks, each visible in code and in a test: a five-value status that a read defaults to active so four of the five withhold an item; an assertion table carrying validFrom/validTo beside recordedAt/replacedAt with an as-of read exposed on the query tool; two append-only records, a commit log of what each change touched and a forget log read before a delete because "after the delete there is nothing left to read it from"; a cross-repo visibility predicate placed in SQL so "a peer's repo-private row is never read into this process at all"; and the test that proves it, with the peer's shared item and the caller's own private item asserted present in the same query. The design claim — a replaced fact is retired rather than left to compete — is backed by a committed ablation, and the file explaining those runs carries the best sentence about measurement in this corpus, on why the losing arm is kept forever: "if this stops looking bad, the metric has broken rather than the product improved." The same file tells a reader how to misread it, records a regression the project reported against itself and then withdrew, and forbids subtracting across runs whose embedding preset differs. The published 98-to-47 figure is the single-hop 6k pair; at 262k it is 87 to 42, both multi-hop arms sit near zero, and the effect that holds everywhere is stale leaks — 62 to 2, 28 to 5, 29 to 0.

kiwi-mem spends its hardest engineering on forgetting. An AGPL-3.0 personal memory gateway in Chinese — 33,801 lines of Python, 156 commits, Postgres behind an OpenAI-compatible endpoint. One mark: the project key is compiled into both retrieval arms, and the branch that matters is the caller who names no project, which narrows to project_id IS NULL rather than dropping the clause. The conversation side adds a three-state scope where attribution-unknown rows fail the global predicate instead of falling into it. But the work worth reading is deletion. Three tombstone tables at session, turn and message granularity, retained permanently because an expiring one lets a long-offline device "re-plant the body text the user deleted", with exactly one revocation path — restoring a backup, which is the user explicitly asking. Beside them, two counters for the races a delete creates: a per-session source revision so a background summariser discards a result derived from material deleted while it was working, and a global reset generation for requests still streaming when everything was cleared, which had left no trace in any table to purge. And then the finding: the conversation-scope predicate is a named constant precisely because copies drift, with a committed test counting occurrences in the source and requiring exactly one — and the same predicate appears once more, spelled IS TRUE instead of = TRUE in a diagnostics count, agreeing today and invisible to the guard written to prevent it.

Beever Atlas puts the retired-fact filter on three read paths and falls back to the fourth. An Apache-2.0 system that turns a team's Slack, Discord, Teams and Mattermost chat into a self-maintaining wiki — 96,884 lines of Python across 284 source files, 389 test files, 680 commits, Weaviate for facts and MongoDB for pages. Three marks. channel_id is compiled into every store query with no unscoped branch; a superseded fact is stamped with invalid_at and a pointer to its successor rather than deleted, and dropped from the default read; and a committed test asserts the default returns only the live fact while flipping include_superseded returns both. The exclusion runs in Python rather than in the query for a real reason — the store cannot express an is-null filter, which a sibling test pins by asserting no is_none reaches Weaviate — and that placement is why it reached only the three methods that post-process their results. bm25_search returns objects straight from the query, has no include_superseded parameter, and has no test. It is also what four call sites in the capability and agent-tool layers call inside except Exception when the hybrid search throws. So the copy of the read path without the supersession rule is the one that runs when retrieval has already failed, and the only signal is a warning line about the fallback that says nothing about what the fallback does differently.

Pensyve sabotages its own DELETE to prove the second layer holds. An Apache-2.0 memory runtime in Rust — 113,429 lines, 1,393 test functions, 752 commits, SQLite or Postgres behind one storage trait. Three marks, and the scope one is the strongest case in this corpus. A namespace_id predicate sits in every handwritten statement — "the load-bearing layer in every deployment" — and forced row-level security sits behind it as "a backstop for a query that forgets layer 1 — which is exactly the bug PR #218 found". What makes it worth reading is that the module documents two separate ways the backstop had been present and doing nothing: the scoping GUC was set with set_config(..., true), transaction-local, in a standalone statement that is its own implicit transaction, so Postgres discarded it before the scoped query ran and "every policy compared against NULL and matched nothing"; and Postgres exempts a table's owner from its own policies, so ENABLE ROW LEVEL SECURITY was inert until FORCE moved into the schema every startup applies. The payoff test takes delete_memory_by_id_in_namespace's statement, deletes the namespace clause — SABOTAGED_DELETE — runs it from another namespace and asserts the row survives. And the inverse discipline is applied to the other layer: a test gating the predicate calls relax_rls first, because otherwise "a cross-namespace assertion passes on the policies alone and proves nothing about the namespace_id predicate it was written for". The residual risk is announced rather than hidden: FORCE cannot remove BYPASSRLS, so startup reports the role's own exemptions, because such a role "makes FORCE enforce nothing with no other symptom".

KGLite sets the date once and the traversal obeys it. An MIT embedded knowledge graph — 362,473 lines of Rust, 4,172 test functions, 2,922 commits since March 2024 — that runs in-process with no database service and speaks Cypher, a fluent API and MCP, built so the same graph serves "an application, an analyst, or an LLM agent". It is an engine rather than a memory, and what it gives a memory built on it is time in the form a traversal can use: date("2005") is a context on the traverser, not a predicate repeated at every hop, and the machinery checks every node and edge it crosses against valid_from <= reference AND (valid_to IS NULL OR valid_to >= reference). Which property names carry that window is declared per graph rather than fixed by the engine, so an existing dataset becomes temporal without a migration. One mark, on the test that pins both halves of a point-in-time read: at 2005 the licensee who held the licence is asserted present and the one who did not is asserted absent, in the same result. What it does not give is the second axis — nothing records when the graph learned a fact, so closing a window because the world changed and fixing a window that was wrong are the same edit, written and read identically. The agent surface is the other thing worth reading: the graph carries its own query skills, gated by a predicate over the graph's actual shape, with a stated precedence in which "a graph wins a name collision because it is the more specific statement" and both owned layers lose to the operator's files.

Meridian filters before the write, and leaves two plaintext copies behind. An MIT desktop tool that turns a day of screen activity into a timeline, a summary and updated tickets — 144,686 lines of Rust, 2,033 test functions, 2,831 commits, capture running in-process in a Tauri tray. No capability marks, which for a screen-capture memory is the expected shape: a frame is an observation with no status to filter on, one timestamp rather than two axes, one user and one database rather than a scope. What such a system is judged on is what it refuses to capture, and that machinery is good — an ignore list drops a frame before it is written, matching an app name exactly or the focused tab's URL at domain granularity, with the limit stated in the setting's own words: "GOING FORWARD ONLY; already captured history is left untouched." Its unit tests assert both polarities including the trap a naive rule fails, dropping m.youtube.com while leaving notyoutube.com alone, and dropping Messages while leaving Messages Pro. The database is SQLCipher-encrypted with a raw 256-bit key and the reasoning for skipping key derivation stated correctly. Against that: the encryption migration writes the original to meridian.db.plaintext-backup-<timestamp> and the only code that touches it afterwards lists it, and the MCP package — whose WebAssembly SQLite cannot open a SQLCipher file — shells out to db-export-plaintext on every read and never unlinks the snapshot. After a migration and one agent read, the screen-activity database exists three times: encrypted where it belongs, in the clear beside it, and in the clear in the temp directory.

Memory Vault makes an unresolved space name return nothing instead of everything. A self-hosted MIT memory over one Postgres with pgvector — 17,143 lines of Python, 589 test functions, 165 commits — reached through MCP, REST, a dashboard and a CLI. Three marks, and the one to read it for is a single else branch. Its search-clause builder documents three cases, and the third is the mark: None searches every space, a populated list becomes space_id IN (…), and an empty list becomes a hard false — "rather than silently widening to every space". The end-to-end test names the bug that comment came from: a spaces=["unknown"] filter "used to silently widen to every space because resolve_space_names returned [] and the caller collapsed it to None via or None" — the falsy-collapse failure, in the worst direction, turning a narrowing request into a widening one. Forgetting follows the same instinct: forget sets a flag, stamps the time and zeroes the importance while the row stays, every read excludes it starting with the first clause the shared builder emits, and destruction is a separate operator-run purge with a thirty-day floor. The threat model is among the better ones here — it declares the deployment it is built for, names the data classes the system is unsuitable for, documents the gaps rather than assuming them away, and ships a re-runnable pentest script against its own claims.

Open Graph Memory deleted its own benchmark gate when the endpoint it measured went away. An MIT graph memory over one Postgres — 24,508 lines of Python, 260 test functions, 203 commits — with a FastAPI service, an ARQ worker and a Python SDK. Three marks, and the reason to read it is the evaluation directory. Two milestones' gates were removed when /v1/query was retired, with the reason in the README: "structured graph endpoints cannot honestly reproduce answer, citation, retrieval-mode, or fallback metrics." The baseline before that survives as retained JSONL scored by an evaluator that never calls a model or store, "keeping published baselines reproducible without presenting those metrics as a current runtime check" — a number kept checkable and explicitly denied the status of a current measurement. What replaced them is executable: the M4 gate boots a fresh Postgres through Compose, creates a second tenant with its own token and dataset, and drives the public graph routes as the outsider, asserting that the two tenants' same-named entities have different ids and that every one of the primary's paths returns 404. The extraction half has its own negative — the M3 golden labels "deliberately exclude the ambiguous and unsupported relations: a deterministic extractor must not invent either", with thresholds frozen at 1.0. One finding: the fixture's excluded_relations list, which names the two forbidden relations, is loaded by no code, so a failing gate reports a precision number rather than naming what was invented.

funes declines correction, and argues for it. Hugging Face's own Apache-2.0 memory over coding-agent transcripts — 22,429 lines of Rust, 281 test functions, 558 commits — indexing Claude Code, Codex, pi and Hermes into one Lance dataset that can be published as a Hub repository a teammate recalls from. No capability marks, and for once the reason is a position rather than an omission. Its rationale states that a mutable memory "must decide at write time what each new piece of information supersedes — and every wrong call loses information silently, by overwriting the right answer with a confident wrong one", so funes makes no write-time decisions at all and resolves obsolescence at read time; and the sentence that carries the argument is that "a log also keeps what a knowledge base throws away — the superseded passage is often the answer itself: what did we try before, and why did we move off it?" The counterweight the document does not make: recency weighting ranks, it does not withhold, so a wrong passage stays as recallable as its correction. The one boundary funes does enforce is publication, and it is enforced hard — index-time redaction is best-effort and warns when the scanner is absent, while push is always-on and fail-closed, reconstructing whole content blocks before scanning "so a secret split across chunks cannot evade detection" and holding back every chunk of an offending block. One of its tests carries the exact shape of a key that once got through: stored with escaped newlines, so the detector's canonical raw was not a substring of the stored bytes and value matching missed it.

Deus sends a contradiction to the person and never to the invalidator. An MIT personal assistant running on the user's own machine — 119,845 lines of TypeScript and Python outside tests, 1,248 commits, per-conversation containers and an evolution loop that rewrites its own system prompt. Two marks. When the indexer finds a newer atom contradicting an older one it retires nothing: it writes both texts to pending_conflicts under a comment at the insert site reading "Log to pending_conflicts for user review — never auto-invalidate", and only an operator running --resolve-conflicts or --dismiss-conflict clears it, with the dismissal guarded so resolved_at is write-once. The migration that added that timestamp declines to backfill: "we never observed WHEN they were resolved, so inventing a timestamp would fabricate data." The expiry mechanism has its own detail worth copying — the read predicate is expired_at IS NULL OR expired_at > date('now'), compared against the clock rather than tested for presence, so a scheduled expiry retires an atom when its date arrives instead of the moment the stamp is written. Three published claims need qualifying, and the architecture document is more careful than the README in every case: the semantic graph is described as having "bi-temporal validity" over a schema carrying one temporal axis — an as_of read exists and travels the session date, but no column records when the store came to believe anything; the headline "95% recall on the LongMemEval benchmark" is a 50-example run whose own table reports Recall@1 at 94%; and the comparison table's "~37K lines" is honestly dated five months before this pin, against a tree whose src alone is 64,400.

Dev MCP Suite advertises three tools it never implemented, and defines a revert as "remove what the backup does not contain" while letting the backup not contain things. Four MIT stdio MCP servers in one npm package — 84 files, roughly 7,400 lines of JavaScript, one runtime dependency, no database — giving an agent a Markdown vault with wikilinks, a JSONL session journal, a file snapshotter outside git, and a repository briefer. One mark, negative_eval, earned on a journal filter asserted against a populated timeline with the excluded entry proved retrievable by another path four lines later. Each server answers tools/list from one literal array and dispatches tools/call from a separate switch that nothing reconciles against it: 42 tools declared, 39 with a handler. memory_import_session, pack_dense_brief and journal_standup carry full input schemas into every client's tool menu and have no implementation in the tree — and release 3.5.0 is named after exactly those three, with the CHANGELOG listing each under Added and the roadmap marking all three done. The second finding is in the snapshotter. checkpoint_create declines three classes of file — anything past a 4,000-file cap, anything over 2 MB, anything containing a NUL byte — and computeDiff classifies every file absent from the manifest as added since the checkpoint, so checkpoint_restore with clean: true unlinks exactly the set that was skipped: every image, database file and large asset that was present the whole time, reported back as "removed N newer files". The skipped counter that would fix it is already computed at capture and never consulted at restore. The committed test for that path seeds two small text files, so nothing in the fixture is a file create would skip and the case is green against the defect. Two more of the suite's options are decorative in the same way: runAutoIndexer accepts a dryRun it never reads, and the journal compressor accepts a maxTokens budget it never checks, truncating at fixed counts instead. The vault half is the better half — deletion is honest about being deletion, and memory_dedup suggests merges and refuses to perform them — but a note carries no status and one caller-settable created is its only time field, so a backdate is unrecoverable and there is no write time to compare it against.

ZCode removes the confirmation on a memory write, and spends its care on deciding which writes qualify. Z.ai's coding-agent harness, opened as a single Apache-2.0 commit — a 126 MB TypeScript monorepo of which memory is about 1,370 lines. The store is deliberately plain: one Markdown file per fact with name, description and a metadata.type drawn from user, feedback, project, reference, beside a MEMORY.md index the prompt forbids putting content in. There is no database, no embedding and no ranking — recall is the index file pasted into the system prompt, and anything deeper happens because the model chose to Read a filename it saw in a 150-character hook. No marks: one mtime, no status, no record of a deleted value, and scoping that is a hashed per-project directory rather than a key on a record. What is worth the read is the permission machinery. applyMemoryFilePermission auto-allows memory Markdown writes — the ask a user would otherwise get is removed — and conditions that grant on containment, an .md suffix, and a denylist covering .git, hooks, .husky, .zcode, skills, commands and agents, so memory can write prose and nothing that later executes. Two resolvers exist, one with the denylist and one without, and the grant correctly calls the stricter one; the normaliser beneath it strips bidi and zero-width controls, truncates at : for NTFS alternate data streams, and trims trailing dots and spaces, each answering a named bypass. The writer is a background sub-agent refused per call at the execution boundary rather than through a narrowed catalogue, and its delete path is a parsed grammar — one invocation, rm, no redirects or env assignments, no -r, no glob metacharacters, every argument absolute and ending .md inside the root. The gap is that none of it is covered: no manifest in the tree declares a test script, no runner is configured and there is no CI directory, while apps/zcode-cli/.husky/pre-commit calls pnpm test. Four test files exist, covering session import and config migration.

Gas Town replaces a coding agent's memory file with a row every agent in the same database reads, and the database is chosen by the working directory. The multi-agent orchestrator's gt remember stores a typed string — feedback, user, project, reference, general — in beads' Dolt config table, and a SessionStart hook prints every row into each new session with corrections first. That makes a correction a crew member records reach every polecat in the rig without anyone copying a file. At the pinned commit the write fails against every beads release from v1.1.0, which reserves the memory. key prefix gt remember writes under, and gt installs the latest. The design also means nothing ranks, bounds or attributes a memory. The town and each rig hold separate sets, while the mayor's prompt calls them shared across the town. An auto-key built from the first five words overwrites any memory that opens the same way. After a compaction or resume, the fast path re-injects nothing.

beads spends its care on making four verbs tell the truth, and leaves the memory itself primitive. The Dolt-backed issue tracker stores a memory as a string under a key in its config table. Every write reads and replaces in one transaction and one Dolt commit. A prime that cannot read the store says so in the injected text, and a single word naming a command is refused as content. Retrieval is the whole plane in alphabetical order, cut by optional caps that also drop alphabetically. When two clones edit one memory, the pull keeps the remote's value and reports it only on stderr.

yantrikdb-mcp passes the YantrikDB engine to any MCP client with careful refusals and leaves the boundaries to the model. The author's separately distributed MCP server runs the engine in process behind twenty-one tools. A parameter the engine cannot honour, such as a backdated write or a time window, is refused with the fix named rather than dropped. A preview-shaped maintenance call defaults to preview after one ran wet. Scope is a namespace the model may omit, and omitted means every namespace. The skill write gate guards skill(define) while the generic remember writes into the same namespace. Rule skills go to a review queue nothing reads.

PMB takes the model out of the loop on both sides and loses corrections in the middle. A local SQLite and LanceDB memory served over MCP, it injects lessons and hybrid-recall hits on every prompt through a Claude Code hook, journals the turn when the agent wrote nothing, and makes its sub-millisecond async write crash-safe with a durable outbox. Its lesson loop measures follow-through with Wilson intervals and keeps the verdict out of ranking. The keyed-fact supersession it relies on for corrections archives the only current value when that value is restated, a negated value returns through repair-keyed, and the as-of query the tool description offers has no caller outside tests.

mempalace-code is MemPalace rebuilt as a code index, and the bookkeeping is the part to read. A hard fork from 7 April 2026, it swaps ChromaDB for LanceDB, chunks on declarations with symbol and line-range metadata, and gates re-mining on a per-file hash; a stale sweep deletes only rows it can prove are regenerable. The packaged plugin exposes four tools and no delete. The manual memory beside the index is weaker than upstream's: get and delete by id interpolate the id into SQL unescaped, the CLI and MCP server open different knowledge graphs by default, and the duplicate check reads across wings.

engram (NickCirv) turns a git revert into a structured regret, and keys it on the column its own reindex deletes by. An Apache-2.0 TypeScript code-graph layer for Claude Code, published as engramx, whose memory is mistake, decision and pattern nodes regex-mined from reverts, fix commits, CLAUDE.md and engram learn, and injected as landmines before an edit. A revert keeps the overturned subject, the date it was found false and the superseding sha. Mistakes share source_file with derived AST nodes, so reindexing a file deletes its warnings, and a full init drops every learned one. valid_until has readers and no writer. sql.js rewrites the whole file on every close, so concurrent hooks are last-writer-wins. One mark, negative_eval.

Emulo checks its evidence harder than the profile it serves. A stdlib Python tool that keeps only the messages a developer typed to Claude Code, Codex, Copilot CLI, OpenCode and Antigravity, has the host agent distil them into work, design, writing and video rules, and installs the result where agents load it whole. Every quote must sit verbatim in the message dated as it claims, an inferred rule needs two sessions and two source-quarter strata, and a rule citing contradicted evidence is refused. Then the reducer model writes you.md itself, and the validator only asserts the validated rules are in it. A wrong rule has no correction path: re-mining reactivates the cached reduction. No mark.

sqlite-memory-mcp keeps a real change log and a promotion gate the agent opens by default. An MIT drop-in for the official MCP memory server's nine graph tools over one SQLite file, with six companion servers adding sessions, tasks, bridge sync and a claim-to-fact tier. Every core write appends old and new values to memory_events, and facts are superseded or invalidated with a rationale and an effective time rather than deleted. The gate is weaker than the README: promote_candidate defaults to human_confirmed on the agent's own tool, and a regex claim seen three or four times in observations becomes a canonical fact with no gate at all. The re-ranker scores a stronger BM25 match lower.

MEX ties a Git-shared project wiki to the code it describes. An MIT TypeScript CLI keeps architecture, decisions and conventions as typed entities in Markdown under .mex/, grounds each to a code-graph symbol by fingerprint and body hash, and flags a claim when that code changes. Structured writes run through an operation layer with hash preconditions and a crash-safe, body-free intent-and-completion ledger. The ordinary loop goes around it: the shipped instructions have the agent edit context files directly and read them whole through a router, so the ledger records few writes and the lifecycle filter, which hides only archived, never touches what the agent reads. Superseded entries are returned, ranked last. Two marks, audit_log and negative_eval.

m3 Memory makes correction part of every write, and decides it by proximity. A local-first SQLite MCP server shared by Claude Code, Cursor, Gemini CLI and others, it closes an older same-type memory's valid_to, soft-deletes it and records the event whenever a new write lands within 0.92 cosine with different text, behind a compare-and-set that keeps racing writers to one supersession. Scope predicates come from one builder shared by every candidate path. The weaknesses sit at the edges: similar true facts supersede each other, across agents when agent_id is empty; as_of keeps is_deleted = 0 and so never returns a superseded row; and the FTS short-circuit drops agent isolation, as_of and the benchmark-variant gate.

RuVector guards the embedding space of its hook store carefully and lets tool-call telemetry push its memories out. An MIT Rust vector-engine monorepo whose npm CLI keeps a per-project .ruvector/intelligence.json, written by hooks remember, an MCP tool, and Claude Code hooks that record every file read, search, edit and shell command. Every vector write is checked against a stamped embedder, model and dimension, and a corrupt file is quarantined rather than saved back empty. There is no per-memory forget, one FIFO drops the oldest 1,000 entries of any type past 5,000, and the CLI and MCP writers use different field names. The Shared Brain server beside it accepts an unauthenticated inject as a system contributor. No marks.

Graft gates how much of a note the agent sees, and loses its corrections at both doors. A local C11 daemon over one SQLite file per profile, it embeds each note's title with BGE-M3 through llama.cpp and answers query with STRONG, WEAK or MISS. A weak hit returns the title without the body, and a vector hit is strong only when trigram overlap agrees. Supersession is atomic and filtered from every search arm, but only the viewer's HTTP save sends it. The agent skills correct by delete and re-insert, and profile sync re-inserts every shared note deleted locally while never carrying a supersession to other clients.

Memex puts its care into a gate that refuses a secret without quoting it, and leaves correction to a file move. A Zettelkasten of markdown cards for coding agents, served through a CLI, an MCP server and harness plugins, it screens every agent-facing write and every search query for private keys, token shapes and credential assignments, and tests what the gate must allow as well as what it must refuse. The gate belongs to the write command rather than the store, so three importers write past it. Archiving moves a card, records nothing about why, and silently replaces an earlier archived card of the same slug.

Paperclip runs a company of coding agents, and its memory is a wiki one of them keeps. The experimental LLM Wiki plugin gives a managed Wiki Maintainer agent ten tools to write markdown pages into a local folder, indexes their paths, titles and backlinks in Postgres, and templates project standups from issue history with source hashes, snapshots and page bindings recording where each page came from. Search is a substring match on titles, never page text. Both scope keys are tool arguments, and the host binds plugin file access to the calling run's company but not plugin SQL, so the metadata tools read another company's page names. A review-required distillation discards the patches it asks a person to review.

open-bridge is the family's workspace template, and its memory is a set of rules with careful tooling around them. An MIT git repository of Markdown and YAML that coding agents read every session; memory is one file per fact behind a MEMORY.md index. memory-location.py resolves where Claude Code's auto memory writes against where a reader should look, lints the index against the harness's 200-line and 25 KB cut-off, and counts session links whose transcripts have expired. No code creates a fact, and every gate is prose the agent executes: approval is a CLI with no actor check, migrate re-copies deleted facts from the legacy directory, and the exporter reads a different directory from every other reader. No mark.

ShellBrain hands every write to a background librarian and checks its evidence, not its authority. A Postgres and pgvector store for Claude Code, Codex and Cursor, it syncs transcripts and, after fifteen idle minutes, runs a Codex or Claude agent that files typed cases and concept claims, each citing a stored episode event. Six lifecycle statuses gate every read through one shared predicate, and each transition is logged with a rationale. The librarian's route allowlist is an environment variable, Codex runs with its sandbox off, the actor on a correction is a string it supplies, and its duplicate check cannot see memories already marked wrong.

MeMesh keeps what reaches a model unasked behind one trust gate, and leaves the gate's inputs soft. A Claude Code and Codex plugin over one SQLite file, it captures session snapshots and commits by rule, recalls by FTS5, and injects a project-scoped block at session start. One predicate decides every unasked injection, so imports stay recallable and never injected. A forgotten snapshot line is kept as a hash the Stop hook refuses to restore, and agent-prepared digests wait for a person. The weak side: any plain remember on an imported memory stamps it trusted, ingested note files are stamped untrusted and never reach the briefing, and guard proposals have an applier and no producer.

autoharness lets every model propose a skill and only a deterministic promoter write one. A Claude Code plugin, it hands a redacted slice of the transcript to a haiku reflector after 50 tool calls; the reflector stages create, patch or delete intents through one MCP tool, and the promoter lands them as SKILL.md folders once six lint classes pass in memory. A grouped index of its own skills opens each session, and a loads-per-request rate archives the unused into .archive/. The boundary it advertises leaks: create skips the ownership check and adopts a same-named hand-written skill, the tool drops the fold marker, and lifecycle eviction writes no ledger entry. One mark, negative_eval.

MegaMemory makes the coding agent the indexer of its own project graph, and keeps removal honest but correction thin. A stdio MCP server over one SQLite file per project directory, it stores concepts the agent writes after each task and finds them with in-process MiniLM embeddings. A soft delete keeps the reason and every agent read filters it; a timeline table logs every tool call; a merge command reconciles knowledge.db files across git branches and raises a removal on one side as a conflict. Updates overwrite with no prior value, a removed concept's name cannot be reused under its parent, and unresolved conflict copies are searched as settled while the agent holds the verb that closes them.

Exomem governs who may read a vault the user owns, and leaves what the vault believes to the agent. An MCP server over a Markdown or Obsidian vault, it indexes agent-authored typed pages for BM25, embedding and wikilink search and compiles a bounded working-memory packet. Every write commits with its log.md entry in one atomic batch, Sources and Evidence are append-only, and a per-principal release plane drops hits a remote audience may not see. Supersession keeps both pages, but find only halves the old one's score, an opt-in hook injects superseded units unlabelled, and every Review Studio button calls a tool the agent already holds. It does not run on macOS.

Rust Self-Learning Memory records a coding agent's work as scored episodes with no model on the write path, and ships an MCP server whose own gate refuses them. A Rust workspace — core library, libSQL and redb backends, an MCP server and a CLI — where completing an episode computes a reward, a rule-based reflection and tool-sequence, decision, error-recovery and context patterns. Cache keys and the ANN index carry the embedding provider's identity, and eviction deletes embeddings with per-backend reconciliation. The quality gate scores reflection and patterns before either exists, so its ceiling is 0.64 against the server's default 0.7, while the CLI and tests use 0.0. Explicit delete leaves embeddings, patterns and cached results. One mark, negative_eval.

Cortexes keeps every Claude Code session and makes a person decide what becomes a note. A SessionEnd hook records and redacts each transcript into a git-backed Obsidian vault; a distill skill walks it through bounded pages and asks the user to confirm each verdict, and cortex-vec indexes the resulting pages for BM25-plus-vector RRF search. Its strongest part is filter parity: one --repo clause, evaluated by Chroma and by a single field table, with a test that fails when the two drift. The derived index is the weak part. Without an OpenAI key an upsert exits before updating BM25, no skill deletes or rebuilds, and every human confirmation is skill prose.

Wordcell makes a Markdown vault beside the code the only authority and routes recall from the path being edited. Formerly KB, it serves notes, plans and captured sources through a CLI, an SDK and a stdio MCP server; wordcell context returns inherited AGENTS.md rules beside the notes whose repository_scopes cover a file, grouping plans by lifecycle status. Writes are revision-checked, locked per note and installed atomically, and every QMD vector hit is reconciled against a fresh scan before ranking. Correction is the weak side: no delete verb, a Supermemory re-import re-creates a deleted imported note, and superseded versions and terminal plans are returned beside current ones.

RecallNest separates what an agent was told from what it transcribed, and then lets scope leak around the separation. One LanceDB store serves Claude Code, Codex, Kimi and a phone agent over MCP and loopback HTTP. Transcript chunks land on an evidence layer that the stable profile in resume_context refuses, and promotion copies them into durable memory with a promotedFrom link. Replacing a belief archives the old version as its own row, and a forget leaves a text fingerprint that memory-file reconciliation honours. The weakness is scope: the store filters it in SQL, but the write path's canonical-key match reads every scope, and MCP search retries across all scopes when a scoped search finds nothing.

Memoose builds the correction vocabulary of a typed graph and leaves the door open behind it. A local Claude Code plugin, it keeps entities and relations with evidence pointers in one SQLite file per project, filled after each turn by a Haiku keeper launched through claude -p. A relation declared functional keeps one current value, ordered by valid_from rather than arrival, and every write lands in an insert-only provenance table. Every hook filters superseded facts, and a test proves the prompt hint does. But the upsert clears the superseded flag, so restating an old value revives it. The keeper runs with unrestricted Bash and --dangerously-skip-permissions over transcript text.

Cortex Hub makes recall unavoidable and then lets what it recalls drift. A self-hosted MCP hub for a team's coding agents, it pairs a TypeScript port of mem0's extract-then-decide pipeline over Qdrant with a knowledge base that carries validity dates, hall types, recipe lineage and usage counters. Repository hooks refuse any edit, including one through sed -i or a redirect, until both knowledge and memory search have run. The memory plane filters on a user_id key the agent composes from project and branch. The knowledge plane is weak where it learns: quality-gate feedback from any project credits whichever ten documents anyone touched in the last hour, and archived or invalidated documents stay in search.

second-brain (LindaHaviv) decides privacy at write time and forgets its facts every night. A single-user brain on Oracle AI Database 26ai: loaders pull posts, transcripts and chats into one table, an MCP server behind OAuth serves them to any assistant, and a research agent records each run and distills runs into facts. A deterministic regex tags an episodic run or dialogue turn business, and every SQL read of those tables filters to content. Raw rows expire after 90 days only while a consolidation ran within seven. The distilled layer is the weak half: each consolidation deletes every fact and inserts the model's new set with no diff. The default backend is Oracle's oracleagentmemory package, which publishes no source.

aios (rexleimo) builds the governance for model-written memory and leaves both ends of it sealed. A local control plane around Codex, Claude Code and other coding CLIs, it keeps an append-only memo log where each event carries provenance, a claim status and a valid-from time. Correction is an appended supersede link, checked against an ACL on scope and owning agent. An automatic write from a model's verified=yes lands as a candidate that every memo read drops. The promote verb is hard-wired to deny, so candidates never leave the queue. Meanwhile the MCP and Pi write tools pass no identity and land verified under local-user.

Dynamics-memory treats the agent reaching past its memory as the signal to repair it. A Python sidecar for opencode, it logs every turn to SQLite, distils it with one LLM call into candidates whose decaying value — raised when a recognizer says an answer used them — moves them between pools, and files a repair job whenever the user corrects the agent or the agent searches the raw log itself. A budgeted in-process investigator reads the log and proposes memories that must cite earlier log units. Correction is the weak half: nothing deletes, a superseded value is archived into a pool retrieval still scores, and the verdict tool is the agent's own.

Neo Agent Brain keeps every turn a coding agent takes, verbatim, and says precisely when each read can see it. The Memory Core behind Neo.mjs's AI maintainers accepts add_memory at a JSONL write-ahead append, then drains each turn into Chroma for semantic recall and a SQLite graph for recency; the response reports recency and semantic visibility as separate fields. Every row carries a transport-derived userId, the recency read fails closed without one, and a caller may narrow the sharing policy but never widen it. The edges are weaker. purge_session deletes the Chroma rows and leaves the graph node whose miniSummary the recency read keeps serving, and the private thought field is withheld from peers on one read path of three.

Octobrain puts a real scope key on every read and loses it on the write. A Rust MCP and CLI server extracted from Muvon's Octocode, it keeps titled notes in one LanceDB table scoped by normalized git remote. Retrieval is BM25-plus-vector RRF with Rocchio expansion, decay and a source-tier multiplier. A supersedes edge down-ranks instead of deleting, and closing a goal consolidates its sources. Every upsert stamps the session's scope, so a global memory edited from a project moves into it. global=true is dropped by a cached provider, and forgetting a global memory reports success and deletes nothing. The archived state is filtered and never written. One mark, scope_enforced.

Kimetsu is the family's event-sourced member: one Rust binary, one SQLite file per project, and a log that the tables are rebuilt from. Lessons arrive through MCP tools, a SessionEnd distiller and pack import, and a model-free broker — FTS5, optional BGE-small cosine, two-hop graph expansion, abstention floors — injects them on every prompt. A correction keeps each revision and resets the evidence tied to the old text, and a citation credits only the revision the model was shown. What is weak is who holds the gates: accept, reject, invalidate and conflict-resolve are agent tools, open by default, and the producer's own confidence decides whether a lesson waits for review. Conflict decisions do not replicate through sync.

Lint-AI decides what is still true at query time, and decides it well only for one-claim documents. A Rust layer whose hooks capture each coding-agent turn into a project-local Tantivy store, it extracts claims by regex at every refresh, chains them by subject, and removes a superseded document from the candidate set before ranking on every entry point, returning it labelled when a query asks about history. The policy hides a document only when that document is the superseded claim and nothing else, so a captured Request/Result record is at most marked conflicted — and the hook that injects it drops the mark. The agent cannot delete or supersede what it captured.

nano-brain builds careful hybrid search over a document store that cannot demote anything it holds. A self-hosted Go MCP server over PostgreSQL and pgvector, it keeps agent-written notes, LLM summaries of harvested OpenCode, Claude Code and Pi sessions, and indexed code in one documents table keyed by workspace. BM25 and vector legs run in parallel, fuse by RRF and take a 30% recency weight. supersedes is a pointer no search reads, a deleted session summary is regenerated on the next 120-second harvest, and memory_ticket reads sessions from every workspace. The hybrid search's OR fallback drops the time window and tags the caller asked for, while memory_search's copy of the same fallback is guarded against exactly that.

Global Agent Memory gets the plumbing of a reviewable memory right and leaves the review to the model's manners. A local MCP memory for Claude Code and Codex, it keeps notes as Obsidian-compatible Markdown, indexes them into SQLite FTS5 and optional vectors, and returns only active notes by default. Mutations are replay-safe by request id and compare-and-set on updated_at, and protected notes sit behind owner grants that can only narrow. But memory_approve is on the agent's own tool list, memory_supersede activates a fresh replacement in one call, and the candidates resource lists every project.

RightMemory takes every memory write away from the coding agent and gives it to a validated model updater. Memory is Markdown nodes with typed edges in a git repository. Agents and a transcript reviewer submit evidence; a batched updater edits Memory in a throwaway worktree, and the runtime lands the commit only if the role's paths and the whole graph validate, with the exact input batch committed beside it. A fenced git lease serialises updaters across devices, and a hub shares views under view-bound tokens. The weak side is reads: pending evidence is retrievable before judgement, pruned memory returns under grace, and question views reach the provider's whole root behind a prompt.

Claude Engram turns a Claude Code session's own failures and choices into memory, and keeps the injection quiet. Global hooks store failed-tool output as mistakes and gated user sentences as decisions, a detached miner adds more from transcripts, and one JSON file per project path holds them. Before each edit at most three memories are injected, and only with a path-aware file match. The boundary is weaker than the gate: both hook readers fall back to any registered project with the same directory name, an acknowledged mistake is archived and then re-captured, and post-session cleanup deletes a rule under 20 characters. One mark, negative_eval.

CTX Cognitive Version Control versions an agent's reasoning rather than its facts. A .NET CLI and MCP server over a .ctx directory of JSON, it stores goals, tasks, hypotheses, evidence, decisions and conclusions as typed records with their own state enums, commits the whole working context as a snapshot with an entity diff, and supports branches and merge. A consistency audit names done tasks without an accepted conclusion and accepted decisions without evidence. The states steer planning and not belief: the context packet injects refuted hypotheses and rejected decisions with a label, ordered by confidence, and a superseded hypothesis keeps its old label. Merge reports conflicts and then writes the incoming copy over each.

Roampal Core lets the model that used a memory decide whether it survives. A local ChromaDB server behind Claude Code hooks, an MCP server and an OpenCode plugin, it injects eight memories per prompt and asks the model next turn to rate each; the ratings promote exchange summaries from working to history to patterns, or delete them, while a cross-encoder sets the final retrieval order. Profile routing refuses an unknown profile, and a down embedder is announced in the injected block. delete_memory archives the top semantic hit when no text matches, archived rows are purged when the profile next loads, and the companion paper's 85.8% LoCoMo figure was measured with promotion and decay off.

OpenCode Session Recall indexes the history OpenCode already keeps, and gates every expensive read behind a cheap one. A lease-holding distiller turns each session into a card and up to 5,000 FTS5 rows of human text, reasoning and commands; a query ranks cards, checks the FTS index for needles, and only then drills a dozen sessions in bounded pages, with a tool-output sweep refused unless a scope is supplied. Removal events force a full replace, and the plugin keeps its own output out of its index. Against that: deletions reach the store only through the lease holder's events, nothing evicts a card whose session vanished unobserved, and opt-in auto-recall injects cards from every project, ignoring global: false. One mark, negative_eval.

AOCI-CODE binds every line of a model-written code index to the digest of the file it describes. An FSL-1.1-MIT Go CLI and stdio MCP server that keeps one FRAS line per managed file (responsibility, relations, API and non-obvious constraints) in Git-versioned plain-text Volumes, delivered whole at session start behind a ten-ordinal recall challenge. Every Code write must carry the source file's SHA-256, re-hashed at commit and refused if the bytes moved, and batches commit all-or-nothing with preimage recovery. Against it: staleness is the only state an entry has, derived per call and delivered unmarked by the default per-object read, and aoci_report, the documented route for disputing a live entry, refuses in the default layout. No mark.

MCP Context Server stamps an owner on every row and reads it nowhere. A FastMCP server over SQLite or PostgreSQL, it stores user prompts and agent reports as thread-keyed entries, generates an embedding and a local-model summary before each write commits, and serves filter, FTS, compressed-vector and RRF hybrid search. The write path is careful: nothing is stored if generation fails, updates are a version compare-and-set, and deduplication re-asserts the hash it observed. But owner_id and a default-private visibility reach no query, only a visibility change checks ownership, and the by-id fetch every search leads to takes no thread. The project's own docs say reads are not yet access-controlled.

Tree Ring Memory builds its startup brief's scope from the harness, not from the model. A Rust CLI over one SQLite FTS5 file, it injects at most eight lines at session start through Claude Code and Codex hooks, filtered in SQL on the project root's name and the caller's own agent, workflow and session before the candidate cap, and blocks each stop once to ask for up to three restricted capture writes. No LLM runs anywhere. Writes are idempotent on a hashed operation namespace that redaction cannot release. Correction is weak: forget requires a reason and discards it, explicit recall returns expired rows the brief hides, and the agent can write heartwood directly in the default open mode.

Engram MCP (edg-l) retires a memory by redirecting to its successor, and its own housekeeping can undo the redirect. A Rust MCP server over one SQLite file, with typed memories, branch-chained handoffs, ADRs and todos. A supersedes edge makes retrieval return the successor at the old memory's rank with a matched_via note, a dead flag drops a memory whose subject is gone, and every destructive path snapshots row, edges and embedding to a trash. Decay counts days of curated activity rather than the calendar. The 6-hourly and CLI dedup passes merge across supersedes edges and dead flags, and handoff_resume and the recall_context prompt skip the status filter. Two marks: trust_state, negative_eval; scope_enforced is withheld because memory_graph reads across projects.

Counterparts models a Claude Code companion on human memory and types every correction. Hooks and a stdio MCP server feed one SQLite file per owner: the model writes its own memories at the Stop ask, recall surfaces them each turn inside 2,048 bytes, and a lived-day clock decays and archives them. Two memories that disagree are settled as changed, corrected or open on an undoable trail with actor and reason, and a corrected one leaves recall. Removal is owner-only by import-graph test and interactive confirmation. The weak side is the boundary: any session can archive an unprotected memory by id, removal leaves a content hash in two logs, and nothing scopes by project. Three marks: trust_state, audit_log, negative_eval.

Claude Engram (mlapeter) buffers Claude Code transcripts and lets a nightly sleep decide what survives. Hooks append each turn to a per-project buffer with no model call; a detached runner sends 16 KB chunks to Sonnet, and once per active day consolidation prunes below a strength floor, gists old craft memories, merges within a register and rewrites model-authored identity documents, committing the JSON store to git before and after. Nothing is deleted by compression: originals and merge sources go to a deep archive. That is also the weakness: forget removes only the active row, so a verbatim pre-gist copy stays where deep_recall can reactivate it, and the dashboard serves every memory with no bind address or credential. One mark: negative_eval.

Orgtree keeps an agent's memory in two Markdown files the agent writes, and spends its engineering on making the next session read them. The Windows desktop runs Claude Code, Codex, Antigravity and OpenRouter agents as an org chart, and each keeps a CLAUDE.md of standing notes and a breadcrumbs.md log in its scratch folder. The engine mirrors the notes into the prompt on lanes whose CLI ignores CLAUDE.md, hashes them into the warm-process identity so an edit forces a respawn, and splices the breadcrumb tail into the first turn after a cheap compaction only. The files carry no author, time or status, append-only is only a request, and the verified handoff record's splice waits on a flag file nothing creates.

Mainline keeps the reasoning behind agent work as an event log in Git refs, and two of its lifecycle states have no writer. The Go CLI has an agent start an intent, append turns locally and seal a summary of decisions, rejected alternatives and touched files. The seal is one commit on a per-actor ref moved by compare-and-swap, and every view is a replay. mainline context ranks prior intents deterministically and pulls a superseded intent in after its replacement. Seal refuses agent-written constraints, which only a terminal-bound guard add creates. Superseded and reverted are read by every surface and set by no command, yet two thirds of the published eval advantage rests on a superseded fixture.

Agent Memory Engine withholds a memory once the file it came from changes, and serves the one a conflict flagged. The local stdio MCP server keeps a typed memory tree per repository, written deterministically from an agent's task report and scored by Jaccard overlap in Python. A memory derived from one file records its hash; every retrieval re-checks it and moves a drifted node to needs_revalidation or invalidated, which the composer skips. Trust is assigned from provenance, and no agent tool reaches the human-confirmed rung. The weak side is wiring: needs_review is served despite the README, the agent's branch never reaches the stored node, and nothing calls the human elevation verbs.

AlphaOne ai-memory hides what it has not verified on every read lane at once. A single Rust binary serving MCP, HTTP and CLI over SQLite or Postgres, it stores titled, tiered rows that an agent writes explicitly and recalls by FTS5 and embedding blend. One fail-closed allow-list of lifecycle states filters recall, search, list, export and sync, with a negative test per lane, and federated writes lacking a verified author signature can be quarantined until one arrives. The weakness is wiring: the revision ledger, consolidation tombstones and the signed-approval quorum are resolved or tested and never reached in production, and the approve tool takes its approver as a parameter.

Selvedge records why a code entity changed, and what was rejected, as an append-only log. Each memory is a change event on a path such as users.email, carrying the reasoning, a constraint, a stale_when condition and, for an approach never written, change type reject. A supersede event re-opens a verdict by id instead of editing it. Every write lands beside a SHA-256 chain record in one SQLite transaction. A Claude Code PreToolUse hook blocks schema edits on reverted entities until prior_attempts is checked. What is weak: status is whichever event is newest, so a plain modify after a reject silently restores the path. The gate's check reads telemetry with no session column, and no single event can be deleted.

local-memory-mcp retires facts by closing an interval, and forgets that it retired learnings. An MIT stdio MCP server on one SQLite file: learnings, decisions, session summaries and an entity graph, searched by FTS5 BM25 and in-process multilingual-e5 cosine fused with RRF. Row and vector commit in one transaction, a destructive fuzzy merge was removed and its test rebuilt to fail if it returns, and a superseded observation stays readable through entity_open({asOf}). The correction story stops at the graph. The duplicate gate skips archived rows, so a learning archived as wrong is accepted again when restated. An export round trip drops every learning archived with a reason. Reflect calls a learning never recalled because no read path counts recall.

archeus gets the freshness bookkeeping right and lets its lessons around their own review. A Claude Code launcher, formerly claudectl, it keeps one JSON graph per project of model-extracted module facts and session-distilled lessons, injected through a CLAUDE.md digest, path-scoped rule files and an opt-in prompt hook. Provenance advances only for modules actually extracted, a fact a re-read no longer emits is invalidated rather than deleted, and a stopword prompt retrieves nothing. Lessons wait as pending, but the extractor's own confidence approves them at 0.8, a pending lesson seen in three projects is promoted to the global CLAUDE.md, and module-edge expansion still injects invalidated facts.

Marvis gives coding agents a workspace-scoped incident memory inside a self-hosted company brain, and ships its correction layer dark. The open core of MarvisX runs an in-process MCP server over one SQLite file; its memory is a learnings table of post-mortems with prevention rules, recalled by check_learnings before risky work and surfaced by session_brief at cold start. HTTP and MCP share one use-case layer, so every learnings use-case read binds the caller's workspace once; the knowledge-graph search lane does not. The weakness is correction: system-time supersede columns, a binary live filter, a cosine write gate, a proposal queue and a nightly re-scan are all in the tree, behind a default-off flag, and no reachable path ever sets invalid_at.

Attune AI frames every recalled memory as untrusted evidence and then injects it anyway. A Claude Code plugin, its Stop hook asks a local Ollama model for up to five findings per session, gates them for PII and secrets, and stashes them in JSONL or the Redis Agent Memory Server; SessionStart injects the newest five. Writes count only when a readback proves them, and a human verification is bound to a digest of the text it verified. The weak side is that labels stand in for decisions: the session-start block fills with other projects' findings under a "this project" header, a wrong verdict leaves the memory served, and the review-gated promotion into the curated tier has no caller.

Session Recall indexes every Claude Code, Codex and Cursor transcript into one local SQLite file and keeps a distilled project memory beside it. Five MCP tools search, grep and walk raw turns. An embedding-space fingerprint turns semantic ranking off, and says so, rather than mix vector spaces. A nightly meta docs job has a caged agent write bug, procedure and decision entries into git through four verbs, and search before create is enforced by the server. Against that: entries reach every later unscoped search with no review, the delete reason the tool demands is discarded, and entries are indexed with an empty cwd, so scoped search never returns them. One mark, negative_eval.

Mnemo supersedes carefully and forgets loosely. An Apache-2.0 HTTP MCP server on one SQLite file per namespace: FTS5 tiers and sqlite-vec cosine fused by RRF, weighted by recency, access and LLM-scored importance, then reranked. Update closes the row in one guarded UPDATE … RETURNING with a race test built to fail if the guard goes, and a changed embedding model is refused at open. Delete only closes the row, and history and export still return the text. The advertised memory_audit table has no writer, the extractor's supersedes list is dropped, and the subject scope the evals measure lives on a CLI core the server never calls.

osk-system binds every citation to the bytes it cites, and leaves search open across projects. A Python MCP engine over a Git-backed Markdown vault: hooks for Claude Code, Codex, Kiro and Antigravity capture each round verbatim, and the agent distills nodes whose derived-from edges reach the round. A recheck ledger records both bodies' hashes per citation, so a changed source reopens every citing node; append-only ledgers with causal parents survive multi-device union merges. Writes are confined to the session's scope while search reads every Space, the cascade stop on corrections is a prompt, and node edits leave no record outside Git. One mark, negative_eval.

IHMT dates a correction and then lets the old value tie with it. An MIT, standard-library-only MCP server for Claude Code, Codex and opencode that files saved text as self-describing leaf files under extractive summary branches and walks them from root.json with BM25 and a beam of three. A near-tie returns AMBIGUOUS and asks for a clue instead of guessing. Thirteen regexes put six attributes on a dated timeline, and a superseded value's text comes back with an OUTDATED notice. The timeline never reaches the ranking, so the old and new address tie; a pruned branch is reported as nothing stored; and several agents share one store whose catalog and timeline have no write lock. No marks.

Graph, temporal, and symbolic memory

holomem, graphiti, cognee, hipporag, holographic, gini-agent, memvid, neo4j-agent-memory, memary, m-flow, nova-ai, argo, bwmem qwen-mm-plugins, hillock, growmos, sift-kg, corbell, mettaclaw, omegaclaw-core, agentic-graphrag-blueprint, brainapi, tempomem, inite-brain, dense-mem, mazemaker, xerj, janus-graph, utopia, rushdb, nornicdb, create-context-graph, semantica, origintrail-dkg, kaeru, waggle, anda-db, temporalstore, osiris, holo-invariant, tessellum, mushroomdb, theurian, chitta-field, claudinio-brain, loreweave, mindreader, mnesio, mnestic, automem, emem, dreamgraph, tidemind, smriti

Structure is the retrieval mechanism. BrainAPI is the family's cleanest answer to a question the rest of it fudges: which facts are allowed to accumulate. An event is a node rather than an attribute, so an actor's involvement is a leg to the event — and _invalidate_superseded_relationships can then hold two rules at once without a special case, stated in its own docstring: "an actor accumulates one leg per event and none of them supersede the others," while a functional attribute like LIVES_IN has one current value and a newer edge closes the older. Three committed cases pin it in both directions, and the closing is enforced by a validity predicate called from thirty-odd sites, checking both predicates of a two-hop path so a superseded edge cannot be laundered through the middle of a chain. What the closing does not do is record a second clock: invalid_at is set to the successor's valid_at, so both ends of the interval are world time, and all three read sites test the field for truthiness rather than comparing it to a query instant — a boolean in a timestamp's clothing, with the only history affordance a keyword regex over the question. The project also publishes the most checkable benchmark artifact in this family and undercuts it in the same repository: the committed LoCoMo run's 152 rows recompute to the ledger's 95.39% exactly, and the NOTES.md beside them says the run is a selective re-score on one conversation of ten, judged by the answerer's own model, with a "cold full re-run under frozen v4d harness recommended before external claims." The caveats are the project's own; the file that calls itself "top published scores" carries none of them. Agentic GraphRAG Blueprint is the family's cleanest statement of what a derived summary costs to keep true, and of what happens to everything the summary sits on. Its community reports are keyed by report-{sha1(sorted members + sorted internal edges)[:12]}, so the id is the content hash: whether to regenerate is a membership test, which reports went stale is a set difference, and an unchanged community is provably unchanged. That one decision buys the only deletion in the system, and its incremental claim is tested the right way — test_run_ingestion_incremental_skips_unchanged_files re-runs over unchanged content and asserts fake.calls == calls_after_first, zero model calls, then asserts exactly two on an edited file. Underneath the reports nothing is removable, and the shapes are worth naming because they recur across this family: a chunk's id is chunk-{basename}-{index}, so an edit that produces fewer chunks leaves the previous version's tail retrievable under the same source; entities are written under if name not in known_entities, freezing the first description an extractor wrote and leaving the store's own update-on-exists branch unreachable; and relations append to a MultiDiGraph with no dedup, so an edited file re-contributes a parallel copy of every relation its unchanged paragraphs still support. The project also ships the invariant that would catch half of this — "vector store is empty but the graph has data; forcing a full rebuild" — and Terraform that guarantees it fires, mounting /app/data from an Azure File share while leaving CHROMA_DIR=/app/.chroma_db outside the mount, so every container restart re-extracts the corpus the incremental path exists to avoid. Graphiti tracks transaction time and real-world validity separately, invalidating facts by closing an interval rather than erasing history. HippoRAG seeds a personalization vector and lets Personalized PageRank diffuse relevance instead of planning hops, and links similar entities rather than merging them. Holographic encodes facts as SHA-256-derived phase vectors so entities can be bound and unbound algebraically, with no embedding model to version. Gini reimplements the Hindsight model locally with bi-temporal columns and four RRF-fused channels. Neo4j Agent Memory adds a third tier beside short and long term — reasoning memory, recording traces and tool calls through a context manager, so a raised exception becomes the outcome and failures are stored by default where almost everything else here records only successes. growmos is the family's smallest complete loop, and the one whose maintenance is not a request. A dependency-free CLI that keeps entities, aliases, edges and raw mentions as JSONL in .growmos/ beside the repository, hands every judgement step to whatever agent you already run as a task packet, and takes the deterministic half — hashing, ids, validation, scoring — for itself. Two decisions are worth lifting. Its edge id is a hash of (source, normalized predicate, target), so the same triple from a second document appends a source rather than a row and confidence becomes the count of documents that agree — corroboration in place of a model's self-reported certainty. And the maintenance loop is enforced by control flow: the installed Stop hook returns {"decision": "block"} while extraction or resolution packets are pending, so a session cannot end with the graph behind, with a re-entry guard so the block never loops. That is the direct answer to the failure this atlas records more than any other, where a contract asks a model to update memory and nothing checks that it did. The same hook file also tells the agent the loop "does not need permission", which is the other kind of thing entirely. It carries no capability mark: provisional is counted by every health surface and filtered by no query, when is a validity range nothing reads, and a review verdict lands in a memo rather than on the node it judged — see growmos.

Memary is the family's minimum viable member and the clearest one to read: a LlamaIndex graph, plus forty lines that count how often each entity has been mentioned — the smallest complete instance of reinforcement by frequency in the atlas, and the one where that mechanism is demonstrably inverted at its only point of use. Memvid gets time travel from its storage format rather than its schema: an append-only file of immutable frames, so a memory card keyed entity:slot can be read as of any past instant and a whole session can be replayed.

Qwen MM Plugins is the family's only memory of something nobody said. Every other store in this atlas remembers an utterance, a fact extracted from one, or a trace of an agent's own work; this one remembers a video. Its video-memory capability, one of two memories in a multimodal plugin suite, turns hours of footage into a four-level tree — Root, SuperEvent, MacroEvent, and a leaf subgraph of typed entities, timestamped micro-events, on-screen OCR text and edges labelled SEMANTIC | CAUSAL | TEMPORAL | HIERARCHICAL | SPATIAL | IDENTITY — and gives an agent a tool per level so it can start at a story arc and descend to a three-minute window. Retrieval is hybrid in the strict sense the fusion pattern argues for: a dense cosine arm and a BM25 arm over the same nodes, combined by reciprocal rank rather than a tuned score blend. A check_dimension_compatibility guard catches a store embedded by one model and queried by another, and the loader answers it by dropping to BM25 alone with a log warning — no mismatched ranking, and no sign in the result that the dense arm is off.

Two things separate it from the rest of this family, and they point in opposite directions. Its time_range is content time with no record time beside it — when something happened in the footage, never when the memory was written or by which model — which is the inverse of Graphiti's bi-temporal pair and leaves every row an unattributable model opinion. And it cannot be corrected at all: no delete, update, supersede or tombstone surface exists in the capability. What it does instead is unusual enough to be worth the family's attention. Its skill file tells the agent that memory is "always coarse and maybe inaccurate" and mandates a frame-level re-read of the video after any hit, so the memory is positioned as an index over a source that still exists rather than as a record to be believed. That is a coherent answer for a store nobody can fix, and it is worth separating from the more common position here, which is to treat retrieved memory as fact and have no recovery path when it is wrong — but it holds only while the reader obeys a prompt, and the wrong node stays in the graph to be retrieved again tomorrow.

Nova AI is the family's only symbolic member in the older sense of the word, and it is the one that shows what the word costs. Every other system here builds structure over embeddings, hashes or a graph database; Nova calls no model at all, and its knowledge is a hand-built concept graph where a word has senses, a sense has is_a/part_of/causes edges, and every edge carries its own source and confidence. Because there is no model to defer to, each epistemic decision had to be written down, and two of them are better than most of this atlas manages: a relation is stored only after the user answers "Mag ik onthouden dat 'X' is een soort van 'Y'?" in the affirmative, and every concept carries an audit_log on the record itself recording old and new values. Then the same absence shows the other way. find_contradictions checks a word's parents against incompatible category groups, works, and is called by nothing — one facade re-exports it and no path invokes either — and no code anywhere removes a relation, a sense or a concept. Knowledge is strictly monotonic in a system whose chained inference walks straight through a wrong edge and then explains its reasoning. It can be told to forget a preference and cannot be told that a stored fact was wrong.

ARGO is the family's case where the graph is deliberately not the memory. Its unit is an ArchiMate 3.2 element, and the constrained relationship vocabulary is the design's whole argument — a rule engine rejects a connection the language forbids, so architectural claims are typed rather than extracted. But design/KG/SystemArchitecture.json is canonical and Neo4j is a projection that clearGraph wipes with DETACH DELETE and re-CREATEs on every sync, so the graph carries no supersession, no history, no status and no audit — correction lives in a file and a review process, and the store does not pretend otherwise. What it does carry is a read path worth copying: retrieval fails closed when the embedding index is not qualified rather than degrading silently, and a write that fails to index cannot report success, both asserted by committed acceptance cases. Its test posture is inverted from the corpus norm — 27,152 lines of tests to 6,674 of implementation, weighted toward architecture fitness functions — and the suite is nonetheless red at HEAD: 114 passed, 8 failed, including a credential-boundary case whose taint rule is file-scoped and flags a Cypher query carrying no credential. A check that cries wolf is a check that gets skipped.

Hillock is the family's smallest member and the one that moves the whole trust decision to the read path. 1,754 lines, AGPL-3.0, a local console against Ollama: facts are triples in SQLite with no timestamp, provenance, status or scope, so nothing about a stored row can express doubt. What carries the weight instead is a gate made of control flow — the model is invoked only inside the branch that has already matched a stored fact above threshold, and every path that matches nothing returns a fixed refusal without calling it at all. That is the difference between asking a model to decline and making the un-evidenced question unaskable, and it is the cleanest instance of the second in this corpus. Beside it sits the failure worth the reading. Its gate bundles each fact from exactly three components while bundling the query from all of its surviving tokens, so cosine falls as a question lengthens against a fixed 0.42 threshold: the same fact passes at six components and is blocked at eight. A system whose central claim is knowing when to refuse has calibrated that refusal against an unstated assumption about phrasing. Its correction path is the other half of the same shape — a DELETE narrowed to five named functional predicates, of which exactly one is ever produced by the extractor's own normaliser, so everything else accumulates.

Tradeoff: structure answers questions flat stores cannot, but extraction and resolution mistakes have a blast radius proportional to how connected the graph is.

sift-kg and Corbell arrive at the same defect from opposite directions, and between them they state the rule. Both derive a store from documents a team already has, and both apply the human's judgement to the artifact a later pass regenerates. sift-kg keeps every extraction per document, so its graph is a genuine projection and a bad extraction is always traceable — and sift build reconstructs graph_data.json from those extractions while reading neither decision file, so an applied merge and a rejected relation are undone by the rebuild its own agent skill tells you to run when documents arrive. Both persistence semantics sit in one function twenty lines apart: the merge branch writes with no prior read, truncating every CONFIRMED and REJECTED back to DRAFT, while the relation branch reads, dedupes on (source_id, target_id, relation_type) and extends. Corbell has the provenance half right — every Decision extracted from an ADR carries its source_file — and the gate wrong: CandidateDoc.confirmed is honoured by the learner and set by exactly one caller, for every candidate, when auto_scan is on, which workspace.py:47 defaults to True; there is no command that closes it selectively, and the next docs:scan overwrites the file a person would have hand-edited. A rebuildable projection is only safe when every correction is an input to the rebuild. See sift-kg and Corbell.

Two forks of one MeTTa agent are the closest thing this corpus has to a controlled experiment on whether a use-signal is worth its complexity. MeTTaClaw and OmegaClaw share root commits and the same roughly 200-line agent core on the Hyperon stack, and both store memory as (timestamp, atom, embedding) written only when the model calls remember. They diverge on the read path. MeTTaClaw keeps a reinforcement ledger the agent itself drives — promote and demote are skills described to the model as marking a memory it "found useful" or does "not find useful ... anymore" — decays that standing as value × (1 + Δdays)^−0.7 computed on read, and returns a promotion-ranked slice appended to a distance-ranked one rather than blending the two into a score. OmegaClaw removed all of it: src/memory.metta is 61 lines against 112, query is a single line returning the top twenty by distance, and the recall budget doubled in the same move.

Neither has published a comparison, and each keeps something the other lacks. MeTTaClaw has a persistent AtomSpace the agent extends with callable functions, bounded and exported every loop, and no tests at all. OmegaClaw has 32 test files against a live container, including the pair that earns its mark — a fact-shaped statement must not grow the vector count, with an explicit-remember prompt on the same counter as the control — and a memory that, once written, cannot be deleted, superseded, marked or even demoted. Both carry Non-Axiomatic Logic as a callable reasoning tool over truth-valued atoms, and in both the atoms that persist carry no truth value: OmegaClaw's documentation is explicit that the reasoning AtomSpace is "per-invocation (fresh AtomSpace each |- call)". A calculus for merging conflicting evidence sits one function call from a store that has nowhere to put the result.

Chronotope is the family's spatial member: the graph is a scene, and the retrieval is geometry. A numpy-only library over one SQLite file, it turns labelled 3D detections into object nodes through a deterministic fusion arbiter — merge above 0.62 on a weighted mix of distance, box overlap, feature cosine and label, reject below confidence 0.30, otherwise a new node — infers near, on and under edges from boxes, adopts objects into regions by centroid, and answers what's on the table by traversing the edges into the anchor. Its strongest property is on disk: every mutator fuses the staged observations before it commits, and a test reopens the file to prove no observation is ever persisted without a node. Its weakest is what happens to evidence the arbiter refuses or a caller forgets — the rows stay in the file, unlinked, keyed on nothing and read by nothing, so the same rejected sighting is re-scored from scratch every time and a forgotten object is recreated by the next frame. The exclusion tests that earn its one mark are exact: a radius search returns ["near"] with far in the store.

holomem is the family's smallest member, and its finding is a forgetting policy with a confidence gate and no store. A 423-line module — MIT, nine commits from 2 to 6 September 2026 — that holds every fact as a weighted triple in one fixed-size complex vector, a Fourier holographic reduced representation whose symbols are derived from a hash of their names so the vector rebuilds identically from a plain fact list anywhere. An unconfirmed fact halves every 45 days from its last mention, relearning adds a quarter up to a ceiling, a contradiction multiplies the old value by 0.35 rather than deleting it, a second trace bound to the month of learning answers what a relation held then, and every answer comes with a margin over the runner-up that the README gates as a z-score at four, because the absolute threshold it replaced stopped firing as the trace filled. A committed forty-cell capacity sweep places the collapse near a quarter of the dimension and the README's table recomputes from it exactly, with three retracted figures named. Nothing persists, nothing is scoped, nothing is recorded, and no test asserts a damped value stays out of an answer; no mark. The same author's membench harness, on the benchmarks page, scores it against a corpus that knows when each fact stopped being true.

Janus-Graph puts a durable queue and an MCP surface in front of Graphiti on FalkorDB, and shows where a wrapper can undo the engine it wraps. An add_episode call is one SQLite insert; a sweep hands each episode to Graphiti.add_episode with the sweep's clock as the reference time — the worker reads a created_at the claim query never sets — so a day-relative phrase in a delayed or replayed episode resolves to the wrong day. Between the model and Graphiti sits a schema-repair wrapper that hands each rule the input at the first validation error — for a malformed edge, that edge — and every rule falls back to an empty list, so one bad item empties an episode's extraction or its contradiction list and the episode is marked done. search_memory passes invalid_at IS NULL, which is what earns the bitemporal mark and also hides any fact extracted with a future end date; it reads a graph named by a driver default while writes follow the configured group id, and under the shipped example config the two differ and recall returns nothing. The nightly dream run reports clustering, deduplication and pruning as done without touching the graph, and its one working phase requeues dead-lettered episodes without closing their dead-letter rows.

Utopia is what this family looks like when the second clock is read as carefully as it is written. One Rust binary over one Postgres extracts entities and facts from uploaded documents and from sentences an agent records over MCP, types them against an editable ontology, and keeps valid_from/valid_to with a precision per endpoint beside recorded_at/invalidated_at — and the read predicates for both axes are assembled in exactly two modules, world_axis.rs and record_axis.rs, so no read site hand-writes invalidated_at IS NULL and every one of them takes an instant, documents, chunks and entity merges included. A correction never overwrites: a new value on a relation the ontology marks functional closes the old interval and links back through supersedes, and under a confidence floor or with ambiguous timing it opens a conflict for a person instead of rewriting history. The memory path is narrow and deliberate — remember is the only write tool exposed over MCP, and the triples extracted from a remembered sentence land in pending_facts, off the graph and out of retrieval and inference, until an editor confirms them; rejecting one writes the triple into rejected_facts, which the next proposal consults, so a re-extraction does not re-ask. Two limits are stated in the source rather than discovered: that key never reaches bulk ingest, so a document restating a rejected triple asserts it, and the lexical index holds only current chunks, so record-time search returns correct hits and misses the ones since replaced. It carries all seven marks, and its sharpest idea is none of them: an automatic entity merge is held for a person not when confidence is low but when undoing it could not recall what it had already emitted — a contradiction a checker would raise, derived facts that would be rewritten, or an answer already given in a conversation.

RushDB is the family's clearest case of a database that carries an agent-memory protocol without the database knowing anything about it. A NestJS server turns pushed JSON into a Neo4j property graph where properties are nodes and embeddings hang off the value relationship, and the memory layer is a separate 600-line client package: EPISODE and MEMORY_FACT records with SHA-256 identities derived from canonical JSON, five authorization-scope fields written as ordinary properties, and a recall that puts all five in where before any similarity is computed. That last detail is the mechanism worth taking — canUseVectorIndex = !hasWhere && !hasMultiLabels, so any scoped query drops out of the approximate index and into a Cypher plan that narrows candidates first and scores every survivor exactly, which trades a scan for the silent recall loss of a post-filtered neighbour list. Deletion is DETACH DELETE and the embedding rides on a relationship it removes, so there is no second index to reconcile. What the tree does not contain is the other half of its own design: the durable outbox, the fail-open recall timeout, the bounded capture and the deactivation of a superseded fact are all specified in its skills package and implemented in harness adapters that live somewhere else. Nothing here ever writes a fact inactive, the supersession field occurs once as its own type declaration, and a corrected fact hashes to a new id that the scope filter admits beside the old one.

create-context-graph is the family's scaffolder, and it inherits a lesson about what survives a write surface you do not control. A CLI generates a whole application — a Python API, a web front end, an ontology, fixtures and one of eight agent frameworks — around neo4j-agent-memory's three tiers, and since its default backend became a hosted memory service, its templates carry a compatibility layer for a REST API that accepts only a name, type and description on an entity and has no way to add a relationship. The answer is a hybrid write shape: properties rendered into the description as markdown, edges appended to the same field as a fenced YAML block, deterministically sorted, documented on its own page, and written by three identical implementations, two of which a contract test holds in lockstep by diffing their captured call sequences — the encoder is careful work. What is missing is the decoder: no parser for that block exists in the tree, the only readers split on the fence and keep the text before it, the migration script the docs name does not exist, and the README's claim that the front end renders those edges is not supported at the pin. Correction is absent on both backends and the code says so — the reset counts what it cannot delete and prints that the endpoint does not exist, with a comment recording that an earlier version swallowed the error and reported zero removed. The scope story splits the same way: on the self-hosted path the generated seeder puts a domain key in the node key and nine REST read paths filter on it, while neither the CLI's library ingest nor the generated importer writes it and not one of the bundled agent tools' queries names it, under a test whose assertion is that a list has at least zero members. Those tools read the same graph, so scope_enforced is withheld. On that path the agent's free-form query tool is described to the model as read-only and executes writes. The reusable third is the ingest machinery — per-connector watermarks that advance only after a clean batch, a drainable failure log, and redaction wired into four separate content paths of the session connector.

Semantica has two memory stores under one façade and governs only one of them. Its ContextGraph carries the temporal model Utopia above is the reference for — valid_from/valid_until on every node and edge, recorded_at/superseded_at on knowledge-graph relationships, and a query_at_time that takes either axis or both — plus a removal vocabulary of four distinct operations: retract closes the window and leaves the record in state_at before the cut, purge removes the content and keeps a tombstone that deliberately holds only the id, the time and a reason, an ErasureCoordinator drives that across every store holding a copy and returns a receipt naming which ones it reached, and apply_revision supersedes a fact retroactively while keeping the prior version queryable on the record-time axis. Every graph mutation fires a callback that an attached version manager INSERTs into an append-only SQLite mutation_log. Beside all of that sits AgentMemory, a process dict whose one filter predicate recognises type, start_date and end_date and returns True for every other key — so retrieve(query, user_id=...) returns everyone's memories, and forget(conversation_id=...), the second example in that method's own docstring, matches every item in the store and deletes it. The days_old arm of the same function was fixed one commit before this pin, with a regression file that tests that arm three ways and the other two not at all. Its retrieval has the same shape of defect one level down: the vector branch builds its results and the loop that consumes them is indented into the sibling elif, so for the vector store the package itself ships, long-term recall falls through to keyword matching. The tombstone is withheld for the reason worth copying — the purge record omits the content on purpose, which is right for erasure and is exactly why no write path can consult it.

OriginTrail DKG V10 puts the graph on a network and makes trust something the protocol writes. Apache-2.0 node software whose agent memory is RDF in three named-graph layers: a private Working Memory draft per agent, a gossip-replicated Shared Working Memory for a context graph's permitted peers, and Verifiable Memory whose Knowledge Assets are Merkle-rooted on chain. Levels above self-attested come only from endorse and M-of-N verify confirmations, and an author's own trust quads are refused. Working Memory isolation is a graph URI that encodes the agent plus a caller check, tested in both directions. The family's usual strength — traversal — is absent from the path an agent actually recalls through: the OpenClaw memory slot is a keyword CONTAINS scan over every literal in six graphs, ranked by layer and blind to the trust levels the network charges to establish.

kaeru is a graph the agent thinks in rather than one it retrieves from. A Rust engine on embedded CozoDB with about seventy curator verbs: episodes, hypotheses with verdicts, contradicts reviews, supersession, reasoning chains saved as trails, role slots, and promotion of settled work into an archival tier. Validity-keyed nodes make every change a retraction plus assertion, so any node can be read as it stood at a past second, and every mutation writes an audit node into the graph. The limits are in what the structure is allowed to do: the time axis is record time only despite the bi-temporal label, a refuted claim recalls like a supported one, and the initiative scope is whichever one the agent names.

Waggle governs one path into its graph carefully and leaves the default path open. A project memory for coding agents over MCP: each turn is stored verbatim, sentences are extracted deterministically into typed nodes with evidence spans and validity windows, and opposed decisions get a contradicts edge. In its browser workspace a person approves the exact text of an agent's correction before it supersedes the old node. The MCP query tool agents are told to use defaults to hybrid retrieval, whose printed hits skip the validity filter, so a superseded decision still reaches them.

Mazemaker puts the validity window on the edges and leaves the claims without one. A C++ associative-memory core — Hopfield, VSA, an LSTM, kNN over SIMD — under a Python client with a sleep-cycle consolidation engine whose named phases run over a sampler that deliberately reaches old and low-salience slices, because cross-session supersessions live there. The supersedes phase is the correction surface and it is narrower than the pitch: a pair qualifies only when both memories carry numeric, dollar or quantity tokens and those numbers differ, so a correction with no digit in it is never seen. What it writes is a directed edge, not a state, and recall demotes the older hit by half a point, tags it superseded_by, pulls the newer one in and returns both — a label rather than a filter. The temporal columns tell the same story: connections carries event_time, ingestion_time, valid_from and valid_to with an as-of read over the validity pair, while the memories row carries created_at and last_accessed and nothing else, and ingestion_time is written, migrated and returned without ever appearing in a filter. Within one recall the neighbour walk passes the requested instant and the supersedes traversal beside it does not.

Dense-Mem puts both clocks in the query and the invariants in the schema, then hands the gate to a model. A self-hosted Go service over PostgreSQL where a relationship carries valid_from/valid_to for the world and created_at/recorded_to for when the store believed it, and a recall with an as-of instant gates both windows and joins the latest transition event at or before that instant, so the status it tests is the one that was in force then rather than the one in force now — a point-in-time answer rather than a filter over a past interval. The governance is where most projects put comments and this one puts constraints: a CHECK makes a row that is both candidate and active unstorable, the read path's indexes are partial on active-and-promoted so an unpromoted claim is absent rather than filtered, and each transition foreign-keys to the verification event and support decision that caused it under ON DELETE RESTRICT. A correction that lands on an identity whose history is inactive is refused as an inactive_relationship_collision rather than reviving it. What the vocabulary promises and the code does not staff is a person: needs_review, quarantined and disputed are resolved by background workers and a verifier the server refuses to start without, and no route lets a human approve a claim into fact.

INITE Brain keeps two clocks apart and then fences only one of the surfaces that read them. A NestJS service over a SurrealDB database per tenant, where a fact carries validFrom/validUntil for when the claim was true and recordedAt/retractedAt for when this system believed it, a supersede closes the interval without stamping a retraction, and a six-value status filters every read by name. An asOf read gates validity and deliberately leaves knowledge time alone, and a committed test asserts the entity profile and the search lane gate the same three axes and no more, so a backdated fact cannot appear on one and vanish from the other for the same instant. The per-user fence on the search lane is unconditional and fail-closed; on the entity timeline and the competing-pair list it sits behind an opt-in flag, and the code's own comments record what the default costs — "a personal fact never produced a timeline event", "a user-scoped COMPETING pair was invisible to adjudication". Its GDPR erasure writes an HMAC-hashed row read by an admin listing and a diff service and by no write path, so it proves the deletion happened and does not stop the next ingest recreating the entity.

Anda DB says in four lines what this atlas has spent hundreds of reports circling. An MIT Rust workspace at version 0.13.0 — 232,178 lines, 169,548 of them Rust across fifteen crates, with 1,642 test functions — whose governance module opens: "Cognitive content may describe authority. Only this plane can grant it." A Space can hold a Proposition saying Alice is an administrator, an Assertion supporting it at high confidence and Evidence for both, and Alice administers nothing, because a grant is a row "no KML clause reaches" — "[w]ithout that separation, any path that can write memory is a path to privilege escalation, and every Agent memory system has such a path by construction: it is the entire point of the system." The same module splits three questions most systems answer with one number: should I believe this, am I allowed to touch it, how strongly may it influence what I do. Belief is a view and never a column, because storing it "would create a second answer that could disagree with the Assertions it came from, and nothing would say which one was right" — computed under three rules that read as a list of this corpus's recurring failures: absence of support is not rejection, "[s]aying the same thing three times is one voice repeated, not three independent voices", and two Assertions citing the same Evidence merge into one group because "[m]anufactured corroboration is exactly what an attacker builds". AS OF SEQ and FOR TIME are named apart in the history module's first sentence, one reading an append-only version log that every commit writes through a single macro, the other reading a world-time window the Assertion carries. Erasing a source strips the content from every artifact derived from it and leaves a row keyed by the content digest, so re-publishing those bytes is refused; and the erasure validator will not take a plan's word for a backup deletion, because "a model-authored plan is not such a receipt". Six marks. Approval is Principal-signed separation of duties rather than human review, and the whole thing costs a governed graph database with its own query language to adopt.

TemporalStore states the confound under its headline number and keeps the number. An Apache-2.0 Rust engine — 646,492 lines across three languages, 318,314 in the main crate, with 2,404 test functions — pitching one time-aware store in place of a vector database, a feature store, a counter tier and a stream pipeline, with a Redis-compatible surface and a token-budgeted ContextPack on top. The storage half is serious, and the ranking carries the best-argued constant in this reading: lexical scores are mapped onto the cosine scale saturating at half its ceiling, so that "in a MIXED store, a strong semantic (embedded) match still outranks a purely lexical one, while un-embedded nodes remain rankable (never a flat 0) instead of collapsing to recency order" — the hazard every backfilled store has and few name. No marks, for reasons that sit together in one function. valid_until_ms is the field that would close a world-time window, is marked "[d]eprecated hot-schema field: reserves this field" with no successor named, and is set to 0 by every constructor in the tree — while context_event_matches_filter still tests it under #[allow(deprecated)], so the branch cannot fire; the status check beside it reads an in-row field that is also deprecated and skipped on write, after status filtering moved to a secondary index. Both timestamps exist on the row, but primary_time_ms() prefers ingestion time, so an as-of read answers what had arrived rather than what was true. The scope rule — global visible to all, an agent-layer request also reading user and workspace — has one call site, in skill lookup, gated on whether the caller sent a non-empty scope string, and tenancy is a hash of account_id:tenant_id from the caller's own request defaulting to acct_local:tenant_local_agent. The benchmark documentation is candid in a way few are — it records that ingestion once dropped tool messages, "losing ~35% of real local context", calls grading against a recency slice wrong, and tells readers to scope adoption claims to memory and resources because there is no skills tier — and its headline still divides a ~1,333-token pack by a 1,698,940-token corpus the baseline arm never read, under a footnote saying so: "The saving headline is still computed against the full corpus." The 83% row beside it is the measured result.

mushroomdb makes a hidden node answer exactly like a node that was never there. An embedded Rust graph, MIT or Apache-2.0, version 0.6.8 and explicitly pre-1.0 — 177,137 lines across ten crates with 2,328 test functions — in which a relationship is a schema rule: declared once, it derives its matching edges on every later write, retracts them in the same commit when it stops matching, and leaves each edge carrying the rule, the score and the values that produced it, so the explain call answers with evidence rather than an assertion. The access control is the most carefully reasoned at this scale in the corpus, and its three failure modes all resolve to deny: "Empty role (no keys, no labels) = empty mask = sees nothing. Unknown role on a request = Err (never silently grant full access). Corrupt roles.json at open = roles poisoned." A caller's own mask can only intersect with the role's, and two details show the thinking went past the happy path — a hidden key returns the same 404 as an absent one, under a comment reading "no oracle", and the history handler drops edge events whose other endpoint is hidden, because "[a] role token must not learn about hidden nodes via edge history events". Even the sidecar's version numbering is an access decision: a file using a narrowing field is deliberately unreadable by an older binary, which "would resolve a narrowed role to its full label set", so an unknown version poisons, "which denies rather than over-grants". Three marks. The caveat is which surface holds them: the MCP server is stdio with no identity — its dispatch takes no role — so role there is an argument, "[t]wo ways to ask the same restricted question", coherent for a subprocess that already holds the files and not a boundary around the agent using them.

Theurian proves a record's absence by building the corpus that never held it. An Apache-2.0 Python engineering-decision record served read-only to agents — 313,168 lines with 4,209 test functions across 244 files, self-labelled alpha — whose pitch is stopping an agent re-proposing what a team rejected. Its absence proof does not assert that a withheld row fails to appear; it builds three deployments and requires that the one withholding records answer every query in a battery identically, "on the wire, refusals included", to one that never held them — with a third control deployment present because without it "an equality is satisfied by a build that wrote nothing, a query that matched nothing and a corpus whose plant was unreachable — three ways for this file to hold vacuously". Beside it sit tests that a withheld record never costs a visible one its slot, that a visible record's bytes do not move when its neighbour is withheld, and that the page boundary does not move: the side channels, not just the content. The same reasoning puts the visibility question before ranking rather than after, because asking late "made a withheld document able to occupy a candidate slot, and every number computed from those slots — count, usedTokens, fusedScore, droppedForBudget — move with it". A SQL validity filter was removed rather than repaired once found comparing ISO-8601 timestamps as text, which "silently disagreed" with the domain comparison across UTC offsets. Five marks. No human-review mark, and the README says why before a reader can find it: "there is no approval command and no approver field anywhere in this codebase, and nothing in the code checks that the merge happened" — what is enforced is that no MCP tool can write approved knowledge at all, so agents cannot approve; that humans did rests on the team's pull-request discipline.

chitta-field makes a veto an Option rather than a zero. An MIT Rust associative-memory substrate at version 2.7.12 — 57,134 lines with 287 test functions and a C FFI — designed for shared NFS storage, concurrent writers and sub-millisecond recall, encoding memories as Sparse Distributed Representations of 64 active bits in 16,384 so the hot path is bitwise overlap rather than a learned index. status_multiplier maps Active | Verified | Observed | Proposed to configurable weights and Superseded | Contradicted | Archived to None, and the recall loop reads that as ….is_none() => continue. The distinction is the point: a zero weight is a number any later normalisation or re-rank can multiply back up, and an Option is not — the compiler makes every caller decide. Held separately, EpistemicStatus — UserStated | ToolDerived | ModelInferred | AutonomousSynthesis, commented "[h]ow a memory was obtained — orthogonal to confidence" — returns a plain f32, so provenance ranks a memory down and can never suppress it. Its contradiction detector draws the line most systems here blur: "claim-centric, not text-centric. Two memories contradict when they make incompatible claims under overlapping scope (same subject+predicate), not merely when they are semantically similar." Every mutation is an Op in an append-only segment whose records chain as SHA256(seqno ‖ op_type ‖ prev_hash ‖ payload), with a V3 header carrying a vector_space_id so replay fences out segments written under a foreign embedding model or dimension — a lineage failure that otherwise surfaces as quietly wrong neighbours. Two marks. The chain is per segment and there is one segment per writer process, so it is tamper-evidence within a writer and not a single order across them; and nothing is consulted at write time against a contradicted claim, so a restatement is caught by the next reconcile pass rather than refused.

Osiris stopped its parsers writing confidence numbers and made them declare how they knew. An AGPL-3.0 Python entity-graph engine — 269,603 lines with 6,388 test functions over PostgreSQL and a Redis event bus, served as MCP — whose evidence module names the defect it replaced: "[b]efore this module, every parser invented its own confidence number (0.4 → 0.99) with no shared meaning, so 'noise' was baked into the graph as fake-precise facts and nothing downstream could reason about why a node was believed." A parser now declares an EvidenceClass — self-declared, authoritative API, direct observation, derived, co-occurrence — and confidence becomes "a projection of the class, not a guess". The sixth class is the one to copy: CORROBORATED "is never assigned by a parser — it is computed at read time when ≥2 independent sources agree … Storing it would go stale the moment a third source lands", and it outranks a single authoritative source. Two marks. The write path matches the epistemics: "[t]he Actions layer — the only mutation path into the ontology … the domain write, its audit_log row, and any outbox / object_events rows all commit in one transaction … No bypassing", over append-only assertions with a backward supersedes pointer whose supersession is scoped within a source, so one parser correcting itself cannot silence another — which is also what makes read-time corroboration meaningful. objects.status and merged_into are a projection of the event log that reads filter on, and unmerge restores a merged object to active under a never-delete rule. What is absent is enforcement: the classes rank and never withhold, so a co-occurrence fact at 0.35 returns beside a self-declared one at 0.9, and agents are object types in one shared graph rather than partitions of it.

HOLO-Invariant scores the naive alternative on its own benchmark and publishes which metrics the naive alternative wins. An MIT Python continuity framework — 91,867 lines with 1,603 test functions and zero runtime dependencies — built on the premise that correction should become a verified relation rather than replace history, so "[o]riginal, correction, and target remain inspectable". Five bounded metrics over one committed fixture that "fixes the target before results are observed", under a closed condition schema "so undeclared fields cannot alter the scoring contract", with the reference result regenerated in CI and the comparison asserting both sides share a fixture_hash before reading a metric. Then the part this atlas has almost never seen: a plain latest-value store is scored on the identical fixture and passes two of the five — latest-justified recall 1.0, superseded resurrections 0 — with the deltas asserted to be exactly zero and a comment stating the claim, "[t]he difference is specifically uncertainty + lineage + stale-continuation behavior, not latest-value recall." Naming the metrics your baseline already passes turns a five-for-five scoreboard into a narrow claim that could have come out otherwise. One mark: passes_bounded_continuity_fixture requires no superseded claim to return as current and full recall of everything that is, so the must-not-resurrect half cannot pass by returning nothing. Every result payload records truth_claimed: false and accepted: false. It is not a memory an agent writes to — there is no store, no read path and no scoping — and the five metrics are bounded to one fixture's shape, which the identifier concedes.

Tessellum shipped the demotion path before the promotion path it guards. An MIT Python knowledge-construction system — 138,183 lines with 2,905 test functions — built on typed atomic notes, Folgezettel trails, a one-way CQRS split and a dialectic cycle with a Dung grounded-semantics solver. It says in its second sentence that it is "not an agent-memory store"; it is here because it keeps claims that can turn out to be false, and because of one module. Its demotion gate states this atlas's own central finding better than the atlas does: "a promoted claim that stops being true has no way to notice on its own, so a promotion path without a demotion path is a mechanism for entrenching whatever was believed first. That is not a hypothetical failure mode — it is the one the memory literature documents most consistently — which is why this gate ships ahead of consolidation rather than beside it." The protocol is a blinded test in three parts, each closing a way the test could cheat: the claim is suppressed by construction, because the request type "has nowhere to put" its text or id and a caller who smuggles it into the question is refused; the model is pinned with an explicit model_id and frozen_at, because one that re-tunes on the corpus it checks "would eventually regenerate its own promoted claim from the promotion, and the gate would certify itself"; and the verdict is a token-overlap ratio, because "[a] model must never decide the verdict: a demotion nobody can recompute is a demotion nobody can appeal." Its first trigger fires when a claim stops regenerating from its own sources "even with no attack against it anywhere in the log" — the case every argumentation-driven system misses. The entrance and exit once measured different quantities under one name and now share one measurement, and the missing-data convention inverts between them, because the entrance's conservative bound applied at the exit "would let a hole in the log lower the count and demote a sound claim" — so an inconclusive finding quarantines, never retracts. Five outcomes each carry whether they are conclusive, under a paragraph refusing to overstate: "[r]e-derivation tests reproducibility and fidelity, not world truth, causal validity or transfer." No marks: the statuses are computed from the argument edge set rather than stored, and there is no scoping — a statement about the mark definitions rather than about the work.

Claudinio Brain gives the current-value read no branch of its own, which is the structural answer to a failure this page reads often. An MIT Rust knowledge graph at version 0.3.0 — 13,173 lines over 28 files, one SQLite file, one binary — whose fact table labels its own axes in the schema ("Valid time: when this was true in the world" against "Transaction time: when the brain learned it") and binds them from different sources in one insert: the caller's --at and the clock. The part to copy is RecallQuery::for_when, which builds one temporal predicate for every mode so that Now is not a branch but AsOf(now) — "the two cannot drift apart because there is only one arm". A store whose current-value query works while its as-of query is quietly wrong looks correct in daily use and fails on the only question it exists to answer, which is exactly what happened one report earlier in Engram Cognitive. Its second distinction is retracted_at, annotated "set when we learn it was NEVER true" and kept in a different column from valid_to, so an expired value and a false one are not one row state; a retraction leaves every read mode including history, on the argument that "a retracted claim was never true, so replaying it would be a lie". Against that, scope is stored, partitions the vector index, and is still an Option the query defaults to None, so what separates one namespace from another is whether the caller remembered to ask. Loreweave makes the markdown authoritative and the database disposable. An MIT TypeScript temporal knowledge engine at version 0.37.1 — 19,552 lines over 100 files, a SQLite index over a directory of markdown — whose fact module opens with the inversion the rest of it rests on: "Fact lines in markdown are the durable record; DB fact rows are a replay." Every assertion appends a - [fact] line and every closure a - [invalidate] line to a dated journal in the user's own vault, and a rebuild "wipes and replays ALL fact rows deterministically, so the index stays a pure cache", which makes correcting an agent's mistake a text edit rather than a migration. Its temporal query is the most complete this page has read on these four columns: queryFacts takes asOf and asKnownAt as separate parameters over separate column pairs, and the comment on the second handles the case that separates a real transaction axis from a decorated one — "A fact asserted afterwards was not available to anyone reasoning at T, however early its validity was backdated to start." What it has no answer for is scope: no fact carries a tenant, project or agent key and no read applies a predicate, so the vault is the whole boundary.

Mindreader makes an omitted scope narrow the view instead of widening it. An MIT Rust MCP server over Neo4j at version 0.7.2 — 20,225 lines over 24 files — whose thesis is a refusal: "Mindreader gives AI agents a memory they must curate, not a history they can search", with nothing captured secretly and nothing silently overwritten. Layer memberships are stored on nodes and edges, and the rule the layers module exists to hold is the one this page keeps finding inverted elsewhere: "Empty scope is global-only. Named ids form an OR union." A caller who forgets the argument sees the global layer rather than every project, so the accident is an under-answer rather than a disclosure. The predicate is also applied to all three elements of an assertion and to the ABOUT anchor beside it, since "[r]elationship visibility also requires visible endpoints (enforced in Cypher)", which closes the traversal route into a hidden node through a visible edge. Its trust vocabulary is the same shape as the one two reports earlier: SpikeRank is documented as an "[e]pistemic fact classification used in retrieval ranking (Knowledge highest)", so a lone unconfirmed Signal is sorted below better-founded facts and handed over anyway.

bwmem writes the question its old schema could not answer into the migration that fixes it. An AGPL-3.0 TypeScript memory SDK at version 0.11.2 — 17,259 lines over 105 files on PostgreSQL with pgvector — whose 007_bi_temporal_facts.sql opens by naming the gap rather than the feature: the table already had validity bounds, and "[w]hat was missing is the second time axis — WHEN WE CHANGED OUR BELIEF — distinct from when something was true. Without this you can't honestly answer 'what did we believe about X on date Y' — you can only answer 'what was true on date Y.'" The read that follows takes both instants and defaults both to now, so the ordinary call and the historical call are one code path and the historical one cannot rot while the current one keeps working. Two other things are enforced where they cannot be forgotten: fact_status is constrained by a database CHECK to four values with every read filtering to active and the partial index built on that same predicate, and user_id is a required first argument on every public read with no unscoped variant anywhere in the tree. Its open trade is the corrections log, which records old_value and new_value in the clear — what makes "how did we come to believe what we believe" answerable, and what Verimem argues an immutable log must never hold, taken here without a purge path that reaches both tables.

mnesio puts a floor underneath its safety thresholds, and a test standing on it. An Apache-2.0 Rust memory of 58,766 lines across twenty crates that compiles batches of agent outcomes into versioned policy artifacts — prompts, heuristics, retrieval rules — and lets none of them activate without clearing EvalReport::is_committable(): every canary passed, the safety probe passed, the objective delta at or above zero. Most safety gates this page reads are a set of configurable numbers, which means they can be widened until they admit anything. This one claims otherwise — "setting every configurable gate threshold to its weakest value still cannot bypass the baseline" — and backs the claim with fully_relaxed_gates_still_reject_baseline_failure_through_pipeline, which relaxes the gates and runs the whole compile pipeline before asserting the rejection, beside unit tests that trip each condition alone and name the invariant that broke. Its graph is bitemporal for a stated reason rather than by convention: evolution "invalidates the previous version and emits a new one", so "[a] flat 'current' graph would lose that lineage the moment the worker fires", and is_live_at conjoins the two axes — "'Live' here means both: the memory was valid at at and the system hadn't tombstoned it before at." What it has no answer for is the question its own subject raises: no person reads a policy artifact before it activates, and the canary set feeding the floor has no guard of the kind the floor itself has.

mnestic adds a second clock and refuses to let you set it. A maintained hard fork of CozoDB — MPL-2.0, 125,343 lines of Rust, 282 commits past the fork point at cozo-core 0.18.0 — kept independently as a substrate for agentic memory, with an unusually scrupulous FORK.md: not the official project, not endorsed, neither the name nor the package identities claimed, upstream's per-file notices preserved under MPL-2.0 §3.4. Upstream already had valid time, the trailing key column that says when a fact holds in the world and that an application may set into the past or the future. The fork adds transaction time behind it — when the database learned it — as a second trailing key component with its own refusal: a supplied value fails with "TxTime is engine-assigned at commit and cannot be supplied", because a belief axis a caller can write is one a caller can forge. The clock is max(now_µs, last_tt + 1), allocated under a per-database critical section so transaction-time order is commit order, with the high-water mark persisted to a system key inside the committing transaction so a crash cannot leave it behind a committed value. Three marks. The third is the one this corpus keeps asking for and rarely gets: as-of reads asserted to return nothing, with the positive control in the same test — and the project knows exactly why that control matters, because release 0.12.2 documents a silent temporal corruption inherited from upstream where a float timestamp landed a row in 1970 and the matching read "returned zero rows and no error, ... indistinguishable from 'no data yet'." Three of the four sites were upstream's, one was the fork's, and the changelog says which is which.

T-Mem writes down the question, not just the answer, and refuses to let the question be read back as evidence. The code behind an EMNLP 2026 Main Conference paper (arXiv:2606.15405, verified to resolve to the title it claims) — MIT, 14,140 lines of Python, no marks. Its argument is that memory today is reachability-bounded: a record is findable only when the query resembles it, which covers descriptive recall and misses the case where a question and the memory it should surface share no surface form at all. So at write time it generates triggers — rehearsed queries a memory ought to be reachable from — in four families across two granularities, and indexes those beside the content. The commitment that makes it safe is the one to copy: triggers never reach the evidence path, so a model's invented paraphrase is searchable and can never be returned as a record. Any design that indexes by generated text without that rule has built a way to launder invention into memory. What it is not is an operating memory: nothing can be corrected, superseded or deleted anywhere in the tree, user_id_list is written on every scene and item and read by nothing, and there is no server, MCP surface or library API — it is a batch pipeline over a benchmark. Two things a reader should know before reusing it: indexes are written and loaded with pickle, so consuming one executes its producer, and the LoCoMo-Plus subset the paper contributes ships no committed result, so its headline gap cannot be recomputed from the repository.

AutoMem makes "current" the default read over FalkorDB and Qdrant, and loses the validity window on one of its two payload writers. A single-tenant Flask service reached through the author's mcp-automem client or a bundled MCP bridge. Ranked recall withholds memories whose t_valid/t_invalid window excludes now or that an INVALIDATED_BY or EVOLVED_INTO edge supersedes, and injects the head of the chain in their place; the client's supersede mode writes that state. The embedding worker builds Qdrant payloads without the window, so vector hits skip that half of the check. Tag listing and id fetch skip the pass, scope is a caller-chosen tag, and the Qdrant recovery script rebuilds nodes without validity or edges.

eMEM is the family's second spatial member, and the one built for an LLM to query rather than a planner. The code of an embodied-memory paper (arXiv:2606.03374), it stores a robot's observations with a position, a timestamp and a perception layer across SQLite, FTS5, HNSW and an R-tree, behind ten read-only tools in which meaning, radius, time and layer compose in one call. Episode and DBSCAN consolidation write gists; archival blanks the source text. The lifecycle is the weak part: archival reads observation age rather than time since consolidation, DBSCAN noise is never archived, nothing can be deleted or corrected, and the paper's two baselines are eMEM with fewer tools.

DreamGraph keeps imagination and belief in separate files and points retrieval at only one of them. It is a per-project MCP daemon that grows an architecture graph of a codebase: dream strategies propose speculative edges, a normaliser scores each against the fact graph, and only edges that clear confidence, evidence and a two-source floor are copied into the validated store that graph_rag_retrieve reads. Rejected edges are barred from reinforcement, closing a confidence-inflation loop. The gate leaks at its edges: a rejected edge decays out in about four cycles and its key then boosts the next copy, the agent's own lucid_action accept promotes any dream edge past the normaliser, and resolve_tension takes resolved_by: "human" from the caller.

TideMind stores what the agent says verbatim, then lets models rewrite it on every recall. A local SQLite graph behind three MCP tools and an Electron app, fed by every connected agent and by Logseq, Obsidian, Apple Notes and Notion. Landing links a digest to its neighbours at fixed similarity thresholds, pending links wait for an LLM judge, and traversal follows only confirmed ones. A person's tag rejection is kept as a row and refused on every later annotation pass. The weak part is content: deep reconsolidation on recall and a dedup merge that prefers the newer text rewrite memories with no review, and the desktop edit can merge into the node it then supersedes, hiding both.

Smriti keeps every turn and chains extracted facts by supersession on a world-time interval, in one SQLite file. Consolidation re-sorts the whole history for a subject-predicate-scope key by valid_from on each write, so a fact learned late about an earlier date lands in the past instead of replacing the present, and a separate enterprise package adds a knowledge-time axis with a revision table, making "what did the store believe on K about W" a SQL query. The controls stop short of the reader. Scope is printed into context and filters nothing, the quarantine flag is honoured only by search(strict=True) and never by context(), and the MCP server an agent uses builds the core class, which has neither.

Verification and trust-first memory

verel, rainbox, magic-context, metaclaw, gini-agent, core-memory, daimon, intaris, muninndb, omniintelligence, agent-memory-doctrine, velantrim-exocortex-crystal, ouroboros-agent-os, open-second-brain, portable-handoff, heimdall, agentdatabase, areev, open-knowledge-format, memory-garden, membrane, distill-kura, eliot-memory-os, inspeximus, pi-memory, mandalore, kannaka-memory, agent-memory-guard, re-call, temvera, verimem, anatid, huqan, yantrik-mind, fava-trails, mind-mem, aimee, vortx-emem, obelyth-cortex, recollect, minta, gmeow-ontology, mgi-mind, pluribus

These treat memory as a trust problem before a retrieval problem. Areev is the family's most complete account of the process by which a memory changes, and it governs the result at one read surface out of several. Three verbs do three different things and the read path treats them differently: supersede writes a successor with a justification and an authorisation list and is filtered out by superseded_by IS NULL; forget erases the row, clears the free-text index and reclaims the attachment bytes — with the standard written into the code twice, "a tombstone that leaves the text findable is not a tombstone" and "a tombstone that leaves the attachment bytes on disk is not a tombstone" — and replicates, because import_bundle replays the op-log record; and retract means two different things depending on which substrate answers — the OmsSubstrate trait and its in-memory reference implementation set verification_status = retracted, while the adapter over the real store rejects that mapping in a comment, "the honest mapping for undoing an engine-created ADD is a tombstone of that grain", and calls forget. So a loop rollback erases on a real deployment, and the status is caller-authored in practice: the reference substrate is the only automatic writer of "retracted" in the tree. Where the status does appear it is applied as -0.3 on a clamped priority score, so a grain marked retracted ranks lower and still reaches the model, and that is why trust_state is withheld here despite a four-value status held apart from a confidence float — the same shape as the mark NexusMem lost, in a much larger system. No case in the conformance kit exercises retract, so the one verb whose two backends disagree is the one the multi-backend suite does not cover. What it does govern is unusual: entity_at takes a world or knowledge axis so "what was true" and "what did we believe" are two answerable questions, and every review transition writes an immutable Observation grain, hash-chained to its predecessor, carrying the actor, the observer type and a because field that is a String rather than an Option. Verel separates confidence, retrieval strength, and verification state, carries rejected values forward, and fences recall as untrusted data. RainBox adds governed atomic correction, lattice-aware conflict detection, and rejected-value tombstones that block model re-assertion. Magic Context maps each memory to the files it describes and re-verifies when git reports those files changed, keeping lifecycle and verification on separate axes. MetaClaw applies the idea one level up, promoting a candidate retrieval policy only when it does not regress across eight measured deltas. Core Memory goes furthest on the axis: a record's epistemic grounding sets a ceiling on how trusted it can ever become, so a speculative memory cannot be promoted into canon by being recalled often — the guarantee is structural rather than procedural. Daimon attacks the problem one step earlier than any of them: the model is asked to label each item verbatim or inferred and to cite the span, and then code greps the quote against the transcript and downgrades the item when it is not there. Everywhere else in this family, trust is assigned by policy over a claim; here the claim's own evidence is mechanically falsifiable, which is why it is the only system in the atlas whose trust classes can be wrong in a way the system itself detects. Its one withholding state lives in a separate ledger: a value a person quarantines is dropped from the briefing and the recall index until a person releases it, and an agent can propose that verdict and never confirm it.

OmniIntelligence is the family's most complete lifecycle and its clearest demonstration that a lifecycle can be argued and unwired at the same time. A learned pattern carries two axes: a status deciding whether it may be injected, and a four-tier evidence ladder deciding whether it may advance, the second gating the first and never the reverse. The tier is monotonic, and the guarantee is the WHERE clause of the statement that writes it rather than the writer's discipline — a CASE mapping tiers to weights, so a redelivered Kafka message and a buggy caller both fail by matching no rows. Demotion is deliberately harder than promotion, with the 20-point gap between a 60% promotion floor and a 40% demotion ceiling named in the constants as the thing that stops patterns oscillating on variance, and an override bound that refuses to let an operator close the band. Every transition writes a row carrying a gate_snapshot of the conditions that justified it, which is the version of append-only memory audit worth having. And its feedback rule is one this atlas finds stated in code nowhere else: a violation counts as negative evidence only when the agent was warned and then observed to correct, because "the warning might have been a false positive" — the distinction between the memory fired and the memory was right that most feedback loops collapse.

Then four of those mechanisms do not reach the code that would use them. The cold-start promotion query deliberately admits unmeasured rows and the reducer it calls refuses every one of them, so a threshold loosened to unblock 5,384 candidates cannot have unblocked anything; the only test of that path replaces the refusing reducer with a mock returning success. verified sits at the top of the evidence ladder and nothing writes it. The manual kill switch — an append-only disable event carrying a required reason and actor, treated as a hard override that bypasses the cooldown — is read through a materialized view whose only REFRESH statements in the tree are inside integration tests. And the Goodhart and reward-hacking guardrails are a tested pure-function node that nothing calls, which the project's own node inventory records by marking seven nodes "(unregistered)". That last artifact is why these are citations rather than accusations, and a published list of what is not wired is a practice worth more than most of what it discloses.

Its other half, OmniClaude, is in the atlas for a different reason and belongs with the plugins above: it holds no memory, and it hashes one session in five into a control cohort that receives no injection at all. That is the only standing randomized trial on whether a memory system helps in this corpus, and its limits are as instructive as its existence — the identity hashed is the session rather than the user, so both arms are drawn afresh each session over a store the treated sessions keep teaching.

MythologIQ's Agent Memory is the family's specification rather than a system, and it carries the most developed deletion model in this atlas. Apache-2.0, 294 files: 22,400 lines of doctrine and 25 ADRs above a 10,400-line executable reference implementation. Its deletion metric is not a volume of removed bytes but a four-way partition of everything derived from a purged source — purged, declared_residual_controlled, declared_residual_uncontrollable, undeclared_residual — with the last cell a hard gate its own docstring calls "disqualifying and un-averageable". Residue is permitted; undeclared residue is not. Two constraints hold it up: "unknown is not a fourth bucket", so state whose derivation cannot be enumerated is declared uncontrollable rather than omitted; and "traversal completeness is itself the measurement", so independent_sweep re-derives residual status from a basis relation instead of asking the purge whether it finished. Running that path with a driver written for this review, a purge that traversed one hop leaves the projection-of-a-projection behind and the sweep returns it as undeclared. The design also states a conflict the rest of this corpus finesses — "deletion dominates correction", because a superseded version retained for reconstructability is, once its basis is purged, exactly the recoverable residue the deletion meant to remove.

Its second contribution is a test posture. InMemoryTemporalGraph reproduces the permissive semantics of the substrate it maps — physical deletion with no tombstone, no actor check, and partition filtering that "defaults to unfiltered" — with the reasoning written down: "A stub that were already safe would prove nothing about the governance layer under test: the negative paths need something real to escape through." That unfiltered default is the same defect this atlas documented in three shipping systems in one round, built here on purpose as a hazard. What it does not have is a store: the substrate is a dictionary, retrieval is token overlap, no run output is committed, and the repository says so itself — "passing fixture validation is not the same thing as proving a production memory system behaves correctly."

Open Second Brain is the family's most complete lifecycle in the fewest moving parts, and the only one whose confidence is a statistic rather than a score. MIT, local-first, living inside the user's Obsidian vault. Its subject is narrow and well chosen — what the user keeps having to correct — and its mechanism is a nightly dream pass that promotes repeated corrections into preferences. Confidence is wilson_low(applied, applied + violated) × freshness: a textbook 95% lower bound at z = 1.96 times a term decaying linearly to zero across a staleness window, so three-for-three cannot outrank ninety-one-for-a-hundred and an unused rule fades without a sweep. Nothing else here computes a confidence that is conservative by construction.

Its trust states are unconfirmed, confirmed and a quarantine probation whose asymmetry is the point: a confirmed rule whose evidence turns dominantly negative stays active and injected but is flagged separately in the digest, one further violated retires it, and one applied that restores the margin returns it to confirmed. The status is cross-checked against the folder the file lives in, so a hand-edited vault fails loudly rather than degrading a rule.

And it carries a rejected-value tombstone arrived at independently. o2b brain reject --reason <text> writes user_rejected_reason into the retired file — set only for user rejections, never for automatic ones — and the next dream pass treats that rule as a suppressor, swallowing signals on its topic before candidate planning because "re-growing it from fresh signals is exactly what they were asking us not to do." Suppression is scope-aware, which no other instance of this mechanism in the atlas is: an unscoped suppressor covers the topic everywhere, a scoped one only its own scope, and a signal without scope never matches a scoped suppressor — the answer to the standard objection that value-keyed blocking is too blunt. Each swallowed signal emits a signal-suppressed event naming the rule and the reason, so the refusal is auditable rather than silent. Every preference mutation is separately appended to Brain/log/pref-audit/<pref-id>.jsonl at the chokepoint where the content hash is computed.

The exposure is structural and the design cannot close it from inside: applied and violated are self-reported by the agent whose behaviour they describe, so a rigorous statistic sits on top of an input nothing independently samples. self-approval-guardrail.ts bounds who may confirm a cluster; it does not verify that a rule claimed as applied was applied.

Ouroboros sits at the family's edge, because its subject is not knowledge. It is a spec-first agent OS — 310,000 lines of Python, MIT — whose durable store holds no facts about the world at all: it records what the system believes the user asked for, and its trust machinery is aimed at intent rather than at evidence. Two orthogonal provenance axes carry it. LedgerSource says what kind of authority a value rests on; DecisionProvenance says how the decision was reached — USER_CONFIRMED, MODEL_INFERRED, TIMEOUT_DEFAULT, LATERAL_CONSENSUS, MAINTAINER_POLICY — and the split exists because a timeout-defaulted decision had been indistinguishable from a user-confirmed one, so degraded specifications executed silently. Only the two model-derived provenances face a clarity gate; the grounded three pass unconditionally.

Its transferable move is a rule about what may become a belief. An interview answer is classified once, where it enters, on an advertised prefix: [from-code], [from-repo], [from-research] and [from-data] mark a fact the caller adopted rather than a decision the caller made, and adopted facts are withheld from the slot requirements are read from while staying intact in the question slot, because sharpening the next question is what the observation was collected for. The rule is per-role rather than per-string, and one parametrized test asserts across all four requirement-consuming render surfaces that the observation's content is absent — with a companion test pinning the deliberate non-redaction of the question line as "intended behavior, not a conceded leak". Conflicts then resolve with no model in the loop: a fixed ten-entry source-priority ladder, then confidence, and CONFLICTING only on an exact tie, at which point the driver blocks rather than invent a merge — the disposition ladder resolve, do not just detect argues for, with the losing entry demoted to WEAK and keeping both its value and a written rationale.

What it does not have is a horizon longer than one build. The ledger is per-session and the lineage per-task; project_map.py can enumerate a project's past runs and refuses to truncate that history, but nothing reads them to answer we already decided this last month. A system whose front page reads "it gets smarter on its own" accumulates within a lineage and not across them — and the only committed experiment in the tree, a paired quality run with 46 evidence files, returns a verdict of inconclusive, declines to report cost because it "cannot be reported without fabrication", and refuses to generalize past its one fixture. The repository's internal discipline and its front page are not the same document.

Tradeoff: more machinery than an MVP needs, and it directly addresses the failures simpler systems discover in production.

Portable Handoff takes the family's idea and applies it to the one artifact nobody else guards: the thing a model writes about its own session. Its unit is a claim carrying provenance — one of eight named channels — beside a five-state trust, and cap_trust(provenance, trust) refuses verified to anything whose declared source is not in {git, tool, test, file, transcript} (src/portable_handoff/models.py:166), at parse time as well as at write. It binds the pairing, not the source: the draft author writes the provenance too, so a claim declared tool and verified passes. What holds outright is the repository snapshot, which finalize takes from local git alone. Where the family's other members enforce trust inside a store they own, this one enforces it on a file arriving from outside — and where they filter, it only labels, which is why the mark is withheld. See Portable Handoff.

Heimdall is the family's outlier: it verifies at read time and stores no trust. Every other member here keeps a status a writer set; this one computes a verdict per search hit by checking whether the filesystem path its note anchors to still exists — STRONG for lexical coverage plus a live path, REBUILT when the path moved and the node was rebuilt, WEAK for semantic-only, STALE when the anchor is gone — and then makes that verdict the primary sort key, with the similarity score demoted to a tiebreak so a verified hit cannot be buried by a better-scoring unverified one. That is this report's most-repeated complaint answered in four lines of ranking code. The cost is that nothing accumulates: a verdict is used to order one result set and discarded, so a node that was stale last week and rebuilt today leaves no trace of either, which is why the mark is withheld even though STALE does more than withhold — it deletes the node, after one bounded basename search. Since the first reading it has grown a second, opposite mechanism — a SQLite journal that a level-triggered reconciler converges against the disk — and the two never consult each other, which is its own lesson. The store underneath is a separate Graft daemon whose C source the repository now vendors and whose binary it does not. See Heimdall.

AgentDatabase is the family's clearest case of the policy being an artifact rather than a habit, and of what that costs when the store predates it. Two JSON files carry decisions the rest of the corpus leaves in comments. memory-mutation-policy.json maps every source type to an admission verdict per operation, and both raw_import and model_inference map to reject_persistence for add, update, retire and dispute alike — a model's own inference is inadmissible as durable memory by construction, which is a stronger guard than any review queue and needs no reviewer. memory-forgetting-policy.json sets eligible_statuses to ["active"], names retirement as retrieval_exclusion_not_history_deletion, and fixes the order in which an abstention explains itself. The read side matches: retrieval_decision returns UNKNOWN with a reason code and the missing_conditions that would have permitted an answer, so "I don't know" carries a diagnosis instead of arriving as an empty result set.

The cost is legible in the store's own data. All 198 live records entered as raw_import — the source the policy now refuses — and not one carries a transition, a supersession or a conflict, so the lifecycle is attested by tests and by a self-generated benchmark rather than by anything that has happened. That benchmark is the best-structured one in this atlas and reports 1.0 across all eight of its categories for a deterministic filter, against hard negatives built to differ on exactly the axes the filter checks. See AgentDatabase.

Open Knowledge Format is the family's format rather than a store, and it shows what trust looks like when a specification defines it and no consumer enforces it. A bundle is a directory of markdown files with YAML frontmatter; generated names who wrote a concept and verified lists who confirmed it, kept apart "because who wrote a concept need not be who confirmed it", and a consumer derives a tier — unverified, machine-confirmed, human-reviewed — from whether any verifier carries a human: prefix. status is draft, stable or deprecated; stale_after is an absolute instant so staleness is a comparison. Every one of those is, in the spec's words, "advisory signals, not access control", and the tree has one consumer, a graph viewer that renders them as badges: nothing filters, ranks or refuses on any of them, so the report carries no mark. Two things are worth carrying anyway. The attested-computation contract puts the sanctioned SQL in the memory, lets an agent fill only declared parameters, and makes did the sanctioned thing run a canonicalised-text comparison — with the caveat that the shipped attester checks a receipt the executing agent assembled and never re-reads the job it names. And the separation of generated from verified is the right shape, undone by the write path: a regeneration keeps the human's signature on text the human never saw, because nothing compares the two timestamps.

Velantrim Crystal is the family's admission kernel, and its rule is that discovery and authority are different code paths. A local-first Python store — AGPL, 750 commits since April 2026, 25,856 lines under core/ with 2,041 tests — where a fact enters as Observed, a truth gate requires a source, refuses a world fact whose source is model output as an invariant no configuration reaches, and applies a confidence floor before Validated and the canon (core/truth_gate.py:22-110); Contradicted, Deprecated and Collapsed are terminal and block a read even against a stale graph copy, a restricted bit is deny-dominant, and is_strict_canonical lets only a VERIFIED, Validated-or-ImmutableCore, unrestricted fact ground an answer or the query returns a reason code (core/canonical_view.py:160-245, core/query_pipeline.py:254-300). A curator queue approves, rejects to Collapsed or resolves a conflict under a compare-and-swap on the fact's revision with the audit event and the canon projection in one transaction, and the audit_log is a hash chain with a checkpoint against a deleted tail and an optional HMAC. An immune memory (core/immune.py) keeps rejected claim patterns keyed on their normalized text, recorded by a curator from the CLI or by the ingest path itself in strict mode, and ingest, the importer and the review diagnosis screen every claim against it before the gate, so a recorded value is refused on sight and only a force approval with a named actor and a reason gets past; that earns tombstone beside trust_state, audit_log, human_review and negative_eval. Erasure is physical, cascades over DERIVED_FROM edges, refuses Ring Zero and writes a content-free receipt whose hash nothing consults, and it does not write to the immune memory; bitemporal and scope_enforced are withheld. A read-only stdio MCP server exposes six tools that never write.

Memory Garden holds the family's strictest line on who may confirm a belief: only the person it is about. A retrospection agent over an Obsidian vault, written in Chinese: it extracts a stance from each paragraph, pairs an early and a later stance on the same topic as a candidate change, and lets only the person rule on it — confirmed, denied, or not their view. A denied pair is skipped on every later scan, a quote or an AI draft is filtered out of the person's positions by an authorship column, and the harness refuses an answer that skips the counter-evidence. The denial is keyed on atom ids hashed from the whole file's revision, though, so editing any line in either note reissues them and the denied pair can come back.

Membrane declares the epistemic vocabulary this family is built on and then enforces it with a number instead. A Go substrate storing six typed record classes in one Postgres and pgvector schema traces a revision status of active, contested or retracted written on four paths and filtered on none; what retrieval can actually see is the salience of zero that retraction and merge set alongside the status, filtered only among the roots and only when the caller passes a positive minimum, which both SDKs default to zero and the project's own lifecycle eval raises, asking for roots only, to make its retraction scenario come out right; graph neighbours are hydrated with no floor. Contesting does not touch salience at all, so a contested fact is returned exactly like an uncontested one. The same zero then collides with the lifecycle: the default deletion policy prunes and the default decay floor is zero, so the next hourly sweep hard-deletes the retracted record and the audit table's cascade takes its history with it — including the entry the prune path writes inside the deleting transaction. A retracted fact that survives long enough to be observed again is reinforced back above zero by the episodic consolidator's fixed gain, which matches on scope plus subject, predicate and object and never consults the status. Everything around that gap is careful: a byte budget computed in SQL before hydration and re-enforced in Go because "ListOptions projection fields are an optimization contract, not a trust boundary", derived records that inherit the maximum sensitivity of their sources under an in-transaction lock with unsafe backreferences pruned, and a contest path that deliberately makes a denied reference indistinguishable from a missing one to avoid an authorization oracle.

distill-kura puts its trust in what may be written rather than in what may be read. A standard-library Python memory with one Markdown store per agent mode: recall hands the whole index to a model that names what applies, after a deterministic tier that answers direct questions without one. Every write must carry quotes found verbatim in the transcript, classed by who said them — numbers only from tool output, the human's decisions only from the human's words — and a memory is retired only on one user quote naming both it and its successor. The retirement is a face on the index line, not a filter, so a superseded memory is still recalled.

ELIOT Memory OS splits how well founded a claim is from whether it is live, and lets only an operator move one out of candidacy. A pre-alpha MIT Rust control plane — 1,172,006 lines across nineteen crate groups with 6,142 test functions, and a README that says "Not ready for use" before anything else — whose memory unit is a claim card over SurrealDB with a redb control write-ahead log carrying pending writes, failed writes and dead letters. Two decisions survive the caveat. EpistemicStatus is Observed | Candidate | Supported | Verified | Contested | Superseded | Stale | Rejected | Unknown and sits beside a separate LifecycleStatus of Active | Dormant | Suppressed | Archived, so Superseded and Suppressed are different facts rather than one overloaded field — the split most of this corpus collapses. It is load-bearing: the store derives a confidence weight from it (Verified => 80, Supported => 50, Candidate => 10), the cognition surface counts a source as promoted only when the claim reads Verified, and the operator path refuses with "only an undispositioned candidate claim can be promoted". That promotion is the second decision — a person dispositions the candidate, and the payload records candidate_only: false, admitted_by_operator: true and the source write id and memory revision, after which a reciprocal verification re-reads the claim and bails unless the status, the write id, both flags and four cognitive-run identifiers all agree with the receipt: the approval is checked against its own record rather than believed. The architecture is held the same way — audit-architecture-boundaries.py reads the Cargo manifests against a declared policy and reports HARD_VIOLATION, TRACKED_DEBT or AUDIT_SIGNAL, where a debt entry is malformed unless it carries a positive issue number, a reason and a removal condition, so the exception category cannot become a silent allowlist; the script adds that "[a] clean result is static source evidence only. It is never runtime or Product Proof." Every SurrealDB table is SCHEMALESS, so every vocabulary above is enforced in Rust and nowhere else. One mark. human_review is withheld because eliot_operator_command, the command that approves, is also a declared MCP tool the agent can call.

inspeximus keeps a ledger of the values a key has retired, and refuses them when they come back. An MIT Python memory at version 2.35.0 — 129,806 lines, 294 test files, 2,837 test functions — built on one claim: "[y]our agent's most expensive failure is not forgetting. It is confidently remembering the old answer." On the write path it builds superseded_sigs, the set of object signatures already superseded for the incoming key, and when the incoming signature matches one and no active record carries it, the write is retired on arrival with superseded_by_policy = "echo_guard" and the current value is preserved — a durable record of a rejected value, keyed on the value, consulted on write, with reaffirm=True and revert() as the named bypasses and store.last_write telling the caller the write was demoted rather than landed. A companion objectless_guard blocks a write with no explicit object against a key whose values are ledgered, closing the path an echo would otherwise take. The comment above it is the most rigorous in this atlas: it cites its own probe with comparative stale rates against recency, mem0-v1, a bi-temporal-Graphiti-faithful policy and a verbatim-hash policy, then states its own defeat condition under "LOAD-BEARING LIMIT (measured, not assumed)" — paraphrase resistance "comes ONLY from the OBJECT being value-preserving", similarity cannot separate a same-value paraphrase (0.95) from a different-value correction (0.84) at "~42% false-block at a 0.9 threshold", and "an echo that OBSCURES the value (coreferent 'her old hobby') is NOT caught" — and records two shipped bugs in the same place: the guard defaulted off so "the adapters missed it for ten releases", and the documented off-switch was dead, with "all three of =0, =1 and unset produc[ing] an identical guarded store. A switch that reports nothing when it fails to take effect is worse than no switch." Its probes audit the project itself — forget_emits_tombstone_probe.py was found "by running the published wheel in a clean room". Four marks. Its README's comparison figures against named competitors are the project's own measurements at n=30 per system and were not reproduced here; the fourth column is its own guard disabled at 100%, which is the control that makes the other three legible.

pi-memory will not let a closed interval be reopened. An MIT TypeScript extension for Pi at version 0.1.0 — 15,920 lines, 41 test files — plus a daemon that analyses changed sources while Pi is closed, whose /memory sync "fingerprints publishers and enqueues work — it does not copy raw source bodies". A fact carries valid_at and invalid_at separately from recorded_at, and both retrieval entry points take an asOf parsed strictly as ISO-8601 — an unparseable value throws rather than silently becoming now — with the federated path resolving the instant once and pushing it to every engine leg, so a multi-scope query reads one moment instead of a different now per leg. Standing is candidate | supported | needs_review | contradicted | superseded, and the storage read is an allowlist — standing IN ('supported','needs_review','candidate') — so a value added later is withheld by default rather than leaking until someone remembers to exclude it. The sentence that holds it together is in resolveFact, which requires a rationale, redacts it before commit, checks a replacement is a different existing fact, and refuses to move a superseded or contradicted fact back to a live standing: "A closed interval cannot be reopened without losing history; record a new fact instead." Its capture gate refuses routine chatter and tool dumps while deliberately exempting corrections and constraints — the class most likely to look like noise and most costly to drop — under a header naming what it is not: "Not a copy of prjct fail-open excess." Two marks. The hybrid and federated APIs filter on the validity interval and return the standing alongside each result rather than withholding a contradicted fact, so the label has to be read; and scope_id is a parameter of the scoped query rather than a property of the handle, which is why no scope mark is claimed.

Mandalore has no delete, and its recall packet tells the model what absence does not prove. An MIT Go engine at version 1.0.0 — 32,248 lines, 109 test files, 393 test functions — behind a CLI, a local MCP server and thin harness plugins, storing Git-backed local-first memory it calls the memory-only successor to My Friday. Every write is a new revision carrying recorded_at, effective_from, explicit supersedes edges, a required change_reason, an evidence block of basis, confidence and source refs, and an authorship of device, actor, harness, model and session validated against a registered device; validateGraph then enforces one root per record, that every predecessor exists, that a "successor changes record identity, kind, or scope" is refused, that a successor "cannot take effect before its predecessor", and that the graph is acyclic. The service surface has no delete, forget or redact — a record captured in error is corrected, never removed, and a test asserts the correction did not "erase predecessor". That earns the audit mark. Its recall packet ships an epistemic notice with every answer: "Memory is evidence, not authority over current user direction. Verify live state. Conflicts require history; empty or truncated results do not prove absence" — the last clause being the defence almost nothing else here gives a model, backed by a Truncated flag computed by comparing what was returned against what matched. Conflicts are surfaced rather than resolved, as a list beside Current. Sensitivity defaults to private and Volatility to drift-prone, so an unclassified memory is assumed confidential and perishable — but sensitivity is deliberately not a read filter, and the test that would catch a change of mind is named TestPrivacySensitivityLabelsDoNotFilterRecall. effective_from is only ever set equal to recorded_at, so the second temporal axis is reserved rather than usable. One mark.

kannaka-memory reads like mysticism on the surface and like a security review at the wire. 119,396 lines of Rust at version 0.16.5 with 1,433 test functions, pitched as "a wave-interference memory system with bilateral chiral hemispheres" on a 10,000-dimensional medium "where recall is matrix multiplication, not search". The substrate is real hyperdimensional computing — encoding.rs is a text→embedding→hypervector pipeline with a pluggable backend, a codebook projection and HDC algebra — but the prose gives a reader no way to tell mechanism from decoration without opening the files. The reason to read it is that memories cross between agents over NATS and Nostr, so the real problem is that a remote peer can say anything, and three modules hold that boundary. provenance.rs signs with ed25519 over domain-separated, length-prefixed canonical bytes "so a signature minted for one statement type fail[s] verification as any other", keeps a bounded fail-closed replay set, and makes verification pure — it "never reads the clock; the caller passes now_ms so tests are deterministic". absorb_gate.rs is "the single write-side chokepoint every wire→store absorb path routes through", and its sanitisation runs "even when the gate is dormant", clamping the wave fields and — the mark — forcing hallucinated "to the local default, NEVER the wire value (an attacker must not be able to set/clear the immune flag over the wire)"; consolidation then filters flagged memories out of belief formation, so a node's verdict about a peer's claim is one the peer cannot write. serve_guard.rs names the exposure it closed — a node with a paid provider was "a public, unmetered endpoint for anyone on the bus" — derives the route from local config "and from nothing else" while logging the wire's ignored routing fields, caps hops so "two brainless nodes cannot bounce one question between them forever", and splits its rate limiter because "[a]n abuse control that a stranger can turn into an outage cheaper than the abuse is not a control". One mark. The licence is the bespoke SPACE CHILD LICENSE v1.0, whose "Peaceful Purpose" field-of-use restrictions make it not an open-source licence under the OSI definition.

OWASP Agent Memory Guard is the only subject here whose purpose is naming what goes wrong in somebody else's memory. An Apache-2.0 Python library at version 0.3.2 with 168 tests, wrapping a host's memory writes and running eleven detectors over them — injection, leakage, privilege escalation, tool abuse, excessive autonomy, ML injection, memory persistence injection, protected keys, cross-task contamination, anomaly and self-reinforcement — and emitting SIEM-shaped events. It holds no memories and carries no marks; it is here for the taxonomy. The self-reinforcement detector states a failure this atlas keeps finding and has never seen put so cleanly: "an agent reads its own prior agent_authored memory, mildly elaborates on it, writes it back, then reads the elaborated version on the next turn and elaborates again. Over a few iterations a hallucination or attacker-suggestion is reinforced into a durable 'fact' the agent now relies on." Its rules are a cool-down on consecutive agent-authored writes and a self-similarity rule under which a resembling write "is treated as reinforcement of the previous write, not independent corroboration" — with the decisive clause in the decay: only a separate external_tool or user_input write weakens the loop, because corroboration has to come from a different source class. Several systems in this corpus count an agent's own restatement as a second sighting; this is the module explaining why they should not. Where it stops is equally instructive: source_class is declared by the integrating code, the default is UNKNOWN, and the detector "[o]nly fires on writes whose source_class is AGENT_AUTHORED" — so a drop-in integration that never labels its writes gets none of the self-poisoning protection and no warning. The standalone scanner/rules.py is sixty-three lines of regex whose unprotected-write rule requires the guard call on the same line as the assignment, so it cannot establish what its SARIF output implies. RE-call found its own negative set inside the corpus it indexes, and measured the damage before fixing it. An Apache-2.0 Python retrieval and memory layer at version 0.13.0 — 102,183 lines over 253 modules with 494 test files, on the caller's own PostgreSQL with pgvector — pitched as "[m]emory that abstains instead of guessing", where every hit carries one of eleven verdicts and only ok ever becomes evidence. The passage to read is about its test data. The off-topic query pool, the subjects a search is supposed to abstain on, was written as Python literals, and this is a system people point at code corpora including its own: "[t]hese subjects are DATA, and as Python literals they were also CORPUS", so a corpus rooted at the repository ingested the list and disqualified every subject in it. The contamination was measured — none of twenty-five subjects surviving against a repository-rooted corpus, eleven of twenty-five against a third-party corpus of the same size, "so the failure was recall dogfooding itself, not the pool being too small" — the pool moved to a .json file the corpus globs do not match, and the distinctive words are now deliberately never written in prose, because naming one re-contaminates the pool. The author records breaking that rule twice while fixing it, once "caught only because the measured survivor count moved the wrong way". Around that sits docs/preregistrations/, 162 dated documents stating what was going to be measured before it was, with amendments and results filed separately, which is the habit the rest of the project's care descends from. It carries all seven capabilities; the two things to weigh against them are a development mode that serves results the trust gate never judged, stamped unverified so they cannot pass as judged ones, and a generation_promoted_unsafe_development audit event for a promotion that skipped validation.

Temvera publishes the attack that works against it, and commits a test that keeps the admission honest. An Apache-2.0 Python research artifact at version 0.0.1 — 11,702 lines over 93 files — sitting behind a PVLDB paper whose title states this page's own thesis, Temporal Fields Are Not Temporal Correctness: Measuring Bitemporal and Deletion Semantics in Deployed Agent Memory. It is both a harness that measures other systems and a reference substrate implementing what it argues for. run_bypass_probes returns four adversarial results, each carrying whether it activated and whether that was expected: cross-tenant replay, claim tampering and expired-signature replay must all fail, while compromised_trusted_signer succeeds and is flagged expected_limitation=True, since a policy anchored on a signer's key cannot survive that key being stolen. The test then asserts that exactly one probe activated and that it is the declared one, so a newly-working bypass breaks the build and deleting the admission breaks it too. The same habit runs through the paper artifact, where each printed figure is paired with a recomputation from a sealed run and the check "fails if either half moves". Its own gap is the one its subject matter makes conspicuous: erasure destroys a per-belief key and appends a receipt, and nothing on the ingest path reads those receipts back, so an erased value can return as a new belief.

Verimem argues that a tamper-evident chain must not remember what it deleted. An AGPL-3.0 Python memory at version 0.7.7 — 130,388 lines in the core package with 1,694 test files on SQLite — where every write passes an admission gate and "a claim the source openly contradicts does not come back as truth". Its mutation audit appends one row per destructive operation "INSIDE THE SAME TRANSACTION as the mutation itself", hash-chained, and record_mutation "never swallows" so a failure to record propagates instead of leaving an unrecorded deletion. The content rule is the part to read: the row carries the action and never the material, because "storing WHAT was deleted — even as a hash, brute-forceable on short text — inside an immutable chain makes GDPR Art.17 erasure a logical contradiction", so the chain proves "THAT/WHO/WHEN/WHICH-RECORD, not what the record said". Its status vocabulary withholds rather than ranks — the BM25 clause is status NOT IN ('orphaned', 'quarantined', 'user_belief') and every route back is a keyword a caller had to type. Scope is where it stops: multi-tenancy is a topic-string prefix whose LIKE narrow is assembled in the CLI rather than inside recall, so isolation holds on one path and depends on the caller everywhere else.

anatid turns the scope-predicate problem this page keeps reporting into a build failure. An MIT Python memory at version 0.4.3 — 36,813 lines over 44 files in one DuckDB file that SQL can read directly — whose visibility module explains itself in a sentence: DuckDB "has no AS OF SYSTEM TIME and no row-level access control", so tenant scoping and time travel are "predicates anatid compiles into every read, and a read that forgets one of them returns another tenant's rows or a row that was not visible at the requested instant." Every read obtains them from one class, and test_no_module_writes_the_visibility_predicate_by_hand parses each module's AST, pulls its string literals with docstrings excluded and f-strings handled, and fails when a SQL-shaped literal carries a hand-written tenant predicate — one test per module "so a failure names the file". Two smaller rules share the instinct. An accelerator "only ever narrows the candidate set", because "[a]n index built over current state cannot answer what was visible at an earlier instant", so a stale index costs recall and cannot leak past the guard. And the audit's counterpart id "goes in a COLUMN, never into reason", because a hard forget "has to be able to find and delete every audit row that names an erased memory, and it cannot search free text for it" — the same concern Verimem answers from the opposite side by keeping content out of its chain entirely. What anatid has no axis for is judgement: a memory is current or superseded, and nothing records whether anyone checked it.

HUQAN refuses to take the approver's identity from the request that asks for approval, which is the hole this page keeps finding under a review claim. An AGPL-3.0 JavaScript admission gate — 287,058 lines over 1,563 files, three binaries, no model and no API key — sitting between what an agent proposes and the state that would change, including a memory write. Across this corpus a system that claims human review usually stores an actor or approved_by string the calling code supplied, so what it has recorded is who the request said approved it. HUQAN's oversight runtime states the opposite rule and builds to it: "The runtime never accepts an approver identity from the decision body. The receiver/operator supplies an authenticated context and the injected identity resolver turns that context into a receiver-owned identity result." The resolver is required for the runtime to construct at all, separation of duties is checked on the resolved identity by both reference and hash, an override is authorised only when the policy allows it and the firewall actually returned block, and above a critical risk score prior approvers are pulled from the mutation journal into a set so one person cannot satisfy a two-approver rule twice. "Missing or ambiguous identity, stale state, scope drift, unavailable durability, and firewall disagreement all fail closed." Its own limits are stated as plainly: escalation "requires a second approver, so it is simply absent in a single-user install", the self-approval and override rules are policy flags, and the graph behind the gate is far thinner than the gate in front of it.

yantrik-mind deleted its audit's exemption for the trusted caller, and wrote down why. A Rust companion of 203,205 lines across nineteen crates, with no licence file, delegating its belief store to a published YantrikDB pin and contributing a purpose gate of its own. Operator reads once sat outside the read ledger on "the trusted owner path"; the exemption is gone because "the operator's background lanes (dream/proactive/research/…) are exactly the cross-subject reads a purpose audit exists to catch, so a ledger blind to them would be theater." Every receipt now names who read, through which facade method, for what declared purpose, how many results crossed the boundary and how many the gate suppressed, hash-chained so an edit or reorder breaks every later value — a record of reads rather than of mutations, which is why it earns no audit mark here and is the half of that pattern most systems do worse. Its scope default is the other thing to take: Shared or Private(owner) on the belief, with legacy untagged memory made private to the primary member "so pre-multi-user facts never leak to a later-added member", which is the migration decision taken in the safe direction. The gap is that the thirty-line argument for pinning the engine to a published crate — because a path dep "built the mind against whatever that tree happened to contain" — was not applied to the three sibling crates still carried as path dependencies directly below it.

FAVA Trails puts the authority in the process and lets the tool argument choose only a view. An Apache-2.0 MCP server of 14,496 lines of Python against 19,148 lines of tests — 994 test functions in 41 files — keeping thoughts as markdown files with YAML frontmatter inside a Jujutsu repository, where agents never see a version-control command because "raw jj stdout is NEVER returned to agents". A thought carries a six-value validation_status and the default read admits exactly one of them, so a draft or a rejected thought is absent rather than ranked down. The part worth copying is one line of the governance module's docstring — "Tool arguments select a view; they never establish a caller's authority" — implemented by building the principal from FAVA_TRAILS_AGENT_ID and FAVA_TRAILS_OPERATOR in the server process's environment, so an agent_id a caller supplies is a filter and never a claim; the authoring view raises PermissionError when the process has no configured identity rather than quietly narrowing to the default, which is the failure mode that returns a plausible smaller answer. Supersession is gated the same way round: is_effectively_superseded retires a record only when its successor is approved, so a draft correction cannot blank the current answer while it waits. Four marks. The limit it states about itself twice, unprompted, is that a shared endpoint is one identity boundary — and the default reviewer is an LLM one-shot, recorded as llm_advisory beside the human kind an operator-only path produces, so a trail can be entirely model-approved, correctly labelled.

MIND-Mem lets the door decide what a write may become, and makes the store refuse a write that has no door. An Apache-2.0 Python memory of 166,989 lines keeping typed blocks in Markdown with an FTS5 index. Each ingest source is an IngestTier; a table maps it to the status it may mint, so imports and agent messages land quarantined, capture lands pending, and only an approved proposal lands active. write_block refuses without an admission receipt, recall filters withheld statuses on every leg before fusion, and every admission is hash-chained. The gate is Python and the store is Markdown: a block typed into DECISIONS.md by an editor or an agent's file tool is served. Five marks.

aimee is a personal-assistant runtime whose C services hand every memory decision to one Go module, and it makes trust a function of who wrote a fact. Authority comes from the verified caller, never from text. A fact asserted below system rank lands as a candidate that no read serves until an operator approves it on the KB console, a verb the agent's MCP tools do not carry. Every retraction and rejection writes a value-keyed refusal that the Go writers consult and a trigger repeats, fact refusals on one database-generated identity, and memory mutations reach a hash-chained SQLite store from a trigger in the same transaction. All seven marks. The near misses sit beside them: a rank-20 system assertion also clears a candidate, and the runtime role that cannot delete a refusal can lift one with an unaudited UPDATE. A native-delivery route hands personal recall rows and their revisions to a vLLM plugin that is not published.

emem (Vortx AI) makes authorship and time checkable and leaves truth to the reader. A Rust MCP and REST server, it holds ed25519-signed Markdown notes written with the memory-tool verbs, and a geospatial fact plane whose addresses only admitted keys may occupy; everyone else registers derivations that default reads never see. Attestations and note edits land in a Merkle log with RFC 6962 proofs, and fact recall bounds observation and record time separately, walking history for the latter. The weak side is the note plane: reads are public, delete leaves bytes readable by content id, namespaces compare eight key characters, and the search index attributes every note to the server's key, so its attester filter matches no agent.

Cortex (Obelyth) verifies the answer rather than the memory. It keeps one person's notes as Markdown in a private GitHub repository behind an MCP server, and every brain_ask reply carries a reader model's quote checked against the file bytes at the served commit, stamped VERIFIED, CORRECTED, SUPERSEDED or UNVERIFIED. Retraction is a house text convention, a SUPERSEDED banner or a (was: "…") correction, read at answer time. Guest clients get a separate handler that lists only ask and propose, and their notes wait in a review queue. The weak side is the trusted door: it writes without review, nothing deletes, and a refutation without the house words still verifies.

Recollect keys a rejection on the value and enforces it on the evidence as well as the claim. A self-hosted Rust server over PostgreSQL row-level security, it stores claims as insert-only revisions with review state, fact and knowledge time and cited evidence spans, reached through a per-Brain MCP endpoint. A browser rejection or correction writes a rule on the normalised subject, predicate and value; every writer stamps a match blocked_by_rule, and recall withholds raw source chunks that repeat the value. What is weak is the default: browser-created Brains learn from captured sessions under an autonomous policy that accepts model output without a person, default recall serves unreviewed claims, and erasure lifts the rule. Seven marks.

Minta is the family's case of a trust layer described in full and wired in part. A local FastAPI hub stores typed context objects in SQLite and Chroma, packs seven text slots into a context block, and routes captured corrections through an inbox. Its search re-reads every vector hit from SQL under an owner and status predicate, so stale and archived rows stay out by default. What fills those states is missing: resolve_and_persist, the only writer of stale, has no caller, the bitemporal check has none, the scheduled scan is added paused, and a confirmed scan finding becomes a new note instead of acting on its target. The agent holds the inbox's confirm verb.

GMEOW treats every memory as a claim and retires one by appending, never by erasing. An RDF ontology engine, its MCP server stores reified claims in one append-only GTS file, gates each write through a Transaction-Logic verdict, and records a tool call beside every committed write. revise_belief appends a suppression frame that every default recall honours, with an explicit include_suppressed view, and one test pins that across five recall shapes with a control. The store is far thinner than the ontology: no standpoint, evidence or valid time reaches it, the gate restates checks the handler already made, recall is whitespace-token overlap, and the claim file takes no lock although its engine requires a single writer.

MGI-Mind is the family's duel rule, and on the default path the duel is decided by age. A local Rust MCP server over Qdrant: memories pass a relevance gate into a recoverable quarantine, and a fact triple on a single-valued predicate must win a duel against the visible rival, which leaves the loser stale or superseded. One function generates both the Rust visibility rule and the Qdrant filter, and a cross-process lock holds the duel. But entrenchment's dependants term is written only by a migration command, so any rival younger than about nine months flips, re-adding the old value rebuilds its row and flips it back, and the inheritance discount has no producer.

Pluribus quarantines a poisoned lesson out of recall and serves its source text through the same recall tool. A Go and Postgres control plane where every coding agent shares one global pool of constraints, decisions, failures and patterns, with no model on the server. A deterministic harm screen stores safety-negating advice as quarantined, recall drops quarantined, deleted and rejected rows in SQL and again after its lanes merge, and only a different agent's use raises authority. The perimeter is weak. recall_context's Activity lane reads the advisory rows behind quarantined and deleted memories unfiltered, tools/call runs tools the tier hides, and the votes that release quarantine count self-declared agent_id strings, one vote sufficing in the shipped config.

Research lineage

generative-agents, voyager, hipporag, a-mem, memoryos, nooa-memory, second-me, simplemem, livingfeed, mnemopi, aeris, sesa, pro-long, arc-code, memharness, tycho, retrodict, polyphony-arc, merchantbench, dovsg, lightmem, context-infrastructure, t-mem, mini-agi, selmem, rrsi, agent-memory-distillation, farm, embodied-lgr, zenbrain

Context Infrastructure is the family's clearest case of consolidation that deletes its own evidence. 159 commits from three authors, 10,646 lines of Markdown against 3,496 of Python — the documentation is the system and the code is its trigger. Its author publishes it as the structure of a setup they say has run for a year, and the README refuses the product framing outright: "这不是 开箱即用的工具,而是一个可以参考的蓝图" — not an out-of-the-box tool, a blueprint.

The ladder is three layers. A daily observer hands a prompt to an agent, which scans the workspace and appends a dated block to one Markdown file under three marks — 🔴 kept permanently and eligible for promotion, 🟡 good for weeks, 🟢 garbage-collected. A weekly reflector promotes the durable entries into rule files by responsibility boundary, against a threshold the prompt actually states: "跨项目通用 + 多次验证 + 有明确适用场景", general across projects, verified more than once, with a clear applicable scenario. Then it rewrites the observation file without what it promoted. A promoted axiom's frontmatter carries id, category, created and updated — and no pointer back. So the system compounds beliefs upward with no way to audit downward, and a rule promoted from a misreading is indistinguishable from one promoted from a year of experience.

Three things are worth taking whatever else you build. The idempotency rule sits ahead of the task in the prompt that performs the write — read the file, and if today's block exists, change nothing. The prompt tells the agent to append with >> or tee -a rather than edit a large file whole. And the memory file carries its own retrieval instruction in its header: "不要全文加载这个文件", do not load this whole, retrieve on demand. Each is one line, and each is the kind of thing that only appears in a system somebody actually ran into trouble with.

Two caveats a reader needs. Both trigger scripts ship with /path/to/your/workspace and <your-model-id>, so nothing here runs as committed — consistent with the blueprint framing and worth knowing before cloning. And there is no licence file at all, which leaves a repository whose stated purpose is to be copied all-rights-reserved by default.

LightMem is the family's clearest case of a paper's mechanism arriving intact and its epistemics arriving not at all. MIT, 306 commits from twenty-three authors, 98,482 lines of Python across three sibling packages — the reference implementation of arXiv:2510.18866 (ICLR 2026, submitted 21 October 2025). The paper's three Atkinson-Shiffrin stages map directly onto the code: an LLMLingua-2 compressor and topic segmenter filter a sensory buffer, a short-term stage summarises each topic group, and a sleep-time update consolidates offline — "an offline procedure that decouples consolidation from online inference." The online write path contains no consolidation by construction, which is what makes the latency claim structural rather than a scheduling promise.

Two things are worth taking. The payload keeps original_memory and compressed_memory beside the stored memory, so a system whose thesis is aggressive discarding lets a reader see what it discarded. And token_monitor ships the accounting inside the library, so an adopter can reproduce the efficiency argument on their own traffic rather than trusting a table.

It carries no capability marks, and the absences are specific rather than general. There is no status and no confidence — consolidated is a flag the background scan reads to find work. There is no user, session or tenant key anywhere, so one instance is one undifferentiated pool. And the offline update is the part to read before adopting: delete hard-deletes the Qdrant point and update overwrites payload["memory"] in place, so the pass carrying the most judgement — deciding two memories say the same thing — is the one that leaves no evidence. The paper's numbers are large and the harnesses for LoCoMo and LongMemEval are committed; no result file for either is in the tree, which is the more reproducible half of that trade.

MemHarness is the family's answer to a failure the rest of this atlas records and rarely names: retrieval that makes the agent worse. Its paper (arXiv:2607.28272, 30 July 2026) argues that treating a retrieved experience as a static record to replay "regardless of whether they align with the agent's current situation" causes negative transfer, and the design follows from that. Every record in its Milvus bank stores the state_text it was distilled from beside the lesson itself, so at each step the policy can compare the memory's original situation with the present one and either rewrite the lesson into state-specific guidance or reject it and reason unaided — a judgement trained end-to-end with GRPO rather than prompted. Two mechanisms lift out of the trainer cleanly. Its ranking prior is a measurement: (succ + 1) / (use + 2) over the episodes that retrieved a record, so an unused memory sits at 0.5 and eviction below 0.35 waits for at least three uses — the answer to a complaint this atlas makes of store after store, where importance is asserted at write time and never moves. And it deduplicates at both ends, probing the neighbourhood before insert and dropping near-duplicate hits after retrieval. Against that, the subsystem is 6,044 lines with no test of its own, and its task_name scope key is applied to the dedupe probe and the random sampler but not to the retrieval that reaches the agent, leaving task isolation to a collection name — see MemHarness.

Artifacts the practical systems are largely responses to. Generative Agents established the observation/reflection/planning stream and the importance-recency-relevance score — whose weights, read at the source, are hand-tuned constants with two abandoned settings left in comments. Voyager established procedural skill memory with an execution-verified write gate. HippoRAG established diffusion-based associative retrieval. Second Me is the only system here whose memory ends up in weights: documents become a versioned biography, the biography becomes synthesized training data, and LoRA fine-tuning plus DPO produce a local model that answers without retrieving anything. The known limitations say what a single instance is and is not evidence of. NOOA Memory implements the cognitive models the others approximate — ACT-R base-level activation with spreading activation for retrieval, the Ebbinghaus curve for forgetting — and stores the score components of every retrieval on the memory that was retrieved.

PRO-LONG is the family's cheapest memory and its best-evidenced one: the entire store is one append-only text log the coding agent greps, and the repository commits the arms that remove it — a matched run with the log (--log-window full) at 50.2% mean over the 25 ARC-AGI-3 public games against 24.7% with the log replaced by a board pasted into the prompt. The arms differed in a second variable, an action budget of 1,000 against 500, so a third file re-scores the log run at the 500-action cutoff and reports 45.6% — a budget-matched comparison the authors published against their own headline. Its paper (arXiv:2607.20064, submitted 22 July 2026) argues the tradeoff directly: "preserving more information makes retrieving relevant details less tractable", and answers it by keeping everything and making a coding agent pay the search cost.

arc-code is the same memory shape on the same benchmark with the opposite guarantee, and reading the two together is what makes either legible. Both give a coding agent an append-only log plus a notes.md that survives context compaction. PRO-LONG's log reaches the agent as a copy the agent can write to, and the harness finds its next byte offset by measuring that copy. arc-code moved the actuator out of the sandbox entirely — the broker holds the game key, plays every action and writes the log, so the agent has no path to the game that bypasses the record — and it did so because one run proved the point, with an agent that built its own HTTP client and played an action that never appeared in the log. The lesson generalises past ARC: an append-only memory is complete only if the recorder is the only thing that can cause the effect. arc-code also ships the corpus's most useful failure study, 191 sessions with the 14 non-wins analysed, whose central finding is a memory failure — five of the six sessions that gave up early had already written down the open question that would have unblocked them.

Three ARC-AGI-3 harnesses read together say the same thing about compacted memory, and only one of them says it in code. Each keeps a complete raw record and a lossy summary over it, and each declares the summary subordinate: Retrodict's prompt says "the raw log is the ground truth", Polyphony ARC heads its file listing "On-disk files (authoritative; re-read before trusting memory):", and Tycho enforces the same precedence at the snapshot boundary rather than in prose — _is_harness_evidence_path keeps the harness's own turn records out of the manifest, so restoring an earlier world model rolls back the conclusion and leaves the evidence it was drawn from. That is the difference the capability marks are measuring across this whole atlas, on three systems built for one benchmark within a month of each other. Retrodict states the sharpest version of the rule — mark each point checked against the log or still assumed, and do not build multi-step plans on the second — and reads it nowhere; Tycho states nothing about belief at all and is the only one whose memory boundary can fail a test.

MerchantBench is the family's inversion of that arrangement, and the only benchmark here that prices a memory failure in money. The three ARC harnesses keep the raw log and subordinate the summary to it. MerchantBench deletes the raw log on purpose: its reference baseline compacts at 160,000 estimated tokens down to 30,000, and the only thing that crosses the boundary is one Markdown document the agent chose to write. Two details make it worth reading beside them. The warning is advisory — a single user message saying "Call write_memory_doc now if important details should be kept", followed by an unconditional truncation that never checks whether the call was made — and nothing re-injects the document afterwards, so an agent recovers its own notes only by deciding to. Its 366 simulated days are long enough for that to be measurable rather than hypothetical, and the paper (arXiv:2607.28956, 31 July 2026) reports two runs where a wrong belief persisted for hundreds of days: a shelf contracting from 47 active listings on Day 54 to three by Day 322, and an agent that misremembered its own deadline as Day 285 and stopped restocking with 83 days left. The comparison the repository sets up and does not make is between its agents and its humans: _human_playground_script.html puts read_memory_doc in the browser client's AUTO_TOOLS bootstrap and renders it into a panel on screen at every activation, so the human participants — who finished at 3.7× the best LLM configuration — never had to remember to look. And the memory ablation is two commented lines away in env/scenarios/default.yaml and is not run, so this benchmark has never priced the mechanism it ships. MemoryOS is the tiered short/mid/long architecture in its most legible form, with the promotion rule written down as alpha * N_visit + beta * L_interaction + gamma * R_recency — and the coefficients left at 1, 1 and 1 with no ablation in the code or in its paper (arXiv:2506.06326, which states they "are equality set to 1" and ablates modules instead), so verbosity scores like importance. Aeris is the family's outlier and the clearest case in this atlas of a memory model whose only writer is its test suite. It is a deterministic ECS simulation whose agents hold memories that carry no text at all — type, category, importance, certainty, emotional weight, the entity involved, a forgotten flag — and beliefs that declare what most systems here express as a float: a five-value status enum (Active | Weakening | Revised | Abandoned | Contradicted) beside a provenance enum running from DirectObservation to Assumed, and two ids naming the memory that supports the belief and the memory that contradicts it. Grep the tree for the four non-Active statuses and the enum declaration is the only hit. AddMemory and AddBelief have no caller in src/, so the decay pass, the consolidation pass, the retrieval pass and the model projection are all written against a store a running simulation never fills — and 9,834 lines of xUnit over 6,472 lines of engine pass anyway, because the tests construct the state the engine does not. That is the failure mode a green build cannot show you, and the cheapest guard against it is one assertion that a full tick loop leaves the store non-empty. The mechanism worth taking is at the boundary rather than in the store. SemanticValidator refuses the projection assembled for a language model if it contains any of eighteen engine identifiers — EntityId, Arch., MemoryStore, BeliefData — and committed tests assert on the serialized payload that none of them appears. Nothing else in this atlas checks that the model is being handed facts about the world rather than the engine's own vocabulary. See Aeris.

LivingFeed answers the criticism this atlas makes of the family's founder. Generative Agents ships gw = [0.5, 3, 2] as hand-tuned constants with no committed ablation, and MemoryOS leaves its promotion coefficients at 1, 1 and 1 with nothing measuring them. LivingFeed composes importance as 0.35·emotion + 0.30·relationship + 0.20·goal + 0.15·rarity and then stores all four components on the memory, in a required factors object its schema describes as the material for coefficient tuning by offline replay. Storing a composite score's parts rather than only its total is the cheapest answer in the corpus to the hand-tuned-weights problem, and it costs four floats.

The far end of that axis is a paper with no artifact, and it is worth naming because it reframes the problem rather than answering it. EvoHarness-RL: Learning Self-Evolving Runtime Harness for Long-Horizon LLM Agents (arXiv:2608.05446, Ning et al., 5 August 2026, accepted to LLA@COLM 2026) does not tune the constants — it trains the policy over the memory. Supervised fine-tuning teaches the agent a harness action space, and cost-aware GRPO then learns when to read, update and consolidate external state during a long-horizon task. Every system in this report decides those three things with hand-written thresholds, timers and heuristics; this proposes learning them, and its closing sentence is aimed squarely at the corpus: long-horizon agents benefit from trainable policies for constructing and coordinating with external workspaces "beyond simply adding stronger tools or larger memories."

Two of its reported dynamics are the interesting part for a builder. Harness annealing — training "internalizes recurring harness-use patterns into the model policy and shifts the agent from frequent harness calls toward selective external-state access" — is the opposite direction from the corpus, where the usual response to a recall failure is to retrieve more. And harness evolution, where progress updates and experience consolidation "refine the harness into a compact, task-adaptive state substrate", is consolidation judged by whether it helped the task rather than by a compression ratio. It reports 96.9% on ALFWorld with a Qwen3-8B model.

Three caveats belong with it, and the third is this atlas's standing one. Its state taxonomy — Belief, Progress, Experience — cuts across the boundary this report draws: belief is a claim that can turn out false, progress is a run record, and experience is procedural. The abstract names the three and does not define what each holds, so what is quoted here is the abstract and the listing metadata rather than a reading of the method. And no repository, dataset or benchmark URL appears, so on this atlas's terms it is a research direction rather than a measurement — the same standing recorded for MemEvoBench and FiFA on the benchmarks page. The idea is the most direct challenge in the literature to how every system here decides when to write and when to recall, and there is nothing to read.

Its second rule is the second divergence stated as schema policy: "forgetting happens only in Semantic; the original is not erased". Semantic points carry a decay_at computed from importance — one day at zero, thirty at one — and recall filters on it, so expiry needs no sweeper and the episodic event that produced the memory is permanent. Provenance is mandatory in the same direction: source_event_ids is minItems: 1, with the rule cited inline as "any memory is audit-traceable". The documentation and comments are in Korean, so the terms here are translations, as with GenericAgent. The failure worth naming is at the boundary: recall catches every exception, logs "recall failed (bypassing with empty recall)", and returns an empty list — so an unreachable index produces an amnesiac actor and a quiet world looks identical to a broken one.

SimpleMem is the family's most useful single idea and its sharpest warning. Its MemoryEntry carries a lossless_restatement built by two declared transforms — Φ_coref, resolving every pronoun, and Φ_time, absolutising every timestamp — so a stored unit is legible with no surrounding turn. That is context-independence bought once at write instead of reconstructed at every read, it is a prompt and a schema rather than an architecture, and it is portable into any extractor in this atlas. The warning is what surrounds it. The store holding those units offers add_entries, three searches and clear(): no delete, no update, and no scope key to delete by, so the pillar the papers and the packaged Claude skill are about cannot remove one memory. Its governance apparatus — scope_id on every read, an append-only memory_events log recording seven mutation kinds, a scope_access principal table — lives in EvolveMem, the newest and least tested of the repository's three pillars, and does not touch the benchmarked one. It also states its own inversion of the corpus: MemoryEntry.timestamp is when the described event happened, and nothing records when the system learned it, so SimpleMem has the validity clock almost everything here lacks and lacks the record clock almost everything here has.

SESA is Voyager's write gate inverted, and it answers the family's oldest open question. Voyager writes a skill only when a critic confirms the episode succeeded; SESA writes one only from a failure, distilling each losing rollout into a card naming the confusion and the distinction that resolves it. The pair is the clearest statement in this atlas of the trade — verified-success writes are trustworthy and say nothing about what went wrong, failure writes are corrective and unverified — and both are defensible.

What SESA adds is the piece the pattern page has been asking for. Every card carries retrieved_count, helpful_count and hurt_count, all three written by the same rollout reward that trains the model, and a card whose net score has gone negative after at least three retrievals is deleted. Nothing else here has a negative usefulness signal wired to eviction rather than to ranking; the skill libraries in this corpus grow and are pruned, if at all, by age or by hand. Its paper (arXiv:2607.29468, 31 July 2026) is worth reading beside the code, for something this atlas rarely gets. It describes the memory mechanism exactly as implemented — the 0.93 dedup threshold, the 800-entry cap, the top-three E5 retrieval, the evict-after-three-retrievals rule — and then measures it: removing failure distillation costs 2.7 points of seven-benchmark average, the largest of three ablated components, and the abstract splits the memory's value between a solver deployed without retrieval, which keeps 1.8–2.2 points over the baseline, and the bank re-enabled at inference, which adds 0.5–1.0 more. That is a direct measurement of how much of an external memory's benefit ends up in the weights, which this atlas has otherwise only seen argued at Second Me. The gap between paper and artifact is one item long and specific: the paper initializes the bank with 15 hand-written skills and 142 mined during a bootstrap, and neither the seed file nor the warm-start bank exists in the repository, so a run started from the checkout begins empty.

The two failures beside it are as instructive as the mechanism. Eviction leaves nothing behind and the duplicate check compares only against the live bank, so the next similar failure regenerates the card the system just measured as harmful, starting again at zero. And the anti-leakage control is written and never called: retrieve() accepts exclude_uids, every generated card stores the source_uid it came from, and no caller in the repository passes either. See SESA.

Tradeoff: the ideas are unusually legible because no production concern obscures them, and none of these has scope, correction, deletion, or a trust model. Voyager and Generative Agents have been frozen since 2023; read them for design, not adoption.

DovSG is the lineage's robot, and its finding is the gap between the memory a paper evaluates and the memory its code consumes. The RA-L 2025 code keeps an open-vocabulary 3D scene graph over object instances and repairs it locally after every pick and place: remembered voxels the new depth contradicts are deleted, an object that loses more than half its voxels is dropped, its node and children are cut from the graph and the builder adds what is missing. The deletion test is the part to lift — two thresholds on depth disagreement, conservative and local. What the graph is for is the question the tree answers plainly: the planner sends GPT-4o-mini the instruction and five examples with the graph argument commented out, navigation resolves A on B by CLIP similarity and the nearest pair of centroids, and the only reader of a node's relations outside the module that builds them is the visualiser. A survivor keeps whatever parent it had, a re-detected object gets a new identity, nothing records a removal, and there are no tests and no licence file.

SelMem forgets on purpose, and its forgetting ends by erasing its own record. A Rust memory organ for LLM entities with no Cargo dependencies and a hand-written binding to the system libsqlite3, built on the reconstructive-memory literature its PARAMETERS.md grades constant by constant: each trace holds a gist the narrator rebuilds and lets drift, a core set at encode for grounding to pull it back toward, and a verbatim archive record the narrator is never handed. A nightly sleep weathers, rewrites, merges and promotes recurring material up a motif, belief and trait ladder, and a decayed episode goes Latent: its scene leaves recall while its affect keeps biasing what is encoded next. What is spent after that is released, and release_trace takes the trace's per-reconstruction drift log with it, so audit_log is withheld on the one log it keeps. One mark, negative_eval: the archive test proves a planted verbatim sentinel is in the store before and after grounding and absent from every recall. The reusable part is at the model boundary — a proposed core may drop words and never add one, and a segmentation must be contiguous excerpts of the source.

RRSI remembers only what survives a gate, and the survivor is a program. The code for a paper on evolving agent harnesses: a proposer agent edits prompts, control flow, tools, skills and subagents in git worktrees, a critic screens each diff for task-specific content, and a noise floor and a cost rule decide which candidate fast-forwards evolve/<domain>, the branch the next run's baseline starts from. The admission policy is the notable part: gains inside the measured noise band are refused unless cheaper or structurally new, and each edit's component tag must be evidenced by its diff. The weakness is lifetime: the ledger justifying the program lasts one run, the refusal to retry a falsified idea is prompt text, and the harnesses' own Memory store is never wired.

Agent Memory Distillation splits procedural memory by when the student needs it, and wires an evaluation in which a task retrieves its own record. Paper code for arXiv:2608.07169: a teacher's benchmark trajectories become workflow insights and subtask examples injected before the first turn, and per-function call records fetched only when a tool call errors. The store is JSON beside NumPy matrices, built offline and frozen at inference. The weakness is the loop around it: memory is built from the tasks the student is scored on, keyed on the task text, with no self-exclusion in the tree, and the paper's own disjoint runs put BFCL at 31.00 against 38.50. The AppWorld method itself is absent.

FARM is the lineage's second robot, and its correction layer is the part the paper does not describe. The code for FARM: Find Anything using Relational Spatial Memory builds an object memory from posed RGB-D in real time — one 3D Gaussian per object, fused by Hellinger distance and union-find, captioned and embedded by local VLMs — and answers relational queries with closed-form predicates computed at query time. The state is one scene_state.pt the mapping node reloads and locks whole. A Viser UI edits, locks, adds and deletes objects, but every exit from the memory clears one active bit that association skips, so a deleted object returns on the next sighting. No test touches persistence or deletion.

EmbodiedLGR is a robot's object graph that lives and dies with one ROS node. The code for arXiv:2604.18271: Florence-2 labels each frame, and a label within 5 metres of the robot and above 0.75 cosine to a known node refreshes it, otherwise it becomes a node at the robot's pose. Three services answer by label, position and time, and a ReMEmbR caption store in Milvus is the prompted fallback. The place-gated deduplication is the idea to lift. The graph has no edges, although the paper says nodes are linked; it is never written to disk, the caption store is dropped at every builder start, and no test reads either. No capability mark.

ZenBrain ships its neuroscience as a library and its memory as four SQL tables that call little of it. An Apache-2.0 TypeScript monorepo backing arXiv:2604.23878: a dependency-free library of FSRS, Hebbian, Ebbinghaus, sleep-replay and emotion functions, and a MemoryCoordinator served over MCP and a Vercel AI SDK middleware. The coordinator routes text by regex, emotion lexicon and a caller confidence into facts, episodes, procedures or pinned core blocks, and imports four of the twenty modules; no stored row decays. The lexical fallback is careful and says when nothing matched. No shipped surface can delete a memory, consolidate() copies the same episodes on every call, and the paper's ablation runs on a simulation of hand-set multipliers.

The category almost nothing models: prospective memory

NOOA Memory carries two memory types almost no other system in this atlas has: intent — "prospective: trigger-based reminder (when X…)" — and todo, "prospective: durable commitment with an open/done" lifecycle. Nearly everything else here remembers what was; these remember what the agent has undertaken to do.

A third occupant arrives from the other direction, and it is the one that completes the category. Memento does not remember an intention; it makes content unreachable until a date. An entry can be recorded with status = 'sealed' and a deliver_on date, and a sealed entry is outside transcription, outside the full-text index and outside the timeline — every read path in the system keys off later statuses, so the memory genuinely cannot be retrieved. A worker pass then runs UPDATE entries SET status = 'uploaded' WHERE status = 'sealed' AND deliver_on <= current_date, and the entry enters the normal pipeline as if it had just been recorded.

Set beside the other two, that completes a shape worth naming. NOOA and MineContext remember that something is to be done later; Memento holds something to be known later. And its enforcement is the stronger kind: not a WHERE deliver_on <= now() predicate every query must remember, but a state outside the pipeline, so an entry has no segments and no index row to leak through in the first place.

The second occupant differs in the way that matters. MineContext has a todo table — content, start_time, end_time as a deadline, urgency, assignee, reason, and a status integer with exactly two values, stamped with an end_time on completion by update_todo_status. It also has an INTENT_CONTEXT type for "future plans, goal setting, and action intentions", and its ContextProperties model documents event_time as "event occurrence time, can be future" — which is the cheapest route to prospective memory anyone here has found, since a system that already separates event time from record time is most of the way there.

But NOOA's commitments are declared and MineContext's are inferred. Its SmartTodoManager reads recent activity, pulls the relevant contexts, checks which historical todos were completed, and asks a model to extract tasks with due dates and priorities from what it watched the user do. It remembers commitments the user never made — and, per its report, offers no surface on which to reject one.

Gobii is a third occupant and it satisfies a different two of the three requirements below. Its PersistentAgentKanbanCard is a durable commitment with an enforced todo/doing/done lifecycle and a completed_at — the strict lifecycle NOOA has and MineContext approximates with a two-valued integer — but its triggers are cron (PersistentAgentCronTrigger, PersistentAgentSchedule) rather than semantic, and nothing can reject a commitment such that it cannot be recreated. Its SQLite mirror of the board, __kanban_cards, is one of the eight tables dropped before persistence, so the durable copy lives in Postgres and the agent sees a per-cycle projection of it.

Mnemopi is a fourth arrangement and satisfies none of the three. It has the vocabulary — COMMITMENT and GOAL are two of its fourteen first-class memory types — and gives them a decay curve instead of a lifecycle: commitment: { k: 1.0, eta: 240.0 }, and a Weibull with k=1 is exactly an exponential, so a commitment's survival is memoryless and it is gone in about ten days whether or not it was ever discharged. An obligation is the one memory type where the correct behaviour is to persist undiminished until it is met and then stop, which is a state machine and not a half-life.

Counterparts takes the retrieval side of the boundary dispute below on purpose. A dated memory's arriving window is not a scheduled fire: "Arrival is a cue, not a command. There is no bypass lane" (src/core/prospective/CONTRACT.md:20), so the calendar reaching the date enters the same activation, gate and tier competition as a word in the user's turn (src/core/counterpart.ts:1998-2013). Its stated rule is "Hold debts, lose deadlines" (CONTRACT.md:13): a memory that surfaces at its first retrievable moment is a task queue, not memory. Of the three requirements below it meets none outright. The trigger is a date, not a meaning. Prospectivity is derived from the event date and never stored, so there is no open or done state (CONTRACT.md:40-43). Archival keeps a row from arriving, but it is keyed on the row. The one bypass lane is a remind: "plain" item the author asked to be told outright on its day (CONTRACT.md:25).

Four occupants bracket the design question rather than settling it. A declared commitment is a memory; an inferred commitment is a claim about someone's intentions, which is a stronger claim than any preference in this atlas and the one most costly to get wrong.

Why the category is nearly empty is a boundary dispute, not an oversight. Ordinary software already has somewhere for future commitments to live — a scheduler, a job queue, a state machine — and on that division memory is the passive store and the queue is what acts. The reason that division does not survive contact with an agent is the trigger. "At 09:00 tomorrow" belongs in cron. "The next time we discuss project scope" does not, and cannot: matching it requires the incoming turn, the stored commitment, and something able to judge that the two are about the same thing. That is a retrieval operation, so the commitment has to sit where retrieval can reach it. What the field has built instead is retrieval tuned entirely for what was, which is why the two systems that got here arrived by extending a memory schema rather than by adding a scheduler.

The gap is also visible from the other side, in systems that hold a future without committing to it. ai-memory's handoff is a typed record of unfinished work addressed from one harness to another, carrying open questions rather than conclusions — genuinely forward-looking, and a snapshot of an interruption rather than a durable obligation with a trigger and a lifecycle. The distance between those two things is the whole category.

Three requirements follow from the two implementations, and no system here has all three: a semantic trigger that retrieval can evaluate rather than a timestamp a scheduler can fire; an enforced lifecycle on the commitment, so open, done and abandoned are distinguishable states rather than a derived guess; and — for anything that infers commitments — a way to reject one, keyed on the commitment, so a hallucinated obligation the user disowns cannot be re-extracted from the same transcript on the next pass. MineContext infers and has no rejection surface, which is the combination this atlas would flag anywhere else in memory and which matters more here: a wrong preference bends an answer, and a wrong obligation makes the agent act.

The nearest other neighbour is ai-memory's handoff with its next_steps list, and that is a record of an interrupted task rather than a trigger. Whether prospective memory belongs in a memory layer or in a scheduler is a real question — but it is being answered by omission nearly everywhere, and an agent that cannot remember its own commitments will keep rediscovering them.

The category that competes on control, not accuracy

Six systems here — SillyTavern, RisuAI, Project N.E.K.O., Soul of Waifu, Z-Waif, VirtualWife — are roleplay and companion clients, and reading them together produces a finding the seven-column rubric cannot express.

Between them they hold four marks out of a possible 42. On the epistemic questions this atlas usually asks — is there a tombstone, a trust state, a validity time — the answer is mostly no. And these are, by hours of use, among the most-exercised memory implementations in existence, running against users who would notice immediately if memory failed them.

The resolution is not that the users are undemanding. It is that the axis they demand on is different. What a companion user means by good memory is not autonomous factual accuracy; it is authorial control — being able to see what the model will read, and change it. Judged on that axis these systems are not primitive but mature, and the mechanisms are specific:

  • Editability as the primary write path. SillyTavern has no extraction at all; a person writes every entry. RisuAI's HypaV3 modal lets a user edit, delete, merge, pin and re-roll any summary the model wrote, with the re-roll previewed before it lands. This is the memory as an editing surface pattern, and its clearest instances are all in this group.
  • Suppression as a first-class state. @@dont_activate disables an entry without deleting it; N.E.K.O.'s ban-topic directive is keyed on the term and drops any proactive draft that names it; RisuAI's pin exempts a summary from budget pressure.
  • Hysteresis on activation. Sticky, cooldown and delay give a unit state about its own recent firing, so it neither repeats every turn nor drops mid-thread — see retrieval hysteresis. Nothing outside this group has it.
  • Guarding against the agent's own voice. Z-Waif caps the character's previous reply at two of six query terms; N.E.K.O. runs a BM25 corpus over its own output to catch rephrased repetition.

Read the four marks accordingly. A dash in the tombstone column means the mechanism was not found, and for a store whose only writer is the user it is a different absence than it would be in an extraction pipeline — there is no extractor to re-assert what was removed. The columns still measure what they measure; what they do not do is score these systems on the thing they were built to be good at.

Two transfers run the other way, out of this group and into serious systems. N.E.K.O.'s separation of disputation from reinforcement is built because raising something a user asked you to drop is an emotional injury — and it is the same architecture that stops a customer-service agent volunteering a declined mortgage or a CRM summary asking after a late spouse. And the editing surface is the cheapest correction mechanism in this atlas: one click, no model, no trust-state machine, fixing a fact the user knows and the extractor guessed.

Not in scope: the KV cache

The other naming collision, and the one that catches technically careful readers, is between agent memory and the KV cache an inference server keeps for a conversation. Both are state reused across an agent's turns; only one of them holds anything the agent believes.

ThunderAgent is the clean example, examined on 2026-07-31 at 7ddc8610…. It is MIT-licensed, an ICML 2026 Spotlight, integrated into NVIDIA Dynamo and SkyRL, and reports 1.5–3.6x agentic inference throughput. Its contribution is a program abstraction as a scheduling unit: a program_id on the API call, and a router that keeps an agent's successive requests on the worker that already holds its prefix, pausing a program when it goes off-GPU to run a tool and resuming it afterwards.

The check takes one command. Across its 3,361 lines of Python the word memory appears three times, all three in backend/sglang_metrics.py reading memory_usage and token_capacity off a worker to balance load — GPU capacity telemetry. There is no sqlite, no file write, no vector store, no embedding call, no remember, recall, forget or persist anywhere in the package. The Program record is a dataclass holding a backend URL, a two-value status (REASONING on GPU, ACTING off it), a context length and step counts, living in a process-local dict that is discarded when the program terminates.

So nothing survives the process, let alone the session, and the inclusion test is not close. It is worth naming rather than passing over because the confusion runs the other way from the chat-buffer case: this really is a system whose entire value is not recomputing state across an agent's turns, which is what memory sounds like it should mean. The distinction the atlas draws is that a KV cache is an optimisation whose loss costs latency, and a memory is a claim whose loss costs correctness. Deleting a cache entry is free; deleting a memory is the hardest problem on this page.

And the distinction survives the obvious objection, which is that a KV cache does not persist. warpdrv — AGPL-3.0, 518 commits since 21 March 2026, examined at 939315a1… — is a desktop manager for local llama.cpp servers whose KV cache checkpoints do persist, deliberately and carefully. processManager.ts passes --slot-save-path to every server it launches; checkpointService.ts posts /slots/<n>?action=save, writes the slot's cache and the token sequence behind it to a .bin file, and stamps it with a deterministic fingerprint of the model file plus a fingerprint hash. Restore posts ?action=restore and validates that hash against the target server's model, returning typed IFingerprintMismatch entries rather than loading blindly, and the documentation states the binding plainly: a checkpoint is bound to the model file, context size, flash-attention setting, cache quantisation, slot count and backend build, and "restoring under different settings either fails or produces garbage."

That artifact is versioned, content-fingerprinted and compatibility-checked more carefully than several memory stores in this atlas — and it is still not memory, which is the point. Restoring it changes how long the next token takes and nothing about what the model will say that a longer prefill would not also have produced. There is nothing in a .bin slot dump that a later reading could contradict, no identity a correction could name, and deleting one costs a prefill. The rest of warpdrv's twenty-table SQLite schema is the same boundary in other forms: threads, messages, message parts and tool calls are the transcript; an embedding_meta row keyed on messageId indexes that transcript; the code-graph tables index the user's own files; and the remainder is permissions, guardrail definitions, modes and opaque data TEXT DEFAULT '{}' UI state. Its twenty-four MCP tools read the transcript, the embedding index and the code graph, and not one of them writes a fact. Persistence was never the boundary.

Prepared attention state can also sit downstream of a memory, and then the memory is what gets read. aimee calls it native memory: in a preprint (doi:10.5281/zenodo.23120104, 3 October 2026), selected records reach the model as prepared key/value state rather than prompt text. Unlike ThunderAgent's routing state and warpdrv's slot dumps, that state derives from records with an identity, a revision and a lifecycle, and a new revision is meant to invalidate it. aimee stays in scope because the governed store it reads from is in the tree; the route releases rows and revisions, and the plugin that builds tensors from them is not published. Dropping a prepared bank costs a re-preparation, while correcting the record changes what the next release holds.

The sharpest test of that distinction is a paper, and it comes at the boundary from the other side. Do Language Models Need Sleep? Offline Recurrence for Improved Online Inference (arXiv:2605.26099, Lee, McLeish, Goldstein and Fanti, 25 May 2026) proposes exactly what its vocabulary suggests: a model that periodically sleeps, running N offline recurrent passes over the accumulated context to update fast weights in its SSM blocks through a learned local rule, and then evicts the KV cache and carries on. The abstract calls the result "persistent fast weights", the mechanism "consolidation", and the paper's own framing is that computation moves to sleep so that wake-time latency is preserved. Every noun on this page's list appears, and the reported gains are real if modest — on GSM-Infinite, Jet-Nemotron 2B goes from 0.742 to 0.812 on six-operation problems and 0.351 to 0.388 on eight-operation ones as N rises from 1 to 6; Ouro 1.4B goes from 0.419 to 0.615 and 0.210 to 0.272 at N=4 — with the largest gains on the instances needing the deepest reasoning, which is the interesting part.

It is out of scope on three independent grounds, and the first is the one that settles it: Algorithm 1 begins by zero-initialising the fast weights. They are per-sequence. Nothing consolidated during one example is present at the start of the next, so "persistent" means persistent past a cache eviction, not past a session. Second, nothing stored has an identity. The update is a gated Hebbian rule that overwrites a fixed-size state matrix continuously; there is no item to retrieve by name, no claim to correct, and no way to forget one thing rather than everything — the same reason MemAgent is excluded above, one architectural level deeper. Third, there is no artifact: no code, no checkpoints and no released data, so nothing here could be read at a pinned commit even if the first two answers went the other way.

What makes it worth recording rather than passing over is that it is the cleanest demonstration of why this section exists. A KV cache is state an agent's turns reuse; these fast weights are what you get when you train a model to compress that state well instead of storing it. Both are optimisations whose loss costs latency and accuracy, and neither can answer "why do you believe that" or "forget what I told you last week", because neither ever claimed anything. The field is converging on the word sleep for this — a second paper by an entirely different group, arXiv:2606.03979 (Behrouz, Hashemi, Javanmard and Mirrokni, 2 June 2026), is titled Language Models Need Sleep: Learning to Self-Modify and Consolidate Memories and also ships no code — so a reader meeting either should check which sense of consolidation is meant, and whether anything survives the example.

A paper titled Learning how to Forget is about cache eviction, and unlike the two above it ships the artifact. Learning how to Forget: Fine-tuning for Long-Context Sparse Attention (arXiv:2608.19920, Seeger, Zhang, Patil, Benidis and Schelter, 20 August 2026) fine-tunes a model to co-adapt with the KV-cache policy that will evict for it at inference — on a budget its abstract puts at a single 40 GB A100 — and reports that a model trained this way often outperforms one trained with exact attention under sequence parallelism. Forgetting here is the eviction: once the slots are full, a new token overwrites an old one. What is compared are cache policies — lastrec, a content-dependent smart_lastrec, and three H2O variants — over the Helmet suite on NQ, TriviaQA, HotpotQA and PopQA at 64k and 128k tokens, and on TREC, NLU, CLINC150 and a JSON key-value probe.

It is recorded rather than passed over for two reasons. The title names this atlas's central concern and means something else by it, which is the collision this section exists to mark. And it is the one paper here that can be checked: KeysAndValues (awslabs/keys_values, Apache-2.0) is a real library — roughly 65,000 lines of Python beside CUDA kernels under csrc/ — read at 23888f05…, screened first: no auto-run surface, one build-time execution surface (test/conftest.py, which runs on pytest collection) and a pyproject.toml with no lockfile beside it. Reading it settles what the abstract leaves open. keys_values/kvcache/ holds h2o.py, qh2o.py, buffers.py, quant_buffers.py and offloading.py — eviction policies and the buffers they evict from. The word forget occurs once in the package, as a string in a test fixture (kvcache/test_utils_advanced.py:628). Every occurrence of memory is an allocation: an out-of-memory retry in array_limit.py, temporary-memory notes in the attention kernels, shared memory in flashinfer_ops.py. There is no sqlite, no session and no store anywhere in it, and nothing it holds outlives the process. What it forgets is a token's key and value, and it forgets them to fit a context into a GPU — which is exactly the line this section draws.

Not in scope: the semantic response cache

The third collision is a semantic cache: a store of past question/answer pairs, keyed by embedding, that returns a saved response when a new query is close enough and skips the paid call entirely. Unlike the KV cache this one is genuinely durable, genuinely retrieved by similarity, and genuinely has eviction — so it passes a naive reading of the inclusion test, and it is worth saying why it fails a careful one.

GPTCache is the reference implementation, examined on 2026-08-09 at c59fb3a6… — MIT, about 10,800 lines of Python, with no commit since 11 July 2025. It has the parts: an embedding step, a scalar store beside a vector store, a similarity evaluator, and manager/eviction/ with LRU and LFU over cachetools.

One function settles it. gptcache/processor/check_hit.py is the default hit check, and its body is return cur_session_id not in cache_session_ids. The default behaviour is that a cached answer is withheld from the session that produced it and served to every other session. That is the precise inverse of memory, and it is not a bug — asking the same question twice in one conversation usually means the first answer was unsatisfactory, so replaying it is wrong. A memory system's whole purpose is to give a session back what it learned; this one is built to refuse exactly that.

khazad makes the second half of the point, at da10e6fc… — MIT, 1,634 lines, a transport-layer cache on Redis vector sets requiring no application change. It has a CacheScope enum, so a reader scanning for the scope key finds one. Its two values are MODEL and HOST, and its docstring says what they partition: "a gpt-4o answer is never served to a gpt-4o-mini call." There is no user, tenant or session dimension anywhere. A cache's scope exists to keep an answer from being served in a context where it would be wrong; a memory's scope exists to keep it from being served to someone who should not see it. Intercepting HTTP with no application change is the selling point, and it also means the cache cannot know who is asking.

So the rule from the KV-cache section holds with one amendment. A cache is still an optimisation whose loss costs latency, which is why nothing here needs a tombstone. But a semantic cache differs from a KV cache in that its hit can be wrong — two questions can be neighbours in embedding space and have different answers — which is why the serious ones grow a verification step, and why that step is a cost-control problem rather than a memory one.

vision-memory-mcp extends the same shape to images, and is worth recording because it looks more like memory than the other two. At c5fa6625… — MIT, version 1.2.1, 38 commits since 13 July 2026, 20,980 lines of TypeScript — it caches screenshots by perceptual hash with local CLIP embeddings, OCR, and accessibility-tree grounding, and it keeps a transition graph between visual states, so it has clustering, sequences, snapshots and a redaction pass over extracted text with patterns for cards, emails, SSNs and provider tokens. Its own sentence settles the category: the point is "to eliminate repetitive vision LLM calls." What it stores is a derived description keyed by its input, and core/eviction.ts runs a background TTL and LRU sweep — the cache policy this section's rule names, an optimisation whose loss costs a model call rather than a belief the store must account for. Correcting an entry means re-running the model, not retracting a claim.

One detail from it is worth carrying out of the section, because it is a third answer to a question two entries on this page get wrong. core/cache.ts resolves the branch with git rev-parse --abbrev-ref HEAD, which on a detached HEAD prints the literal string HEAD — so work during a rebase or a bisect is filed under a branch named HEAD. The same author's state-memory-mcp asks the same question with git branch --show-current, which prints nothing there, and files the work under main while reads for it return nothing. Two packages, one author, one question, two different wrong answers — and dsh-mnemon a third. The branch an agent is on is not a fact a single git invocation reliably returns, and a memory that scopes by it needs to say so when it cannot tell.

Not in scope: conversation-window management

Most agent frameworks ship something called "memory" that is a chat buffer, and the naming collision misleads people evaluating options.

The discipline on this side of the line has its own name, and it is not memory. Context engineering is the term for deciding which tokens reach the model at inference. Elastic's context engineering vs prompt engineering sets it against prompt engineering as a question of curating "what information the model has access to" rather than of how a request is phrased, and rests the account on a model being a stateless function handed one snapshot per call. That premise is the boundary. A snapshot cannot turn out to be false, because nothing in it was ever claimed to be true, so a context-engineering problem and a memory problem stay different problems however far the vocabulary overlaps — and the overlap is worth naming here, because a reader who arrives holding the newer word will otherwise not find the line drawn in it.

IBM's BeeAI framework is the cleanest example. At commit 21284d7f… its entire memory subsystem is about 1,300 lines across both the Python and TypeScript implementations, and consists of four strategies for deciding which messages stay in context: UnconstrainedMemory, SlidingMemory, TokenMemory, and SummarizeMemory, plus a ReadOnlyMemory wrapper. Its documentation states that "Messages are the fundamental units stored in memory". The memory modules reference no embeddings, vectors, or persistent store; BeeAI keeps document retrieval in a separate rag module, so the framework's own architecture agrees these are different concerns. LlamaIndex's older ChatMemoryBuffer family and LangChain's original ConversationBufferMemory are the same category — which is why this atlas reviews langmem and LlamaIndex's newer block-based Memory instead.

The 2026 form of this replaces the buffer with a state, and it sharpens the line rather than crossing it. SKILL.state: Scalable Long-Horizon Agent Skills (arXiv:2608.26263, Badhe, Tiwari and Chung, v1 26 August 2026, v2 28 August) drops append-only history altogether: at each step the model is handed the immutable skill specification, the current structured execution state and the latest observation, and intermediate reasoning is discarded as soon as it has produced a validated state update. The reported effect is large — a 16.2x token reduction at a hundred steps, and 122k tokens against a Memory baseline's 6.1M at two hundred — beside accuracy gains on a warehouse task (0.94 against 0.84–0.91), InterCode CTF (54.2% pass@1, +7.8 points on the strongest baseline) and both τ-Bench domains, across Gemini-3-Flash and two open-weight models.

It belongs on this side of the line for the reason its own design states. The execution state is overwritten on each validated update, so it has no history a later reading could contradict: a state that is replaced does not turn out to have been false, it stops being current, and the reasoning that produced it is deliberately not kept. That is context engineering done well — the discarding is the contribution — and it is the opposite of the property this atlas counts, where the point of a memory is that something survives to be wrong later. Two things also make it uncheckable here: no code is released, and the headline SkillExecBench is the authors' own and unpublished, so every figure above is the paper's rather than one recomputed from an artifact. What it does supply is a number for the cost side of the boundary — 6.1M tokens to keep the conversation as the memory, against 122k to keep a state instead.

Deciding what stays in the context window is a real problem. It is a different problem, and the test that separates it is whether the store holds anything that could turn out to be false. A system whose memory is a window has no answer to "why do you believe that?" or "forget what I told you last week", because it never claimed to remember.

Nothing survives the session is the wrong shorthand for that test, and SALT below is the case that pulls the two apart: it persists a per-conversation corpus to disk, resumes it across processes, and selects from it by query — while every row in it is a verbatim sentence the session itself produced, with no claim, no correction and no provenance beyond which turn said it. Persistence and retrieval turn out to be the cheap half. What keeps a system on this side of the line is that nothing it stores is the kind of thing a later reading could contradict.

One caveat, from the excluded pile rather than from this list. The boundary is about what the window is, not about how carefully it is maintained, and the best-argued forgetting mechanism this atlas has read belongs to a system on the wrong side of it. Untrivial-ai/agent-orchestrator — a harness, recorded with the other exclusions in the known limitations — mirrors a provider's chat history into SQLite, and when the provider drops turns it propagates that deletion through five statements in one transaction, reaching a queue, a legacy row shape written by an older build, a derived summary column, and a blocked approval. Nothing there outlives the session, so it earns no report. The discipline does transfer, and most stores that do claim durable memory stop at the first of those five.

The most sophisticated instance of the category is worth naming, because it shows the boundary is about architecture rather than about effort. ByteDance and Tsinghua's MemAgent (Apache 2.0, at ef4219b2…) processes arbitrarily long input in fixed context by walking it chunk by chunk, and at each step the model is handed the problem, the previous memory, and the next chunk, and asked to emit an updated memory that overwrites the old one. What it keeps is not decided by a heuristic or a prompt-engineered summarizer: the whole loop is trained end-to-end with multi-conversation RL against the final answer's reward, so the retention policy is learned — dropping the wrong detail costs reward several chunks later. The mechanism is genuinely novel — nothing else this atlas has read learns what to remember rather than being told — and the published claims are strong. The paper is arXiv:2507.02259 (submitted 3 July 2025, revised 29 July 2026, accepted to ICLR 2026 as an Oral), and its abstract states the result as extrapolating "from an 8K context trained on 32K text to a 3.5M QA task with performance loss < 5%" and 95%+ on the 512K RULER test. The two lengths are separate and the distinction matters when the number is repeated: 8K is the context window the agent runs in, not the length it was trained on.

It is still out of scope, and the paper and the code say so independently. The paper's memory is a fixed-length sequence of ordinary tokens inside the context window — 1024 of them in the experiments, sized so that per-chunk compute stays constant — reset for each input document and consumed by an answer-generation step that sees only the problem and the memory. The code agrees: self.memory is a NumPy object array allocated in start() per batch, carried across chunks of one input, and discarded; there is no persistence path, no retrieval, no scope, and no identity a later correction could name. It is a compressor with a learned policy rather than a memory with a lifecycle — the same category as BeeAI's SummarizeMemory, several orders of sophistication up. That the best learned context compression in the field lands outside this atlas is the clearest argument that the boundary is drawn in the right place.

SALT is the instance that persists, and still belongs here. MIT, 19,523 lines of Python, 362 commits since 2 July 2026, at 89feb852…, with a paper (arXiv:2607.17486, 20 July 2026) filed under cs.PF — performance, not language — whose stated goal is cutting prefill compute and KV-cache cost. Its mechanism is a fix for a real failure it names: rank sentences by a scalar and under a tight budget the document's dominant theme eats the whole budget, dropping the one sentence that links it to a second. So SALT organises each sentence's keywords into a trie ordered by sentence frequency and spreads the budget across theme branches before choosing sentences.

The chat mode is what makes it interesting here. SessionTrie keeps one trie per conversation in cache_dir/<conversation_id>/ — embeddings.npy, state.pkl, config.json, written embeddings-first on purpose so a crash leaves orphan vectors a load can drop rather than sentences with no vectors, which "nothing could repair". It survives the process, resumes by id, and each turn re-selects under the budget while seeding the prior turn's per-node coverage so material already surfaced is discounted — cross-turn submodular selection, keyed on the canonical frozenset of each node's root-to-node keyword path so it survives the trie being rebuilt. There is even per-item eviction: past max_sentences the oldest conversation rows are masked, their keyword document-frequency contribution removed, and — the careful part — their verbatim-dedupe hash withdrawn, so re-sending a masked sentence stores it again instead of being dropped against a row that is no longer live.

Persistent, retrieved by query, scoped by conversation, bounded, and evicted with more care than several systems that do hold beliefs. It is still not memory, and the reason is what is in the rows. Every one is a verbatim sentence the session produced or a document it was handed; nothing anywhere in the tree extracts a fact, a claim, an entity or a preference; there is no state a sentence can be in other than alive or masked; and correction is /clear, which wipes the conversation directory (behind a guard that refuses any path outside the sessions root). The store can tell you which turn said something and cannot tell you whether it was true, which is the question this atlas exists to ask. The delegation ledger in its agent mode draws the same line from the other side: it records what each worker was asked and what it cost, "never the worker's prose: the text was printed, and a session that wants it in memory ingests it as a turn instead."

A second boundary is worth naming because it is where this atlas most often declines something interesting: a store of the agent's work is not a store of the agent's beliefs. Being a durable store an agent reads and writes is the entry condition here, not the disqualifier — every system in this report is one. What separates them is what the rows are about.

beads (MIT, at dbbf3a96…) is a distributed graph issue tracker for AI agents, Dolt-backed, and its issues pass the literal test — they persist across sessions, carry identity, and can be corrected. So would Jira. Apply the sharper test from the section above instead: could a row turn out to have been false? An issue can be closed, reopened, reassigned, or wrong about its own status, and none of that is the store having been mistaken about the world — it is the work moving on. A task database records what is to be done; a memory records what is the case, and only the second can be contradicted by evidence. That is the line, and it is about the contents rather than about who drives the database.

The same tree also carries a memory, and it is on the other side of that line. cmd/bd/memory.go at that pin defines bd remember, bd memories, bd forget and bd recall over kv.memory.* rows in the config table, injected by bd prime — a sentence such as "auth module uses JWT not sessions" can be wrong. beads' issues stay out of scope; its memory plane has its own report, and Gas Town, which writes typed memories into the same keyspace, has another.

beads is also noted here because Core Memory borrows the "bead" vocabulary, and a reader meeting both could reasonably assume a relationship.

The nearby case is a corpus index. VectorSpaceLab/general-agentic-memory (GAM) builds an LLM-generated directory tree over long documents, video, or agent trajectories, with a Memory and TLDR summary per chunk and an agent that navigates the taxonomy to answer questions. That is a hierarchical index with exploratory QA over it — the shape OpenViking already covers with its L0/L1/L2 granularities, and one this atlas would need a reason to add again. It also carries no licence file. The OptMem exception was granted for mechanisms worth reading on their own; an auto-generated taxonomy over a document tree is not that, so GAM gets a note rather than a report.

A third shape declines for a reason worth separating from the other two: an algorithm workbench is not a memory system. nuster1128/MemEngine appears in the open-source framework table of the field's own 107-page survey as a representative memory framework with a "modular space" structure, and it implements ten named memory methods — FUMemory, GAMemory, LTMemory, MBMemory, MGMemory, MTMemory, RFMemory, SCMemory, STMemory — behind one BaseMemory interface with store, recall, manage and optimize. It is a genuinely useful thing: a common harness for comparing published memory algorithms against each other.

It stores nothing. LinearStorage in memengine/utils/Storage.py is a Python list; reset() empties it; BaseMemory declares no save or load; and server_start.py keeps sessions in service_database = {}, an in-process dict addressed by a UUID that does not survive a restart. Nothing in the package writes to disk except a Display utility and the config reader. So MemEngine cannot fail this atlas's test in an interesting way — nothing survives the session to have an identity, let alone a correction. It also carries no licence file, which would have excluded it independently.

That a peer-surveyed "open-source memory framework" turns out to have no persistence layer is not a criticism of the project, which is honest about being a research library. It is a reason to be careful with framework lists: the word covers both a store you would run in production and a benchmark rig for comparing algorithms, and only one of them can answer "forget what I told you last week".

A fourth shape is the most frustrating one to decline, because it is closer to this atlas's concerns than most of what it does review: a guard is not a store. OWASP Agent Memory Guard (Apache 2.0, at 78b9227f…) is the reference implementation cited by the security survey above, and it is the closest public code to that survey's Verifiable Memory Governance. Its MemoryGuard screens every read and write through a detector suite, and it carries mechanisms the atlas counts and rarely finds:

  • A classification graph with typed transitions — ephemeral → user_preference_candidate → verified_preference, where that last edge sets requires_verification=True and promote() refuses without an explicit verified=True. That is a trust state machine with a human opt-in on the promoting edge, and it is enforced rather than advisory: writing with a different class raises rather than silently reclassifying.
  • Snapshots and rollback, including a pre-snapshot before every blocked write and before every retire_if sweep — Rollbackability, which the survey calls "largely absent".
  • A self-reinforcement detector aimed squarely at the failure this atlas keeps naming: an agent reading its own prior claim, elaborating it, and writing it back until a hallucination hardens into a fact. It fires only on AGENT_AUTHORED writes and resets when independent evidence arrives.

It gets no report because nothing survives the process. The only shipped MemoryStore implementation is InMemoryStore, a dict; SnapshotStore is a 50-entry OrderedDict ring buffer; the event log is a Python list; and the HTTP and MCP servers both construct MemoryGuard(policy=...) with no store. The MemoryStore Protocol is the extension point, and the durable half is the reader's to supply. So the same rule that excluded MemEngine applies, for a different reason: MemEngine had no store because it is a workbench, and this has no store because it is a layer.

Two observations survive the exclusion, and they are why it is recorded at length rather than dropped. First, the self-reinforcement detector guards a 60-second window over a similarity ratio, keyed by memory key and held in a deque of eight — it catches a tight write-read-elaborate loop, which is a real attack, and not the atlas's actual failure mode, where a nightly extraction pass re-asserts what a user corrected last week. Second, its quarantine is the clearest near-miss on a tombstone in this atlas: a blocked write's value is stored in self._quarantine[key], exposed as a read-only property, exported to metrics — and consulted by nothing. Write the same rejected value again and, if no detector independently matches it a second time, it commits. The one project in the field built specifically to secure agent memory implements four of the survey's five primitives, and the one it does not implement is Verified Forgetting.

Checkpointing is the boundary case most often argued about, and it belongs here rather than in the corpus. LangGraph's native thread-level persistence is the de-facto standard for stateful agents, it survives the process, and it supports time-travel — you can rewind a thread to an earlier checkpoint and resume. Those are real properties and none of them is memory by this atlas's test. A checkpoint is the whole state of a run at a point in time, addressed by thread and step; it has no unit a correction could name, no scope key beyond the thread, and nothing that could be individually superseded, rejected or forgotten. Rewinding a thread discards everything after a point rather than retracting a belief. It is the same category as MemAgent above — durable, sophisticated, and about runs rather than about what an agent holds true — which is why this atlas reviews LangMem, the layer LangChain built for the other question, and not the checkpointer beneath it. Read them together and the split is the point: LangMem stores items in a BaseStore namespace precisely because checkpoints cannot hold that shape.

A fifth boundary needs stating only because every list of recent memory papers puts it beside the systems here: an architecture is not a memory system. Titans: Learning to Memorize at Test Time (arXiv:2501.00663, Google Research) adds a neural long-term memory module that learns what to store during inference, alongside short-term attention and persistent memory tokens. It is a genuine advance and it is a different object: what it memorizes lives in module weights updated per sequence, with no key, no scope, no provenance and nothing a later correction could name. It also has no official implementation — the repositories carrying the name are third-party reimplementations, so there is no canonical artifact to pin even if the boundary were drawn elsewhere. The same applies to the model-editing line (ROME and successors) that the field's surveys file under parametric memory. Second Me is in this atlas because it is a system that fine-tunes on a user's documents and then has to answer "delete my data" — the deletion request is what pulls weights into scope, not the fact of learning.

Eight more candidates were read on 2026-07-29 and declined. They are recorded because each looks like a memory system from its description, and because two recurring shapes account for most of them — resource accounting that uses the word memory, and durable state that records an agent's work rather than its beliefs:

Candidate Why not
kvcache-ai/AgentENV An orchestrator for Firecracker microVM sandboxes. Every one of its 176 files matching "memory" means guest RAM, memory ballooning or a memory snapshot; InMemoryMetadataStore holds sandbox metadata. Adjacent by name only
deftai/subspace ACP, A2A and MCP transport plumbing — framer, codec, wire. Its single "memory" match is a test fixture named memory-message-a
code-yeongyu/oh-my-openagent An agent harness whose durable state is .omo/boulder.json, a work ledger the prompt calls "the source of truth", plus a team mailbox with leases and an ack ledger. Its rules engine loads human-authored files into context and writes nothing back. This is the beads exclusion — a task database and a queue, not a belief store
endomorphosis/ipfs_accelerate_py A model-inference and hardware-routing framework. Its ~4,500 "memory" matches are memory_mb, memory_bytes, memory_gb, WebGPU memory optimisation and resource schedulers — the AgentENV shape again, RAM rather than recall
endomorphosis/swissknife A browser-based collaborative virtual desktop that vendors the previous entry's JS port; same memory vocabulary, same exclusion. It also ships no licence file
endomorphosis/lift_coding A voice-first GitHub workflow assistant. No memory subsystem — the matches are in audio fetching, auth, metrics and a GitHub provider
JesseBrown1980/asolaria-behcs-256 Its behcs-memory-bridge.js indexes markdown memory files into addressable "cubes", which sounds in scope until the constant: const MEMORY_DIR = 'C:/Users/acer/.claude/projects/E--/memory'. It is a personal index over another tool's memory store, at a path that exists on one machine. The 927-line memoryStore.js beside it sits under packages-legacy-import/ and is vendored, so it is not the project's own design either
xD4O/memento Reviewed. A licence appeared — PolyForm Noncommercial 1.0.0 — and the decision was revisited as this row said it should be. See Memento

Compaction appears in this atlas only as a component of systems that also persist — mastra-observational-memory with exact covered ranges and buffered activation, hermes-agent with a hard budget forcing in-turn consolidation, pi with deterministic file manifests on compaction entries. The test for inclusion is not whether a system compacts, but whether anything survives the session with an identity you could later correct.