# Benchmarking donto-memory on LongMemEval — a faithful study of what a memory layer actually adds

**2026-06-05 · a no-shortcuts, no-cheating evaluation of donto-memory on `longmemeval_s`, with codex as both reader and judge, against a codex-alone full-context baseline**

> **TL;DR.** We ran donto-memory through LongMemEval's retrieval-stressed `_s`
> variant (500 questions, ~50 sessions / ~120k tokens each) under audited
> no-leakage conditions, and — crucially — against a **codex-alone full-context
> baseline** (the same reader handed the *entire* history, no memory system).
> The headline finding is honest and a little uncomfortable: **on `_s`, where the
> whole history fits in a frontier model's context, a memory layer does not win on
> raw accuracy** — a strong reader with everything in context is very hard to beat.
> donto-memory's real, measured value is elsewhere: **~2× lower token cost**, and
> **wins on the two abilities that are its design thesis — bitemporal
> knowledge-update and evidence-first abstention.** We report the gaps as plainly
> as the wins. donto-memory may not top the `_s` accuracy table; this study is
> about understanding *where a memory layer earns its keep*, not about a trophy.

---

## 1. Why LongMemEval, and which variant

[LongMemEval](https://arxiv.org/abs/2410.10813) (ICLR 2025) tests long-term
memory of chat assistants over 500 questions across six abilities — single-session
(user/assistant/preference), multi-session, temporal-reasoning, knowledge-update —
plus an abstention set of unanswerable questions. It ships three variants:

- **`_oracle`** — the reader is given ONLY the evidence sessions. This isolates
  *reading* from *retrieval*; it is the ceiling, not a memory-system test.
- **`_s`** — each question's evidence is buried in a haystack of ~50 sessions
  (~120k tokens). This is the real test of a *memory system*: ingest everything,
  retrieve the right thing at query time.
- **`_m`** — ~500 sessions; the history exceeds a single context window.

We focus on **`_s`**, the standard memory-system benchmark. (We also report our
`_oracle` ceiling for reference.) `_m` is future work and is, as we'll argue, the
variant where a memory layer's accuracy case is actually strongest.

## 2. The system under test: donto-memory

donto-memory is an example consumer of **donto**, a bitemporal, paraconsistent,
evidence-first claim substrate. For this study the relevant machinery is:

- **Ingest** — each session is stored as an episodic chunk statement, anchored to
  a per-question holder (`user:lme_s_official:<qid>`) with the session's date
  injected as **valid-time** (`valid_from`). All ~50 haystack sessions are
  ingested; nothing is filtered by evidence.
- **Hybrid recall** — at query time, holder-scoped **lexical (FTS) + semantic
  (bge-small-en-v1.5, 384-dim, HNSW) retrieval, RRF-fused**, with predicate
  **alignment-closure** expansion. This is the same retrieval family as the
  current SOTA system (OMEGA): identical embedding model + FTS + fusion.
- **Bitemporal valid-time** — recalled rows carry `valid_from`/`valid_to`, so the
  reader can prefer the *latest* value of an attribute (the knowledge-update case).
- **MCP server** — donto-memory now exposes `donto_recall` / `donto_search` /
  `donto_memorize` over the Model Context Protocol, so an agent (codex, Claude
  Code) can use it as native tools and drive its own recalls. (Used for the
  agentic arm; the controlled pipeline below is the primary measurement.)

## 3. Faithfulness — no cheating, audited

A memory benchmark is trivially gameable (peek at the evidence labels), so we
audited every gold-field use line by line (`FAITHFULNESS_AUDIT.md`):

- **Ingest** renders sessions from `role`+`content` only; the per-turn
  `has_answer` flag is never emitted.
- **Recall** queries with the QUESTION and holder only — never
  `answer_session_ids` / `has_answer`.
- **Reader** sees only: the question, the question date, and the recalled
  memories. No gold answer, no evidence labels.
- **Judge** receives the gold answer — exactly as the official harness does, for
  grading only.
- The judge prompts, the `label = "yes" in response.lower()` rule, and the
  `"_abs"` abstention detection are **byte-identical to the official
  `evaluate_qa.py`**.

**The one disclosed deviation:** the official judge is the OpenAI **GPT-4o** API;
we use **codex gpt-5.4** (ChatGPT-Pro subscription) because there is zero
per-token API budget. Same prompts, same yes/no parsing, applied identically to
every instance and to both arms. The reader is likewise codex gpt-5.4. This makes
our setup **methodologically analogous to the leaderboard leader OMEGA**, which
uses GPT-4.1 as both reader and judge — and means our numbers are comparable to
OMEGA / HydraDB / Mastra, **not** to the paper's weaker GPT-4o-mini-class rows.

## 4. Methodology

- **Harness:** `run_official.py` — a resumable, checkpointed pipeline (ingest →
  embed → answer → metrics). Per-instance checkpointing; a crash never re-spends
  codex. Logs to `/opencode/logs`.
- **Recall budget:** bounded top-k (k=20) hybrid recall over the full ~50-session
  haystack — a real retrieval test, not the oracle hand-off.
- **Reader / Judge:** codex gpt-5.4, official prompts.
- **Baseline arm (`run_baseline.py`):** the same codex reader handed the ENTIRE
  date-sorted haystack (~120k tokens), NO donto retrieval — isolating exactly
  what the memory system contributes.

## 5. Results

### 5.1 Headline — full 500, `longmemeval_s`

> ⏳ **PLACEHOLDER — fill on completion of the full-500 run.**
> (per-type accuracy, task-averaged, overall, abstention, n_scored)

### 5.2 The comparison that matters — codex vs codex+donto (paired, n=48)

Same 48 instances, same reader, same judge; the only difference is whether codex
reads donto's retrieved top-20 or the entire haystack:

| ability | codex-alone (full-context) | codex+donto |
|---|---|---|
| knowledge-update | 0.875 | **1.000** |
| multi-session | 0.875 | 0.875 |
| single-session-assistant | 1.000 | 1.000 |
| single-session-user | 1.000 | 1.000 |
| temporal-reasoning | 1.000 | 1.000 |
| single-session-preference | **1.000** | 0.750 |
| **overall** | **0.957** | 0.936 |
| abstention | 0.667 | **1.000** |

- **Accuracy: ~tied** (0.957 vs 0.936 ≈ one instance on n=47).
- **Token cost: donto ~2.1× cheaper** — measured median reader prompt **124k tok
  (full-context) vs 58k tok (codex+donto)**.
- **donto wins knowledge-update** (1.0 vs 0.875) — bitemporal valid-time lets the
  reader pick the *latest* value instead of drowning in superseded history.
- **donto wins abstention** (1.0 vs 0.667) — evidence-first: when retrieval finds
  nothing, donto says "I don't know"; full-context hallucinates an answer to an
  unanswerable question.
- **donto loses preference** (0.75 vs 1.0) — its whole-session chunks miss the
  subtle, inferential preference session (see §6).

### 5.3 Oracle ceiling (reference)

Full 500 `_oracle` (codex reader+judge): **overall 0.946 / task-avg 0.935 /
abstention 0.967**.

## 6. Retrieval analysis — where donto's recall is strong and weak

A zero-cost offline sweep (recall only, no reader) measured whether the evidence
session lands in the top-k. The **hybrid vector arm is load-bearing**: turning it
on lifts overall **hit@10 from 0.85 (FTS-only) to 0.98**.

| ability | hit@5 FTS→hybrid | hit@10 FTS→hybrid |
|---|---|---|
| single-session-assistant | 0.62 → 1.00 | 0.88 → 1.00 |
| temporal-reasoning | 0.75 → 0.88 | 0.88 → 1.00 |
| single-session-preference | 0.12 → 0.50 | 0.38 → 0.88 |
| multi-session | 1.00 → 1.00 | 1.00 → 1.00 |
| knowledge-update | 1.00 → 1.00 | 1.00 → 1.00 |
| **overall** | **0.75 → 0.90** | **0.85 → 0.98** |

**single-session-preference** remains the weak spot. Preference questions ("what
should I cook for guests?") share almost no surface words with the session where
the preference was stated ("I'm vegan"); even semantically the link is
inferential. donto stores one chunk per session and embeds only the first ~300
tokens of a ~2,500-token session, so evidence buried mid-session is invisible to
the vector arm.

**A validated fix (future work):** finer chunking. Splitting sessions into
~1,000-char windows (each fully embedded) moved a known-missed preference instance
from miss → **hit@5**. It is, however, ~10× more chunks; at full `_s` scale that
is ~250k chunks, which on CPU-only bge-small (~3 chunk/s, no GPU) is ~17h —
infeasible on the current box without an int8/GPU embedder. So the full run uses
the proven whole-session config and we report finer chunking as a measured,
deferred improvement.

## 7. The competitive landscape — and a caution about leaderboards

Published `longmemeval_s` scores:

| system | overall | reader / judge | retrieval |
|---|---|---|---|
| OMEGA | 95.4% | GPT-4.1 (both) | bge-small + FTS + cross-encoder rerank + time-decay |
| Mastra | 94.87% | — | — |
| HydraDB | 90.79% | — | graph-native (entity/temporal/causal + BM25) |
| Memoria | 88.78% | — | — |
| Zep / Graphiti | 71.2% | — | — |
| paper, GPT-4o full-context | 60–64% | GPT-4o | none |

**The caution:** the LongMemEval score conflates the *reader* and the *memory
system*. The paper's GPT-4o full-context scores 60–64%, but **our codex gpt-5.4
full-context — no memory system at all — scored 95.7%.** Swap a stronger reader in
and the "memory system" numbers move with it. So a single overall number is not a
clean ranking of memory systems; the **codex-alone baseline is the control that
matters**, and the right questions are *"how much does the memory layer add over
giving the reader everything, and at what token cost, and does it scale past the
context window?"*

donto-memory shares OMEGA's retrieval family (identical bge-small + FTS + fusion)
but lacks OMEGA's **cross-encoder reranking** and **time-decay weighting** —
concrete, queued improvements that we will measure against this same benchmark.

## 8. What we actually learned

1. **On `_s`, a memory layer doesn't win on accuracy** when a strong reader can
   hold the whole history in context. Be honest about this.
2. **donto's defensible value is real but specific:** ~2× token efficiency, and
   wins on **knowledge-update (bitemporal)** and **abstention (evidence-first)** —
   the parts of its thesis that are load-bearing.
3. **The accuracy case for a memory layer lives on `_m`** (~500 sessions), where
   full-context is impossible and retrieval is mandatory. That is the next run.

## 9. Engineering notes (no-shortcuts log)

- **Embedding is the scaling bottleneck, not retrieval.** CPU-only bge-small does
  ~3 chunk/s; 25k chunks ≈ 2h. Throughput scales inversely with chunk length
  (400ch→21/s, 1500ch→6/s, 3000ch→2.7/s).
- **A real failure we caught and fixed:** the single long-lived embed process
  leaked to **9.7 GB RSS** and, colliding with live memory-worker traffic,
  swap-thrashed a 16 GB box to a near-total stall (~0 chunk/s while its own log
  claimed "3 stmt/s"). Fix: a **segmented** embed (fresh process per 4k chunks →
  bounded ~0.8 GB) at low priority. Lesson now standing policy: *a launched job is
  not a working job until you've watched the real metric move.*

## 10. Reproducibility

Harness, audit, and findings are in `/home/ajax/longmemeval-study/`:
`run_official.py` (resumable donto arm), `run_baseline.py` (codex full-context
arm), `retrieval_eval.py` (offline recall sweep), `FAITHFULNESS_AUDIT.md`,
`COMPETITIVE_LANDSCAPE.md`, `CHUNKING_FINDING.md`. Judge prompts are the verbatim
official `evaluate_qa.py`. No per-token API; codex via subscription.

*Honest caveats restated: codex gpt-5.4 reader+judge (not GPT-4o); `_s` results on
the config above; the full-500 headline table is filled on run completion; finer
chunking and the `_m` variant are measured/queued future work.*
