| title | Capability Index |
|---|---|
| eyebrow | Which systems do what |
| description | Every reviewed system against the seven capabilities the atlas judges by, filterable, generated from the same frontmatter as the reports. |
| root | .. |
| page_kind | comparison |
The comparative report says what each system does. This page answers the other question — which systems actually have X — for the seven mechanisms whose absence causes a failure the system cannot detect.
Marks come from each report's frontmatter, so this table cannot drift from the reviews. A tick means the mechanism was found in code at that report's pinned commit; a dash means it was not found, which is different from impossible, planned, or badly built. The definitions, the evidence threshold, and why these seven rather than others are on the atlas rubric.
Read the of 7 column as a shape, not a score. Around half of the systems
here carry none or one — the exact share is computed per report and printed in
the strip at the top of each one — so a low number is the ordinary case rather
than a poor one, and most of these columns are outside what most of these systems set out to
do. A local coding agent that never claimed to arbitrate truth is not failing by
carrying one mark. The column is worth sorting on only when you already know
which mechanism you need.
Filters combine with and — "tombstone and scope enforced" is the question worth asking, and it is the one a per-column view cannot answer.
Which one you need depends on where your memory breaks, and the seven do not intervene at the same place. Three of them guard the write, two describe what the store is able to say, one guards the read, and one is the test that keeps the other six honest. Read as a row of seven equal boxes they look like a scorecard; read against the path a memory takes they are seven different answers to seven different questions:
flowchart TD
EV["Evidence<br/>messages, files, tool output"] --> WR{"Write path"}
WR --> ST[("Store")]
ST --> RD{"Read path"}
RD --> CX["Context<br/>what the model sees this turn"]
CX -.->|"that is wrong, forget it"| CO["Correction"]
CO --> WR
T["Rejected-value tombstone<br/>refuses the value, not the row"] -.-> WR
H["Human review surface<br/>a person adjudicates, not just views"] -.-> WR
A["Append-only mutation audit<br/>what changed, in the system's own store"] -.-> WR
TS["Explicit trust state<br/>may this be acted on"] -.-> ST
BT["Bi-temporal validity<br/>true-when, apart from recorded-when"] -.-> ST
SC["Scope enforced in retrieval<br/>the filter that is actually applied"] -.-> RD
NE["Negative retrieval assertion<br/>a committed test that this must not come back"] -.-> RD
The loop is the part worth staring at. A correction re-enters through the same write path that created the belief, which is why a mark on the write side is worth more than it looks: it is the only place that sees both the original claim and the attempt to reassert it after you thought it was gone.
A system with six ticks is not better than one with two. It is differently shaped. Memory Engine governs access more thoroughly than anything else here and knows nothing about whether a memory is true; OptMem carries one mark and is among the most carefully reasoned designs in the corpus. Several systems carry a dash on a capability their design deliberately does not need.
Three things the counts show that the reports individually do not:
Read-path scoping is common; correction is not. Over half the atlas applies a scope key when retrieving. Twenty-four systems of 336 carry a value-level tombstone. That gap is the atlas's central finding, and it is visible here in one column pair.
Trust is usually a number, not a state. Most systems collapse "how sure am I" into "how findable is this" — see decay and reinforcement.
Negative evidence is almost never tested. One hundred and eleven repositories of three hundred and thirty-six assert that particular material must not appear — the assertion every scope, deletion and correction claim in this atlas ultimately rests on. Read together rather than one at a time, they split cleanly, and the split says more than the count. A full re-score on 2026-08-08 found 27 of the 37 assert about a read path — 20 about a particular value, 7 about a scope boundary — while ten assert about a projection, a preamble, a summarization, a file or a write. All ten are real committed tests. Only the 27 are negative retrieval assertions.
Seven asserted a boundary at that reading: that a principal cannot retrieve
another principal's material. Five are worth describing.
MIRIX's test_filter_tags_db.py
creates a memory under one scope, searches under another, and asserts the id is
absent. Aukora Kernel does it better — an unrelated
principal reads ok: false, a subject whose delegation manifest was revoked
reads ok: false, and the owner reads "the secret" in the same block, so the
denial is proved targeted rather than a blanket failure.
EverOS does it at the endpoint: two owners, the same query
string, assert c_ids.isdisjoint(m_ids) plus a positive control on each side,
repeated for two agent owners sharing a keyword. CrewAI
does it over a path hierarchy: three records under /other/scope,
/crew/crew-a/inner and /crew/crew-b/inner, a Memory opened with
root_scope="/crew/crew-a", and an assertion that recall returns exactly one
result and it is the rooted one. CSM asserts the degenerate
case the other four leave implicit: searchMemories called in project mode with
no project id must return [], with the assertion message spelling out the
intent — "project mode without a project ID must fail closed".
All five also hold scope_enforced. Their negative suites are
therefore tests of a capability the same system already claims — which is
worth having, and is not evidence about deletion or correction.
Twenty asserted about content at that reading: that particular material must
not surface to anyone entitled to search, regardless of who is asking. Eight
show the range.
open-cowork's forbiddenHits is an eval-harness field
naming what a query must not return, scored as a penalty.
Verel's tests/test_memory_negative_eval.py asserts a
REJECTED fact is invisible to every recall path — a suite built from the
red-team finding that produced its tombstone. Project N.E.K.O.'s
test_hard_filter_drops_negative_score asserts that an entry the user
disputed is dropped before the rerank, the docstring giving the reason:
"Stage-2 would either reinforce the dispute or, worse, cancel it."
Helm is the weakest of them: its supersede
case asserts recall returns exactly one active row and fails with "recall
returned old value" if the replaced one appears. The value is still in the table
and still readable through history, so this is a genuine read-path exclusion —
but it covers replacement only. Helm's other two exits, forget and the
confidence-floor prune, are hard deletes with nothing asserted about them,
which is the case where a re-derivation would actually reinstate the value.
agent-afk's
it('excludes superseded facts from search') asserts the same about its FTS
path, and Agno is the same shape against a judged verdict — a
retired fact absent from live_facts() while both rows remain in the record. The
Pydantic AI Harness asserts
it about the prompt rather than the store:
test_delete_existing_is_content_free requires a deleted body to be absent from
the tool result, a search test requires all('secret' not in repr(match)) under
a character budget, and two injection tests require a superseded line and a
stale fact not to appear in the captured model context.
Graphify adds the cheapest version of the shape and one
nobody else has: test_negative_only_node_absent_from_sources asserts that a
source cited only by answers marked dead_end appears in none of the three
lesson lists — a source that failed rather than a value that was rejected.
Only the content kind probes the question the atlas is actually asking. A boundary test proves the filter works; a content test proves a value that was rejected, disputed or forbidden stays gone. Within that kind the sharpest cases raise a distinction the rest do not: a test that a superseded value stays hidden is cheap, because the row is still there to be filtered on. The expensive assertion is that a value the system destroyed does not come back — which requires something durable to check against, and is why the tombstone column and this one keep appearing together.
Two further things the joint reading shows. The positive control — asserting that the denial is targeted rather than an empty result — appears in Aukora and EverOS and is absent from the rest, and a negative test without one passes just as well when retrieval is broken. And the assertion shape is reachable from ordinary engineering practice: every boundary case above is an access-control test that happens to assert about recall rather than a result of memory research, and N.E.K.O. reached the content form from a companion app, where re-raising something the user asked you to drop is a product failure rather than a data-quality one.
Strictness is what makes the counts mean anything, and it hides the interesting cases. The ones worth knowing:
- claude-mem has "tombstones" that synchronize row deletion across stores — not a rejected-value tombstone.
- Mercury grades confidence three ways and has no discrete state.
- Most trust-state systems stop short of the state that matters.
Verel, RainBox,
Gini and memsem carry an
explicitly rejected state; Magic Context
qualifies on
staleandflagged. - RainBox's
RetrievalEventand Atomic Agent'svote_eventsare append-only logs of use and feedback, not of mutation. - ai-memory, nanobot and Basic Memory get an audit trail from git, which is a real mechanism and a different one.
- Memanto resolves conflicts with a human and then deletes without a tombstone — the most carefully reasoned correction in the atlas, and the next extraction pass may undo it.
- Daimon is the weakest of the tombstones, and the flag is granted on mechanism rather than on hardening: it suppresses on the read path rather than refusing the write, and its key is a hash of the item's exact text, so a paraphrase defeats it where Verel's normalized key would not. It is also the only holder with no test that re-asserts a forgotten value.
- memsem is the near-miss in the other direction: its suppression table refuses the write and is keyed on the normalised value, and nothing but a human rejecting a candidate ever writes a row into it, so its own automatic supersession path reaches the store ungated.
The seven marks above are about mechanisms. This is the other question: what these systems store memory in, and which retrieval channels they run. Both come from each report's frontmatter, so the table cannot drift from the reviews.
It is a census, not a recommendation. It says what the corpus contains and nothing about what works: the largest row is a plurality of under half, and no row here has been shown to retrieve better than another. Choosing an engine from a distribution is the mistake this table is most likely to cause.
Two rows need reading before the big ones. Delegated to the adopter is a
system whose storage is an adapter its user binds — a framework's BaseStore,
an ORM, "application-chosen" — so it has no engine of its own to count. No
arm named is a review whose retrieval summary named neither a lexical, vector
nor graph channel; that is a gap in the review, not a system without retrieval.
| Stored in | Systems | Read off code | Retrieval arm | Systems | Read off code | |
|---|---|---|---|---|---|---|
| SQLite | 151 | 51 | Vector | 193 | 56 | |
| Files on disk | 149 | 67 | Lexical | 173 | 67 | |
| Postgres | 64 | 21 | Graph | 64 | 20 | |
| Delegated to the adopter | 34 | 9 | No arm named in the review | 83 | 27 | |
| In-process only | 23 | 7 | ||||
| Chroma | 22 | 7 | ||||
| Graph database | 15 | 4 | ||||
| Qdrant | 15 | 3 | ||||
| Redis | 11 | 2 | ||||
| LanceDB | 9 | 2 | ||||
| Embedded key-value | 5 | 0 | ||||
| Milvus | 5 | 2 | ||||
| FAISS | 5 | 2 | ||||
| MongoDB | 3 | 1 | ||||
| Elasticsearch | 1 | 0 | ||||
| Pinecone | 1 | 0 | ||||
| DuckDB | 1 | 0 |
Counted across 336 reports, each of which may name more than one store. The Read off code column is the part of each row confirmed against the tree at the pinned commit: 113 of 336 reports have been read that way, and the other 223 were derived from the review's own summary lines and are labelled seeded rather than reviewed. Read the first number as what the corpus says about itself and the second as what has been checked.