Keywords are already fast and accurate
For a single word, a hit means the index really contains it, not a computed similarity; multiple words are OR-ed, deliberately returning a few extra rows for the model to filter.
0.02–1.1 ms per query
This page describes no features. It answers one question: why believe any of it. Every number below comes from a real measured comparison; every overturned design once looked obviously right.
This is the ledger of negative results left by the predecessor project, and the reason this one was started. Every row here was once an obviously correct design.
The twelve split into three questions: 4 ask whether a score is enough, 2 ask whether merging can be automatic, 6 ask whether the capability actually runs.
| # | Original design | Measured reading | What it became |
|---|---|---|---|
| Scores instead of judgement (4) | |||
| 01 | Model self-report: 192 of 285 pile up at ≥0.9; the whole store has only 8 distinct values | Worthless; filtering by score filters nothing | |
| 02 | Five embedding models all fail to separate right from wrong; the distributions overlap | Not used for verdicts; recall starts with keywords | |
| 03 | Blocks 6/6 unwanted rows, but also 3/4 of the ones you wanted | No tuning separated good from bad; dropped | |
| 04 | Of 6 wrong rows, 3 were judged right | Corroborate at the entity level instead | |
| Can merging be automatic (2) | |||
| 05 | Coverage 8.7% (9/103); loosening it produces false merges | No duplicate writes + coarse candidate screen | |
| 06 | Entities written by more than one source: 0 rows | Record conflicts, never overwrite | |
| Does the capability actually run (6) | |||
| 07 | 10 real queries should have hit 22 times; actual hits: 0 times, with no error | Per-word normalisation + a shadow column | |
| 08 | Nested transactions made every write fail silently while progress advanced | Re-deliverable, idempotent writes | |
| 09 | The three spatial tables do not exist in the store; located_at appears 0 times | Existed only on paper; cut | |
| 10 | Each cycle's window is filled by pins and archives; 0 rows processed | Decorative; cut | |
| 11 | The attention table has no human-facing reader; it stops at stored | If nobody sees it, it is not delivered | |
| 12 | MCP has no push channel; the system can only wait to be asked | History was never handed over unprompted | |
Row 02 is about verdicts; this is about recall. Three reasons, in order of weight.
For a single word, a hit means the index really contains it, not a computed similarity; multiple words are OR-ed, deliberately returning a few extra rows for the model to filter.
A resident embedding model takes memory and CPU; we have not measured a hosted embedding service either, and unmeasured pieces do not go on the main path.
Another index to sync, invalidate and rebuild; keywords cover the current scenarios. If it is ever needed, it becomes an optional interface with its own table, without touching the core.
How it was measured: the local hub's memory.db opened read-only, rc_shadow 1,045 rows, 2026-11-09; SQLite FTS5 MATCH, excluding network and result assembly. Multiple words are OR-ed, not AND-ed: recall runs wide, and the model and the gate narrow it. Also, keyword search works after ingest without an LLM; what is missing is extraction, not lookup.
The constraints are the flip side of the negative results. They are not a vision; they are a gate every merge must pass.
The old form asserted only ok == true; the new form asserts how many rows were actually written. The difference is not style: the former is always true when nothing was written.
// returned Ok, but wrote no rows let report = cycle_once(&db)?; assert!(report.ok); // transaction not reentrant + reads on a separate pool // ⇒ every write failed silently, the watermark advanced anyway // ⇒ this test stayed green for three releases
// assert the real row delta and watermark let before = count_rows(&db)?; cycle_once(&db)?; let after = count_rows(&db)?; assert_eq!(after - before, 3); assert!(watermark(&db)? > before_wm);
Writing down what has run and what has not keeps the design notes from being read as live data.