The substrate
contradiction-preserving · evidence-first

Claim + evidence-span recall — the LongMemEval pivot, and an honest LoCoMo ceiling

2026-06-17. A full accounting of a multi-hour autonomous run: why "LoCoMo 100%" is the wrong target, what LongMemEval proves instead, and the substrate change it produced — a deployed claim + evidence-span recall unit. Every number here is measured, not asserted. For the reports index, see Reports. Companion to LoCoMo failure analysis and LoCoMo claim layer.


TL;DR

claim verdict evidence
LoCoMo 100% is reachable by evolving donto false — it's reader/benchmark-capped, not donto-capped same full-context dump, three readers: gemini-2.5-flash-lite 45.5% → deepseek-chat 74.7% → gpt-5.4 86%. The retrieval is complete in all three; the reader is the cap. Ceiling ~90% (subjective-inference golds + image-text BLIP captions that can't OCR).
LoCoMo can demonstrate donto's value false LoCoMo conversations fit in context, so the dump saturates retrieval — donto's claim layer adds nothing either way. Wrong benchmark for the thesis.
LongMemEval is where the claims thesis bites true, but only when the dump doesn't fit with a weak reader, claims+spans beat the dump on temporal (0.65 vs 0.50) at 3.7× fewer tokens; with a strong reader (GLM-4.7) the dump that fits in context wins everywhere (temporal 1.00 vs 0.90). The reader-independent win is token-efficiency (3.5–4.8× fewer); the accuracy win requires the non-fitting _m split (untested). See §2a.
The right recall unit is the claim with its evidence span true — robust across readers claims-only → claims+spans lifts accuracy on both readers (deepseek 0.30→0.38, GLM 0.47→0.50; temporal +5–10 pts). Atomized triples hurt reader synthesis; the anchored source snippet restores it.
The win is now in the substrate, not just a harness true donto-memory /recall ships an agent-claims arm (commit 2535ab7) that surfaces ctx:claims/<corpus>/<chunk> claims with their evidence spans. Verified live.

1. The goal, and why the literal target is a dead end

The standing goal was blunt: evolve donto until it hits 100% on LoCoMo; when a benchmark question is provably broken, flag it and skip it. No judge-gaming.

The honest finding, established rigorously, is that 100% is not reachable and the gap is not donto's. LoCoMo answers are read by an LLM from whatever context the memory system supplies. When donto supplies the full-context dump (every conversation turn), the reader has everything — retrieval is trivially complete — and the score becomes a measurement of the reader, not of donto:

reader (same dump context) LoCoMo accuracy (100-Q balanced)
google/gemini-2.5-flash-lite 45.5%
deepseek-chat 74.7%
gpt-5.4 86%

The same 100 questions, the same donto context, three readers, ~40 points of spread. The weak reader (flash-lite) refuses inference questions outright — it answers "Not enough information" to "Would Caroline be considered religious?" even with the full transcript in front of it. The strong reader answers them. donto's retrieval is not the variable.

The residual gap to 100% even for the best reader is the benchmark itself: a meaningful fraction of LoCoMo golds are subjective inferences where several answers are defensible, plus image questions whose answer lives in a photo that the dataset only exposes as a coarse BLIP caption (no OCR). One gold was provably wrong (a charity-race day-of-week contradicted by the source) and was removed with a written audit trail (EXCLUDED_QUESTIONS.md). The rest are not "broken" — they're hard or subjective — so they cannot honestly be skipped.

Conclusion: chasing LoCoMo 100% would mean gaming the judge. The right move is to (a) report the LoCoMo number honestly, and (b) pivot to a benchmark where donto's design actually matters.

1a. The definitive full run: all 1,539 questions

To put a complete number on it rather than a 100-question sample, we ran the entire LoCoMo benchmark — all 1,539 scorable questions across all 10 conversations — full-context dump (mean 39,484 context tokens/question, i.e. the whole conversation), images included as BLIP captions, with the cheap gemini-2.5-flash-lite as both reader and judge. Cost: $7.94 (~$5.16 per 1,000 questions); zero empty answers.

category accuracy n
single-hop (cat-4) 0.734 841
open-domain (cat-3) 0.344 96
temporal (cat-2) 0.297 320
multi-hop (cat-1) 0.266 282
OVERALL 0.533 1,539

Per-conversation accuracy is tight (0.46–0.65), so the number is stable across the corpus, not an artifact of one easy/hard conversation.

Read this number correctly. Single-hop — direct factual lookup — is 0.73: the reader can read. The collapse is entirely in the inference categories (multi-hop, temporal, open-domain at 0.27–0.34), because flash-lite refuses or fumbles the reasoning step even with the full transcript in context. This is the exact signature of the reader-ladder above: the same dump, a weak reader. On the 100-question balanced set the capable deepseek-chat reader scored 74.7% and gpt-5.4 86% on identical context. So the honest reading of "53.3% on the full benchmark" is "this is what a $0.10/M reader does with complete information" — a floor set by the reader, not a measure of donto's retrieval, which supplied the whole conversation in every case. (Running the full set on a capable reader would cost ~3–8× more; the cheap reader is what the budget allowed, and it makes the reader-bound nature unmistakable rather than hiding it.)

