1. Executive Summary
LongMemory is the system this atlas reported on as
OpenMemory. The repository was renamed from
CaviraOSS/OpenMemory to CaviraOSS/LongMemory
on 31 August 2026, with a MIGRATION.md moving the npm
package, CLI, environment prefix and routes to the new name. It is
Apache-2.0, version 1.0.0, about 28,400 lines of TypeScript under
src/, plus a Next.js dashboard, a VS Code extension, host
integrations, and a benchmark harness for LongMemEval, LoCoMo and
BEAM.
It is a rewrite, not a rename. The report's previous
pin read packages/openmemory-js/, whose README announced a
rewrite in progress on another branch. That rewrite is on
main: packages/openmemory-js is gone, every
file the previous appendix cited is gone, and the hand-typed sectoral
interdependence matrix at the centre of that reading survives only as a
mapping inside src/core/migration/legacy_cleaner.ts. The
new engine's own invariant list rejects the old shape in so many words —
"worlds are recursive containers, not flat sectors".
What replaced it is worth the report. Strict recall is a single SQL query that asks what was true at a given moment, and the conditions it applies cover most of what this atlas asks a read path to refuse:
WHERE n.tenant_id = @tenant_id AND n.user_id = @user_id
AND n.valid_from <= @at AND (n.valid_to IS NULL OR n.valid_to > @at)
AND (n.superseded_at IS NULL OR n.superseded_at > @at)
AND n.status = 'active' AND n.use_for_reasoning = 1
AND n.confidence >= @min_confidence
AND (n.requires_grounding = 0 OR n.grounding_score >= @grounding_threshold)
AND NOT EXISTS (SELECT 1 FROM contradictions c
WHERE ... AND c.resolved = 0
AND (c.node_a = n.node_id OR c.node_b = n.node_id))
As-of valid time with recorded time kept apart; supersession evaluated as of the same instant; a discrete status; a per-fact validity contract; grounding and source requirements; and — the rare one — a fact under an unresolved contradiction is withheld from reasoning rather than ranked beneath its rival.
The defect is that none of it is tested. There is no
test file anywhere in the repository and no test script in
package.json. The previous reading's
negative_eval rested on
packages/openmemory-js/tests/test_project_isolation.ts,
which was deleted with the package it tested, and nothing replaced it.
The engine's fourteen invariants — "durable nodes are
immutable", "strict recall cannot use superseded facts",
"benchmarks define correctness" — are exported as an array and
handed back by invariants: () => hydrograph_invariants;
the function named assert_hydrograph_invariants returns the
list and asserts nothing. The last invariant is the stated substitute:
the benchmark scorecard gates a stale_leakage rate, which
measures a model's answers over external datasets rather than pinning
the query's behaviour to a fixture.
2. Mental Model
A memory is a node in a hydrograph: immutable, content-addressed, and carrying the time it describes separately from the time it was learned.
| Field | Meaning |
|---|---|
valid_from, valid_to |
when the claim is true in the world |
observed_at |
when the source saw it |
recorded_at |
when the store learned it |
superseded_at |
when a later node closed this one's transaction |
status, use_for_reasoning,
confidence |
whether strict recall may use it |
world_id, facet |
which recursive context it belongs to, and which cognitive attribute it is |
Recall is a choice of question, not a choice of index:
- current truth — active, unsuperseded, valid now;
- historical truth — valid at
@at, superseded or not, ordered byvalid_from— the forensic read; - strict candidates — current truth further filtered by every reasoning contract and by unresolved contradictions;
- associative recall is permitted to use superseded and emotional residue, and the invariant list says it "must label it".
Diagram source
%% caption: one ingest pipeline writes immutable nodes; supersession closes a node rather than overwriting it, and each recall mode is a different question over the same valid-time and recorded-time columns — strict recall adding the reasoning contract and the unresolved-contradiction check
flowchart TD
SRC["events · documents · connectors"] --> ING["immutable ingest pipeline"]
ING --> N[("hydro_nodes<br/>valid_from · valid_to · recorded_at · superseded_at")]
ING -->|"later fact"| MV["close_transaction<br/>superseded_at = now"]
MV --> N
ING -->|"conflict"| C[("contradictions<br/>resolved = 0")]
DEL["connector source deleted"] -->|"marker node, use_for_reasoning = false<br/>+ supersedes edge"| N
N --> CUR["current_truth<br/>active · unsuperseded · valid at @at"]
N --> HIS["historical_truth<br/>valid at @at, superseded or not"]
N --> STR["strict_candidates<br/>+ confidence · contract · grounding · source"]
C -.->|"NOT EXISTS unresolved"| STR
STR --> CTX["bounded context"]
CUR --> CTX3. Architecture
One TypeScript package, src/, with a shared engine
created by createMemory. ARCHITECTURE.md
states the rule that makes the surfaces trustworthy relative to each
other — "api server and cli must use the same createMemory
engine" — and the tree keeps it: the CLI, the authenticated HTTP
server, the MCP transports, the dashboard proxy and the integrations all
construct the same engine.
src/core— the hydrograph types, temporal logic (temporal/bitemporal.ts,temporal/mvcc.ts), reconsolidation, invariants, migrationsrc/stores— the SQLite store and its query table, and an in-memory storesrc/answering,src/connectors— recall assembly and external source importsrc/server— HTTP routes (ingest,recall,explain,timeline,worlds,entities,stats) and middleware (auth, rate limit, CORS, concurrency, telemetry)src/mcp— thirteen governed tools, resources, prompts, transports, andsecurity/audit.ts
Deployment and ergonomics
npm, longmemory CLI, a SQLite file.
Dockerfile, docker-compose.yml, and deploy
descriptors for Heroku, Railway, Render and Vercel sit at the root,
beside PowerShell start and stop scripts. MIGRATION.md
bridges the old npm and registry names through temporary compatibility
packages; application identifiers keep no runtime aliases.
4. Essential Implementation Paths
The query table
src/stores/sqlite/queries.ts holds the recall SQL as
named strings, which is the most legible way to publish a retrieval
contract: load_node, load_edge,
current_truth, historical_truth,
strict_candidates, aliases_for_entity,
canonical_alias. Every one opens on tenant_id
and user_id. world_id is the only optional
key, written
(@world_id IS NULL OR world_id = @world_id).
Supersession without overwrite
temporal/mvcc.ts closes a node's transaction by stamping
superseded_at and returning a new node rather than mutating
the stored one, consistent with "durable nodes are immutable"
and with the architecture document's "mutable lifecycle state is
stored separately". Because superseded_at is compared
against @at, a superseded fact is still returned by a
recall asking about a moment before it was superseded — the property
that makes the historical query honest.
Connector deletion, which is not a tombstone
When an imported source item is deleted upstream,
create_memory.ts:661-690 ingests a marker node — text
"Source item deleted: …",
use_for_reasoning: false,
metadata.source_deleted: true — and a
supersedes edge from it to the node it replaces. That is
delete-sync recorded as supersession, which is the correct and auditable
behaviour for a connector and is not a rejected-value tombstone: nothing
keys on the content to stop it being ingested again from another
source.
Contradictions as a gate
contradictions rows carry node_a,
node_b and resolved.
strict_candidates excludes any node on either side of an
unresolved row. The consequence is conservative in the right direction:
two sources disagreeing about a deployment target produce an answer that
uses neither, rather than one that silently picks the more
confident.
5. Memory Data Model
HydroNode (src/core/types/hydro_node.ts) is
content-addressed and immutable; hydro_nodes stores the
serialised node in node_json with the filterable fields
lifted into columns. A node's contract carries
use_for_reasoning, use_for_personalization,
expires_if_unconfirmed and an optional
max_valid_duration, and strict recall reads the duration
straight out of the JSON: a fact can declare that it is only good for a
period, and the query enforces it.
Provenance travels in node_json as
provenance.source_trace, and source_required
nodes are excluded from strict recall unless they carry a grounding
reference or a non-empty trace.
6. Retrieval Mechanics
Lexical, vector and graph arms feed the recall modes, and results are
returned within a token bound — "Recall is read-only and token
bounded". The distinguishing work is not in ranking but in
eligibility: strict_candidates decides what may be reasoned
from before anything is scored, and orders the survivors by confidence,
then grounding, then recency.
world_id scopes recall inside a user to a recursive
context; omitting it reads across the user's worlds, which is a design
choice rather than a leak, since tenant and user still bound the
query.
7. Write Mechanics
One immutable ingest pipeline for every surface. The HTTP ingest
route requires user_id in the body; the MCP runtime and a
library createMemory bind tenant_id and
user_id at construction, defaulting both to
default. Consolidation and reconsolidation passes run over
the graph; supersession is written as a closed transaction plus an edge,
never as an update in place.
8. Agent Integration
Thirteen MCP tools behind src/mcp/security, with an
audit log of every call; an authenticated HTTP API; a CLI with a
deterministic JSON mode that the VS Code extension consumes; host
plugins and MCP configurations under integrations/. Because
every surface shares one engine, a fact ingested through one is recalled
identically through another.
9. Reliability, Safety, and Trust
Strengths:
- Strict recall is an as-of question, with valid time and recorded time kept apart and supersession evaluated at the same instant.
- An unresolved contradiction withholds both sides from reasoning.
- Facts carry their own validity contract, and the query enforces it.
- Grounding and source requirements gate reasoning, not only ranking.
- Nodes are immutable and supersession closes rather than overwrites, so the historical read is real.
- Tenant and user are in every query without an escape.
- Every MCP tool call is audited, including denials.
Gaps:
- No committed test exists. Not one file, and no test script. Every property above is established by reading the query text.
- The invariants are not asserted.
assert_hydrograph_invariantsreturns the list; nothing checks any entry. - Identity is not derived from authentication. It is fixed per engine instance from configuration, and asserted by the caller in the HTTP ingest body; a deployed server is effectively one identity for recall.
- Only the MCP surface is audited. Ingest over HTTP and the library API leave no audit row.
- No rejected-value tombstone. A fact a user wants gone can be superseded, but nothing keys on its content to stop it returning from another source.
10. Tests, Evals, and Benchmarks
I ran nothing, and there is nothing to run: no
*.test.*, no *.spec.*, no tests/
or __tests__/ directory, and no test script.
The previous pin's isolation test was deleted with
packages/openmemory-js.
benchmarks/ is a harness for LongMemEval, LoCoMo and
BEAM with a scorecard and a FAILURE_ANALYSIS.md. Its gates
include a stale_leakage rate — an answer scores only if it
is correct and does not use stale material, and a
provider passes only if the leakage rate is at or below
stale_leakage_max. That is the right metric for a system
whose central claim is not using superseded facts. It is also a
measurement of a model's answers over external datasets, requiring a
provider to run, rather than a committed case that fails when
strict_candidates loses its superseded_at
clause — so it does not carry negative_eval, and the
invariant "benchmarks define correctness" is doing work a
four-line fixture test would do more cheaply.
No paper, arXiv reference or citation file exists in this repository.
11. For Your Own Build
Steal
- Publish recall as a named query table.
current_truth,historical_truthandstrict_candidatesside by side make the difference between modes reviewable in one file. - Evaluate supersession as of the same instant as
validity.
superseded_at > @atis what lets a historical read return what was believed then. - Withhold a contradicted fact from reasoning until
resolved.
NOT EXISTSan unresolved contradiction is one clause and a large change in failure mode. - Let a fact carry its own expiry contract and enforce it in the query rather than in a sweeper.
- Record connector deletions as supersession with a non-reasoning marker, so the removal is visible in history and invisible to reasoning.
Avoid
- Stating invariants you do not check. A list named
assert_…that returns strings reads as a control and is not one. - Replacing tests with benchmarks. A benchmark gate over a model's answers cannot tell you which clause of your query regressed.
- Deleting the test with the package. The isolation property the previous version tested still holds by reading, and nothing now fails if it stops holding.
- Auditing one surface of several. If three surfaces share an engine, audit at the engine.
Fit
Read this for the recall contract — it is one of the clearest statements in the corpus of what a memory should refuse to reason from. Adopt it with tests of your own, because the repository ships none, and with identity derived from your authentication rather than from configuration.
12. Open Questions
- Will the invariants become assertions, and will
strict_candidatesget fixture tests for each clause? - How is a contradiction resolved, and by whom — and does resolution record which side won?
- Should identity for recall be derived from the authenticated request rather than bound per engine instance?
- Why is the audit log on the MCP surface only, when every surface shares the engine?
- What does the
stale_leakagerate measure in practice — are benchmark results committed anywhere?
Appendix: File Index
- Recall contract:
src/stores/sqlite/queries.ts,src/stores/sqlite/sqlite_store.ts. - Temporal logic:
src/core/temporal/bitemporal.ts,src/core/temporal/mvcc.ts,src/core/memory/reconsolidation.ts. - Model:
src/core/types/hydro_node.ts,src/core/invariants.ts. - Engine and connectors:
src/core/create_memory.ts(identity defaults :195, connector deletion :661-690, invariants getter :815),src/connectors/. - Surfaces:
src/server/app.ts,src/server/routes/,src/server/middleware/auth.ts,src/mcp/runtime.ts,src/mcp/security/audit.ts. - Migration:
MIGRATION.md,src/core/migration/legacy_cleaner.ts. - Benchmarks:
benchmarks/src/scorecard.ts,benchmarks/src/report.ts,benchmarks/FAILURE_ANALYSIS.md.
Searches behind the absence claims
# no test file and no test script anywhere
find . \( -name '*.test.*' -o -name '*.spec.*' -o -path '*/tests/*' -o -path '*/__tests__/*' \) -not -path '*/node_modules/*'
grep -n '"test' package.json
# the invariants are exported and returned, not asserted
grep -rn 'hydrograph_invariants' --include='*.ts' src/
# the audit log is written only from the MCP security module
grep -rn 'appendFileSync' --include='*.ts' src/
# the only sector code left is the legacy migration
grep -rniE 'sectoral|interdependence|SECTOR' --include='*.ts' src/
History
2026-09-15 — 4da4986d…
— second reading, 31 commits on, and a different system at the same
slug. Screened first; nothing was installed and nothing was run. The
rewrite the previous reading noted on a separate branch landed on
main: packages/openmemory-js and every file
the previous appendix cited are gone, the repository carries the
LongMemory name throughout with a MIGRATION.md dated to the
31 August rename, and the sectoral interdependence matrix that gave the
report its title survives only in a legacy migration. The report is
rewritten for the code at this pin rather than annotated, and the title
follows the product. Marks were re-tested from scratch.
scope_enforced holds on a different mechanism —
tenant_id and user_id composed unconditionally
into every query in the SQLite store. negative_eval is
withdrawn: its evidence,
test_project_isolation.ts, was deleted with the package,
and no test file exists anywhere in the tree. bitemporal,
trust_state and audit_log are added on the new
engine — an as-of valid-time query with recorded time kept apart, a
strict-recall gate on status, contract, grounding and unresolved
contradictions, and an append-only MCP call log. tombstone
is withheld after reading the one place the word appears: the connector
deletion marker is delete-sync recorded as supersession. The slug is
kept, as the rename entry below decided, so no published URL moves.
2026-09-13 — the repository was renamed from
CaviraOSS/OpenMemory to CaviraOSS/LongMemory,
upstream of the pinned commit and after the reading below. No
re-reading: the pin, analyzed_at and every finding are
unchanged, and only source_name, source_url,
revision_url, archive_name and the
repositories-inspected entry moved. The slug is unchanged, so no
published URL moved. The archive fork was renamed to
agent-memory-atlas-archive/CaviraOSS--LongMemory to
match.
2026-08-09 — 9fdfc2ac…
— first reading, on main. The README announces a rewrite in
progress on a separate branch, which was not read. Screened before
reading; the tree was read, never installed, and no test was run.