code wiki / _hdl_build / nx_doc_lexstat.nx
nx_doc_lexstat.nx source
↩ module page · 110 lines · 6806 B
1// nx_doc_lexstat.nx -- MEASUREMENT PROBE for the keyword-stuffing penalty design (2026-08-04, debt
2// 1785895889): for each <cid> argument, walk the stored doc EXACTLY as serve pass-1 does (same
3// tokenizer, same DSS_TFSCAN cap) and print the statistics the penalty would ride on: token count,
4// per-term tf for the probe terms, and max-term density (tf*1024/tokens). Read-only; no ranking change.
5// usage: nx_doc_lexstat <domain> <term1> <term2> <cid>...
6// license_tier: ORIGINAL
7import "nx_docportal_search_seg.nx"
8// ARM THE CRASH GUARD (2026-08-15). Without this the probe dies on SIGSEGV with ZERO output and the
9// caller sees an empty body -- indistinguishable from a dropped transport response, which is exactly
10// how this fault stayed unattributable for a day. nx_crash.nx turns the signal into a printed fault
11// address, and with a --debug build nx_addr2line turns that address into file:line.
12import "nx_crash.nx"
13
14func lx_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
15func lx_num(v: i64) -> i64 {
16 let bb: *u8 = sys_mmap(28); var m: i64 = v
17 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
18 let t: *u8 = sys_mmap(28); var k: i64 = 0
19 if m == 0 { t[0] = 48 as u8; k = 1 }
20 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
21 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
22 sys_write(1, bb, k); return 0
23}
24func lx_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } return v }
25
26func main(argc: i64, argv: *i64) -> i64 {
27 nx_crash_guard() // explicit: the IR-level auto-inject only fires when the driver armed the switch
28 if argc < 5 { lx_puts("usage: nx_doc_lexstat <domain> <term1> <term2> <cid>...\n" as *u8); return 1 }
29 let domain: *u8 = argv[1] as *u8
30 let prefix: *u8 = sys_mmap(512)
31 dss_prefix(domain, prefix)
32 // MUST BE ss_open2(prefix, 1), NOT ss_open (2026-08-15). ss_open is ss_open2(prefix, 0) = READ-ALL
33 // into anonymous memory. The live web shard's largest segment alone is 1.58 GB .docs + 490 MB .idx +
34 // 376 MB .pos, and there are 14 segments -- so read-all asks for multiple GB in a short-lived CLI and
35 // dies. MEASURED: this probe faulted on EVERY web cid (existing or absent) while `site` and `trusted`
36 // returned normally and live /search served 200 throughout, because the daemon opens via
37 // ss_open_cached on the mmap-backed path. nx_seg_store's own comment says it: "the big web shard uses
38 // this so the ~1.25M-doc RAM ceiling becomes disk-bound".
39 // ★★★★★★A PROBE THAT OPENS ITS SUBJECT DIFFERENTLY FROM THE SERVE PATH IS NOT MEASURING THE SERVE
40 // PATH -- and this file's own comment claimed it walked "the identical code path" while calling a
41 // different opener. The handle layout is identical either way, so every ss_hget/ss_term reader below
42 // is unchanged; only the backing differs.
43 // OPT-IN OPEN TRACE: this probe is the sanctioned place to measure the shard open, because it runs
44 // the SAME ss_open2 the daemon runs but in a CLI where a 10s cost bothers nobody. Production never
45 // sets this, so the trace costs one integer compare per phase there.
46 ss_open_trace(1)
47 let h: *i64 = ss_open2(prefix, 1)
48 if (h as i64) == 0 { lx_puts("FAIL: shard absent\n" as *u8); return 2 }
49 let tbl: *u8 = sys_mmap(272)
50 ss_tok_table(tbl)
51 let termptrs: *i64 = sys_mmap(2 * 8) as *i64
52 termptrs[0] = argv[2]
53 termptrs[1] = argv[3]
54 let tfout: *i64 = sys_mmap(2 * 8) as *i64
55 let dkey: *u8 = sys_mmap(64)
56 let dp: *i64 = sys_mmap(16) as *i64
57 let dl: *i64 = sys_mmap(16) as *i64
58 var a: i64 = 4
59 while a < argc {
60 let cid: i64 = lx_atoi(argv[a] as *u8)
61 dss_mkkey(cid, dkey)
62 if ss_hget(h, dkey, dp, dl) == 1 {
63 var dn: i64 = dl[0]
64 if dn > DSS_TFSCAN { dn = DSS_TFSCAN }
65 lx_puts("T0" as *u8)
66 let toks: i64 = dss_tf_all(dp[0] as *u8, dn, termptrs, 2, tbl, tfout)
67 lx_puts("T1" as *u8)
68 // PROXIMITY COVERAGE (2026-08-14). dss_prox_all is the OTHER per-candidate walk the serve
69 // path runs (nx_docportal_search_seg.nx:1679, immediately after the tf walk) and it had NO
70 // direct caller outside the daemon -- so it could only ever be exercised by deploying to the
71 // live search surface. That is exactly how it took /search down: a blind hoist of its seven
72 // per-call buffers faulted out-of-bounds on every query and had to be rolled back, and
73 // because it could not be tested in isolation the fault was not attributable.
74 // Called with the SAME arguments the serve path uses -- same dn (DSS_TFSCAN-capped), same
75 // termptrs, same tokenizer table -- so this probe walks the identical code path.
76 // EXTENDS THE EXISTING PROBE rather than adding a second one: nx_doc_lexstat already opens
77 // the shard, resolves cids and loops, and a parallel organ would be a duplicate ruler.
78 // R1i-b: no expansion in this probe, so onterms == nterms == 2 and the call is unchanged.
79 let prox: i64 = dss_prox_all(dp[0] as *u8, dn, termptrs, 2, tbl)
80 var maxtf: i64 = tfout[0]
81 if tfout[1] > maxtf { maxtf = tfout[1] }
82 var dens: i64 = 0
83 if toks > 0 { dens = (maxtf * DSS_MAGIC_1024) / toks }
84 lx_puts("cid=" as *u8); lx_num(cid)
85 lx_puts(" bytes=" as *u8); lx_num(dl[0])
86 lx_puts(" scanned=" as *u8); lx_num(dn)
87 lx_puts(" tokens=" as *u8); lx_num(toks)
88 lx_puts(" tf1=" as *u8); lx_num(tfout[0])
89 lx_puts(" tf2=" as *u8); lx_num(tfout[1])
90 lx_puts(" maxdens_q10=" as *u8); lx_num(dens)
91 lx_puts(" prox=" as *u8); lx_num(prox)
92 // R2d guard trace: the live dcnt/bign the serve path would use + the factor it computes
93 let bign: i64 = ss_doc_count(h)
94 let dc1: i64 = ss_term_dcount(h, termptrs[0] as *u8)
95 let dc2: i64 = ss_term_dcount(h, termptrs[1] as *u8)
96 var maxdc: i64 = dc1
97 var usetf: i64 = tfout[0]
98 if tfout[1] > usetf { usetf = tfout[1]; maxdc = dc2 }
99 lx_puts(" bign=" as *u8); lx_num(bign)
100 lx_puts(" dcnt1=" as *u8); lx_num(dc1)
101 lx_puts(" dcnt2=" as *u8); lx_num(dc2)
102 lx_puts(" factor=" as *u8); lx_num(dss_stuff_factor(usetf, toks, maxdc, bign))
103 lx_puts("\n" as *u8)
104 } else {
105 lx_puts("cid=" as *u8); lx_num(cid); lx_puts(" ABSENT\n" as *u8)
106 }
107 a = a + 1
108 }
109 return 0
110}