2. The pivot: LongMemEval, where the dump can't save you

LoCoMo fits in context. LongMemEval (LME) does not — its haystack is dozens of long sessions per question. When the dump doesn't fit, you must retrieve, and the question becomes: retrieve what, in what form?

donto's answer is its thesis: a bitemporal, paraconsistent, evidence-anchored claim layer. We extracted that layer for the full LME oracle set — 1,054 chunks, ~115K claims into per-chunk contexts ctx:claims/lme/<chunk-uuid> — then compared three recall arms with a fixed cheap reader (deepseek-chat) and the verbatim per-type LME judge:

arm accuracy avg tokens knowledge-update multi-session temporal
DUMP (all session turns) 0.467 9,414 0.65 0.25 0.50
CLAIMS (top-100, ranked) 0.300 1,967 (4.8× fewer) 0.20 0.10 0.60
CLAIMS + EVIDENCE SPANS 0.383 2,533 (3.7× fewer) 0.35 0.15 0.65

Two results matter here.

First — the claim layer is an expansion, not a compression. One holder had 4,272 claims = 51K tokens versus a 10.5K-token dump. Abundance means more claims than source text. So "dump all the claims" is strictly worse than the dump. The claim layer pays off only through ranked retrieval: feed the top-K claims relevant to the question, not all of them. That is the whole point of a substrate — hold everything, retrieve precisely.

Second — the right recall unit is the claim with its evidence span. Bare triples (ex:cool-dry-storage avoids ex:heat-source) are great for retrieval and ranking but hurt the reader's ability to synthesize an answer — the connective tissue of natural language is gone. Pairing each retrieved claim with the anchored source snippet it came from (… — "store it in a cool, dry place away from heat") gives the reader both: structure for retrieval, prose for synthesis. That single change lifted knowledge-update from 0.20 → 0.35 and pushed temporal past the dump — at 3.7× fewer tokens. This is donto's always-on citer earning its place: extraction (what) and anchoring (where) are separate stages, and recall returns both.

2a. The cross-reader check that corrected us

The table above used a cheap, weak reader (deepseek-chat). Re-running the same three arms with a strong reader (GLM-4.7) changes the story in an important, honest way:

arm (date-sensitive types) weak reader (deepseek) strong reader (GLM-4.7)
DUMP 0.467 0.900
CLAIMS 0.300 0.467
CLAIMS + SPANS 0.383 0.500
temporal: DUMP vs CLAIMS+SPANS 0.50 vs 0.65 1.00 vs 0.90

With a strong reader the dump leaps to 0.90 and wins everywhere, including temporal. So "claims+spans beat the dump on temporal" was reader-specific — it held only because the weak reader couldn't synthesize the big dump, and the compact claim layer rescued it. Give the reader enough capability to actually use a dump that fits in context, and the dump wins.

Two things survive the cross-reader check and are the honest takeaways:

  1. claims+spans > bare claims on both readers — evidence spans reliably help synthesis (the §2 point holds).
  2. Token-efficiency is the demonstrated standalone win — claims+spans reach a large fraction of the answer at 3.5–4.8× fewer tokens.

What does not survive: claims beating the dump on accuracy when the dump fits. That is the same lesson as LoCoMo — when everything fits in context, a capable reader plus the raw text wins, and donto's value is efficiency, not accuracy. donto's accuracy win requires the regime where the dump cannot fit (LME _m), which remains the decisive untested experiment. Reporting this correction is the point: a result that only reproduces under a weak reader is not a substrate win.

