code wiki / _hdl_build / nx_memfind.nx
nx_memfind.nx
buildroot/runtime/_hdl_build/nx_memfind.nx
about
nx_memfind.nx -- BM25 ranked retrieval over the laptop-local memory corpus.
CONVERTED TO nx_memplane_lib 2026-08-06. It now owns only the ranking: term counting, document
length, the integer log, the stopword list and query derivation. The walk, name table, read, join
and dump list come from the lib.
WHY BM25, AND WHAT IT REPLACED (measured -- three rankers, one test)
-------------------------------------------------------------------
Test: reconstruct the ORIGINAL description of a memory that duplicates an existing one (the store
law, written twice by two sessions three weeks apart) and ask whether the ranker surfaces the file
it duplicates.
rarity-weighted presence -> TARGET NOT FOUND
conjunction (fraction present) -> TARGET RANK 22 of 2173
BM25 -> TARGET RANK 1 of 2173
Both hand-rolled attempts failed for the SAME reason, solved in retrieval decades ago:
* RARITY fails because this is a SINGLE-PROJECT corpus -- the words that identify a doctrine are
the words the whole project uses (sovereign 1190/2176, data 1138, files 808, store 697,
tsv 442). Dropping "common" terms deletes the topic and keeps the incidental.
* CONJUNCTION fails because the biggest file contains every word; a 255 KB roadmap won a query
about flat-file policy purely by being long.
★★★★★★ BOTH ARE PRESENCE MEASURES, AND PRESENCE IN A LONG DOCUMENT IS NEARLY FREE.
BM25 fixes exactly that pair: term-frequency SATURATION (k1) and LENGTH NORMALISATION (b).
★ WHEN A HAND-ROLLED RANKER KEEPS FAILING, THE FIELD'S BASELINE IS THE KNOWN GOOD YOU WERE
SUPPOSED TO MATCH FIRST.
Integer notes: idf uses log2 rather than ln, which scales every term identically and so leaves the
RANKING unchanged; the BM25 idf ratio simplifies to (2N+2)/(2df+1). Fixed point 1/256 for logs.
TWO ENTRY POINTS, ONE RANKER:
nx_memfind <term> ... -- a human picks the query (interactive retrieval)
nx_memfind --file <path> -- the query is DERIVED from a file's own name+description
⚠ per-term df is printed deliberately: a rewrite once dropped it silently and the gate caught it.
★ A REWRITE THAT PRESERVES THE OUTPUT BUT DROPS THE EVIDENCE FOR IT IS A REGRESSION.
usage: nx_memfind [--dir D] [--n K] [--quiet] (--file <path> | <term> [<term> ...])
Sovereign: imports nx_memplane_lib. license_tier: ORIGINAL
dependencies 1 imports · 0 importers
imports: nx_memplane_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 41 | const MS_DIR: *u8 = "/mnt/c/Users/elder/.claude/projects/C--Users-elder/memory" |
| 42 | const MS_FBUF: i64 = 1048576 |
| 43 | const MS_MSG: i64 = 65536 |
| 44 | const MS_MAXT: i64 = 40 |
| 45 | const MS_TLEN: i64 = 64 |
| 46 | const MS_MINW: i64 = 3 |
| 47 | const MS_DESC: i64 = 120 |
functions
| 49 | func ms_termptr(terms: *u8, i: i64) -> *u8 { return ((terms as i64) + i * MS_TLEN) as *u8 } |
| 51 | func ms_lower(c: u8) -> i64 |
| 58 | func ms_count(buf: *u8, n: i64, pat: *u8) -> i64 |
| 82 | func ms_doclen(buf: *u8, n: i64) -> i64 |
| 99 | func ms_log2fx(v: i64) -> i64 called by 1: main |
| 111 | func ms_isstop(w: *u8) -> i64 |
| 137 | func ms_addterm(terms: *u8, nt: i64, src: *u8, s: i64, wl: i64) -> i64 |
| 154 | func main(argc: i64, argv: *i64) -> i64 |