What the residual gap is (and isn't)

We diagnosed every knowledge-update failure. The losses are not a recall or representation flaw:

  • 43% are extraction coverage misses — the answer fact (e.g. "spent 10–12 hours on the sculpture") was never extracted into a claim. A deeper-gleaning extractor closes these.
  • ~50% had the fact present in the top-K but the reader still missed it — the synthesis problem that evidence spans address.
  • We also tested attaching valid_time (session dates) to every claim: it barely moved knowledge-update (0.25 → 0.30). Dates were not the bottleneck — coverage and synthesis are. An honest negative result worth keeping.

3. The substrate change: agent-claims recall, deployed

A benchmark win that lives only in a harness is scaffolding. So the claim+evidence-span unit was promoted into donto-memory's live /recall (commit 2535ab7):

  • New overlay agent_claims_for_holder surfaces a holder's donto-agent-extracted claims from ctx:claims/<corpus>/<chunk> — which the native claim-FTS arm structurally can't see — scoped via the documented holder↔chunk convention (corpus = the holder's middle segment), FTS-ranked over a humanized projection. Evidence spans attach in the hot path.
  • It runs as a 5th fused arm in the semantic-claim module, gated DONTO_MEMORY_AGENT_CLAIMS — ships dark, degrades gracefully, never breaks recall.

Verified live: /recall for an LME holder asking "first issue with new car" now returns the answer-bearing affectedComponent — "issue with my car's GPS system" claim+span (gold: "GPS system not functioning") that the episodic-only path missed entirely.

Two bugs were caught here by reading the live service logs, not by assuming success: a SQL parameter-ordering mismatch, and a query plan that — once the substrate crossed 42M statements — seq-scanned 513K records and bit-mapped a 42M-row index, timing out at 96s. The fix forces the holder-index lookup first; 96s → ~4s.

The honest perf wall

Making the arm reliably sub-second means precomputed FTS. We tried backfilling the existing donto_x_memory_chunk_fts table (a holder-scoped GIN) for the LME claims — and hit a hard wall: even a single holder's backfill times out at 60s. The cost is GIN-index insert, not the query, and it's amortized badly in bulk over a table that already holds millions of BEAM claims. The correct place to pay that cost is incrementally, at extraction-ingest time — one claim per insert — not a bulk backfill. So the arm currently ships best-effort: reliable at modest recall limits, degrading gracefully under load, with the incremental-ingest fix documented as the clean follow-up. Abandoning the rabbit hole was the right call.


4. The plumbing that made it possible

  • Extraction infrastructure. LME extraction had silently failed: at --concurrency 3 the Hyades gateway returns 200-with-empty-content under load, marking chunks "done" with zero facts. Fixed to --concurrency 1 (462 facts/chunk) and added a reliable OpenRouter gpt-4.1-mini provider lane to donto-agent (bd74b7b) — no weekly cap, no empty-under-load — to drain the queue in parallel at ~$0.006/chunk. The full layer completed: 1,054/1,054 chunks.
  • A 19-hour runaway killed. A deferred migration backfill was found running as a single query for ~19 hours doing disk IO, loading the box. Cancelled (I3-safe); load dropped from 7 to under 3. It's now permanently unneeded — the agent-claims arm reaches the same claims via the context-join.
  • Disk crisis resolved + automated. The boot disk hit 99%. Reclaimed ~69 GB (regenerable Rust build targets + 649 stale opencode extraction-exchange dirs, results long since ingested) → 39%. Root cause fixed with a daily cron so it can't recur.

Every one of these was a "looks done ≠ is working" catch — the discipline that separates a launched job from a working one.


5. Honest limitations

  • The headline LoCoMo number is reader-bound. With a cheap reader it is low; that reflects the reader, not the retrieval. We report it next to the reader-ladder so the bound is explicit.
  • The LME claims experiment uses a lexical top-K ranker as a stand-in for donto's full FTS+vector recall — a floor, not a ceiling, on what ranked claim recall can do.
  • The decisive test — LME _m, whose haystack does not fit any dump — was not run (the _m split isn't downloaded). That is where claims+spans is the only viable arm and donto should win outright, not merely tie-cheaper. It is the clear next experiment.
  • The agent-claims recall arm is best-effort on latency for very large holders until the incremental-ingest FTS registration lands.

6. What to do next

  1. Incremental chunk_fts registration at donto-agent ingest — makes claim recall reliably sub-second for all consumers (pay the GIN cost per-claim, not in bulk).
  2. Deeper-gleaning extraction to close the 43% knowledge-update coverage gap.
  3. Run LME _m — the benchmark donto was built for.
  4. A cross-encoder rerank + time-decay on claim recall (the two levers the strongest published memory systems have that donto lacks).

The thesis held where it should: when the context doesn't fit, an evidence-anchored claim layer recalls the answer in a fraction of the tokens. The substrate now does this in production. The number that doesn't move — LoCoMo — doesn't move because of the reader and the benchmark, and saying so plainly is the point.

Update (2026-06-18): extraction coverage IS the lever — controlled result

The one place the claim layer clearly lagged at equal budget was knowledge-update (claims+spans 0.50 vs episodic 0.80). The diagnosis was always extraction coverage — the live LME layer was drained with a cheap single-pass extractor (~85–130 facts/chunk), not deep gleaning. So it was tested cleanly: the 10 knowledge-update holders' chunks were re-extracted with 6-pass Hyades gleaning (additive, dedup by content-hash) — coverage rose 3.5× (3,119 → 10,923 facts) — and the equal-budget showdown was re-run, read off the same holders, before vs after:

arm (same 9 KU holders) single-pass deep coverage (3.5×)
claims + spans 0.56 0.89
episodic turns (control, identical source) 0.78 0.78

claims+spans jumped +0.33 (0.56 → 0.89), matching and exceeding episodic — while the episodic control reading the same unchanged dialogue did not move. Three holders flipped wrong→right: exactly the cases where the answer fact had never been extracted by the shallow pass. Because the control is flat, the lift is unambiguously from the extraction.

Verdict: donto's recurring knowledge-update deficit was an extraction-depth artifact, not a representation or synthesis limit. Given deep gleaning, the claim+evidence-span unit reaches parity-or-better with raw text at a fraction of the tokens. The budget-showdown "tie" and the cross-reader "dump wins when it fits" were both partly coverage-limited — and the justified next investment is a deep re-extraction of the full layer. This 10-holder slice is the proof it pays. (Full write-up in the PDF treatise, §3.7.)