code wiki / _hdl_build / nx_capsearch.nx
nx_capsearch.nx source
↩ module page · 431 lines · 19480 B
1// nx_capsearch.nx -- CAPABILITY SEARCH (2026-08-06). "What tool does X?" answered from the estate's OWN
2// registry, server-side, so an AGENT or a script can ask it. This is the organ the ecosystem was missing:
3// grounded capability retrieval EXISTED at /code/ask but was browser-only client-side JS plus a local
4// llama-server on the operator's laptop, so no agent and no headless caller could ever reach it, while
5// /api/search has scopes site|web|trusted and NO capability scope -- querying it for `nx_page_verify`,
6// the literal name of one of our own 869 tools, returns arxiv.org and kernel.org.
7//
8// THE RANKER IS NOT INVENTED. It is /code/ask's own `tooltop` (stem-hit on the name + word-boundary hit
9// on the title) plus three GENERIC improvements, each measured against a HELD-OUT gold set before being
10// kept: nx_common_tasks' 45 team-authored task->canonical-organ rows, which predate this organ. The task
11// labels were deliberately NOT folded in as aliases -- that would score ~1000 and mean nothing.
12// known good (/code/ask tooltop) : hit@1 400 hit@5 640 hit@10 720 MRR 482 (permil)
13// + call-grammar field + idf : hit@1 400 hit@5 640 hit@10 720 MRR 507
14// + morphological prefix match : hit@1 520 hit@5 760 hit@10 800 MRR 620 <- shipped
15// +120 permil hit@1 (+30% rel), +138 MRR (+29% rel) over the known good.
16//
17// CORPUS = knowledge/tool_schemas.conf (name + title) joined to knowledge/tool_grammar.conf (the derived
18// argv contract from nx_toolgrammar) PLUS, since 2026-08-24, knowledge/status/libindex.tsv -- THE LIBRARY
19// SURFACE. This organ ranked REGISTERED TOOLS ONLY, and a library has no registration row by design, so
20// every library in the estate was invisible to the one instrument CLAUDE.md orders run before building
21// anything. Measured cost the day it was fixed: two FALSE ABSENT rows on one compare board (a LIVE
22// constraint solver and a complete Mei-2007 erosion pipeline that a sibling board had called
23// "catalogue-proven absent" for a week), six private isqrt copies written beside each other, and a
24// capsearch zero read as corroboration of absence. The lib corpus is emitted by nx_libindex (PG1) from
25// cl_is_lib, the same classifier the adoption ladder uses, so this organ and the board cannot disagree
26// about what a library is. Every result now carries its SURFACE ("tool" is MCP-callable, "lib" is
27// import-only) so a lib hit is never mistaken for something a caller can invoke.
28// Both corpora are read fresh per call, so a re-harvest is LIVE.
29// INTEGER-ONLY scoring (no floats anywhere in this estate): weights are scaled by CS_SCALE.
30// HONEST: a query whose tokens match nothing returns an EMPTY result list -- it never falls back to
31// "here are some tools anyway". No evidence, no answer. And the corpus cap is ANNOUNCED: rows beyond it
32// are COUNTED as dropped and corpus_complete flips to 0, never silently ranked as if they did not exist.
33// nx_capsearch <query words...> -> JSON {query, considered, tools, libs, dropped, corpus_complete,
34// results:[{tool,surface,score,what,call}]}
35// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
36import "nx_syscalls.nx"
37import "nx_store_seed_lib.nx"
38const CS_MAGIC_1024: i64 = 1024
39
40const CS_SCHEMAS: *u8 = "knowledge/tool_schemas.conf" as *u8
41const CS_GRAMMAR: *u8 = "knowledge/tool_grammar.conf" as *u8
42// The library surface: a SOVEREIGN ROW PLANE seeded by nx_libindex (never a .tsv file in the plane -- that
43// shape was retired 2026-08-13 and the first cut of this join repeated it for a day). Read through
44// sts_load_fit, which returns the whole plane or REFUSES; it never hands back a partial. ABSENT is legal
45// (the plane is seeded by nx_libindex on its own beat) and is reported as libs=0 corpus_complete=0 rather
46// than refused, so the tool surface still answers.
47const CS_LIBIDX: *u8 = "knowledge/store/libindex-" as *u8
48// sized: the schema conf is ~125KB today; refuse-on-full rather than rank a truncated corpus
49const CS_FCAP: i64 = 1 << 21
50// The row ceiling. Measured 2026-08-24: registry 1,309 tools; the library surface adds every lib in both
51// runtime roots. Raised from 1,600 (which the lib corpus alone would have bound) and made ANNOUNCING:
52// a bind is counted in `dropped` and forces corpus_complete=0. A silent cap was the defect class this
53// whole change exists to close.
54const CS_MAXTOOLS: i64 = 8192
55// declared: query tokens beyond this are ignored (declared, never silent)
56const CS_MAXTOK: i64 = 12
57// sized: a query token longer than this cannot match anything useful
58const CS_TOKCAP: i64 = 64
59// results returned
60const CS_TOPN: i64 = 8
61// idf-ish damping numerator, integer-scaled: w = CS_SCALE / (3 + df)
62const CS_SCALE: i64 = 1000000
63// morphological match: two words sharing this many leading chars are the same term (mine~miner)
64const CS_MORPH: i64 = 4
65// a token shorter than this is noise
66const CS_MINTOK: i64 = 3
67// surface ids per row: which buffer the row's offsets index
68const CS_SURF_TOOL: i64 = 0
69const CS_SURF_LIB: i64 = 1
70
71func csw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
72func csn(v: i64) -> i64 {
73 let b: *u8 = sys_mmap(32)
74 var x: i64 = v
75 var i: i64 = 31
76 if x == 0 { b[i] = 48 as u8; i = i - 1 }
77 while x > 0 { b[i] = ((48 + (x - ((x / 10) * 10))) as u8); x = x / 10; i = i - 1 }
78 sys_write(1, ((b as i64) + i + 1) as *u8, 31 - i)
79 return 0
80}
81// JSON-escape n bytes of buf to stdout (quote, backslash, controls)
82func csj(buf: *u8, off: i64, n: i64) -> i64 {
83 let o: *u8 = sys_mmap(8)
84 var i: i64 = 0
85 while i < n {
86 let c: i64 = buf[off + i] as i64
87 if c == 34 { csw("\\\"" as *u8) } else {
88 if c == 92 { csw("\\\\" as *u8) } else {
89 if c < 32 { csw(" " as *u8) } else { o[0] = c as u8; sys_write(1, o, 1) }
90 }
91 }
92 i = i + 1
93 }
94 return 0
95}
96func cs_lower(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
97// [a-z0-9_] after lowering
98func cs_isw(c: i64) -> i64 {
99 let l: i64 = cs_lower(c)
100 if l >= 97 { if l <= 122 { return 1 } }
101 if l >= 48 { if l <= 57 { return 1 } }
102 if l == 95 { return 1 }
103 return 0
104}
105// idx-th TAB field of buf[ls,le) -> out2=(off,len); 1 present / 0 absent
106func cs_field(buf: *u8, ls: i64, le: i64, idx: i64, out2: *i64) -> i64 {
107 var fi: i64 = 0
108 var s: i64 = ls
109 var i: i64 = ls
110 while i <= le {
111 var sep: i64 = 0
112 if i == le { sep = 1 } else { if buf[i] == (9 as u8) { sep = 1 } }
113 if sep == 1 {
114 if fi == idx { out2[0] = s; out2[1] = i - s; return 1 }
115 fi = fi + 1
116 s = i + 1
117 }
118 i = i + 1
119 }
120 return 0
121}
122// do buf[a,alen) and tok share >= CS_MORPH leading chars (or are equal)? case-insensitive on buf side.
123func cs_morph(buf: *u8, a: i64, alen: i64, tok: *u8, tlen: i64) -> i64 {
124 var n: i64 = alen
125 if tlen < n { n = tlen }
126 if alen == tlen {
127 var m: i64 = 1
128 var i: i64 = 0
129 while i < alen { if cs_lower(buf[a+i] as i64) != (tok[i] as i64) { m = 0; i = alen } else { i = i + 1 } }
130 if m == 1 { return 2 }
131 }
132 if n < CS_MORPH { return 0 }
133 var j: i64 = 0
134 while j < n { if cs_lower(buf[a+j] as i64) != (tok[j] as i64) { return 0 } j = j + 1 }
135 return 1
136}
137// name hit: whole name == tok -> 3 ; an '_'-separated part == tok -> 2 ; a part shares a prefix -> 1
138func cs_namehit(buf: *u8, off: i64, len: i64, tok: *u8, tlen: i64) -> i64 {
139 if cs_morph(buf, off, len, tok, tlen) == 2 { return 3 }
140 var best: i64 = 0
141 var s: i64 = off
142 var i: i64 = off
143 while i <= off + len {
144 var sep: i64 = 0
145 if i == off + len { sep = 1 } else { if buf[i] == (95 as u8) { sep = 1 } }
146 if sep == 1 {
147 if i > s {
148 let m: i64 = cs_morph(buf, s, i - s, tok, tlen)
149 if m == 2 { if best < 2 { best = 2 } }
150 if m == 1 { if best < 1 { best = 1 } }
151 }
152 s = i + 1
153 }
154 i = i + 1
155 }
156 return best
157}
158// text hit: a whole word == tok -> 2 ; a word shares a prefix -> 1 ; else 0
159func cs_texthit(buf: *u8, off: i64, len: i64, tok: *u8, tlen: i64) -> i64 {
160 var best: i64 = 0
161 var s: i64 = 0 - 1
162 var i: i64 = off
163 while i <= off + len {
164 var isw: i64 = 0
165 if i < off + len { isw = cs_isw(buf[i] as i64) }
166 if isw == 1 {
167 if s < 0 { s = i }
168 } else {
169 if s >= 0 {
170 let m: i64 = cs_morph(buf, s, i - s, tok, tlen)
171 if m == 2 { if best < 2 { best = 2 } }
172 if m == 1 { if best < 1 { best = 1 } }
173 s = 0 - 1
174 }
175 }
176 i = i + 1
177 }
178 return best
179}
180
181// The buffer a row's offsets index, by surface. One place, so no consumer picks the wrong one.
182func cs_buf(surf: i64, sb: *u8, lb: *u8) -> *u8 { if surf == CS_SURF_LIB { return lb } return sb }
183
184func main(argc: i64, argv: *i64) -> i64 {
185 if argc < 2 {
186 csw("{\"organ\":\"nx_capsearch\",\"refused\":\"no query -- usage: nx_capsearch <query words...>\"}\n" as *u8)
187 sys_exit(2)
188 return 2
189 }
190 // ---- corpus: the tool surface ----
191 let szp: *i64 = sys_mmap(16) as *i64
192 let sb: *u8 = sys_read_file(CS_SCHEMAS, szp)
193 if (sb as i64) == 0 {
194 csw("{\"organ\":\"nx_capsearch\",\"refused\":\"knowledge/tool_schemas.conf unreadable\"}\n" as *u8)
195 sys_exit(2)
196 return 2
197 }
198 let sn: i64 = szp[0]
199 let gzp: *i64 = sys_mmap(16) as *i64
200 var gb: *u8 = sys_read_file(CS_GRAMMAR, gzp)
201 var gn: i64 = 0
202 if (gb as i64) != 0 { gn = gzp[0] }
203 // ---- corpus: the library surface, a row plane read whole-or-refused (absent is reported, never refused) ----
204 let lzp: *i64 = sys_mmap(16) as *i64
205 var lb: *u8 = sts_load_fit(CS_LIBIDX, lzp)
206 var ln: i64 = 0
207 var lib_present: i64 = 0
208 if (lb as i64) != 0 { ln = lzp[0]; if ln > 0 { lib_present = 1 } }
209 if lib_present == 0 { lb = sb } // a valid buffer to index; no lib row will ever point into it
210
211 let nm_o: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
212 let nm_l: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
213 let ti_o: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
214 let ti_l: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
215 let gr_o: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
216 let gr_l: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
217 let surf: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
218 var nt: i64 = 0
219 var ntools: i64 = 0
220 var nlibs: i64 = 0
221 var dropped: i64 = 0
222 let f2: *i64 = sys_mmap(16) as *i64
223 var ls: i64 = 0
224 var i: i64 = 0
225 while i <= sn {
226 var eol: i64 = 0
227 if i == sn { eol = 1 } else { if sb[i] == (10 as u8) { eol = 1 } }
228 if eol == 1 {
229 if i > ls { if sb[ls] != (35 as u8) {
230 if cs_field(sb, ls, i, 0, f2) == 1 {
231 let no: i64 = f2[0]
232 let nl: i64 = f2[1]
233 if nl > 0 {
234 if nt < CS_MAXTOOLS {
235 nm_o[nt] = no
236 nm_l[nt] = nl
237 ti_o[nt] = 0
238 ti_l[nt] = 0
239 if cs_field(sb, ls, i, 1, f2) == 1 { ti_o[nt] = f2[0]; ti_l[nt] = f2[1] }
240 gr_o[nt] = 0
241 gr_l[nt] = 0
242 surf[nt] = CS_SURF_TOOL
243 nt = nt + 1
244 ntools = ntools + 1
245 } else { dropped = dropped + 1 }
246 }
247 }
248 } }
249 ls = i + 1
250 }
251 i = i + 1
252 }
253 // join the derived grammar by name (tool rows only -- a lib has no argv contract)
254 if gn > 0 {
255 var gls: i64 = 0
256 var gi: i64 = 0
257 while gi <= gn {
258 var geol: i64 = 0
259 if gi == gn { geol = 1 } else { if gb[gi] == (10 as u8) { geol = 1 } }
260 if geol == 1 {
261 if gi > gls { if gb[gls] != (35 as u8) {
262 if cs_field(gb, gls, gi, 0, f2) == 1 {
263 let gno: i64 = f2[0]
264 let gnl: i64 = f2[1]
265 if cs_field(gb, gls, gi, 1, f2) == 1 {
266 let gvo: i64 = f2[0]
267 let gvl: i64 = f2[1]
268 var k: i64 = 0
269 while k < nt {
270 if nm_l[k] == gnl {
271 var m: i64 = 1
272 var c: i64 = 0
273 while c < gnl { if sb[nm_o[k]+c] != gb[gno+c] { m = 0; c = gnl } else { c = c + 1 } }
274 if m == 1 { gr_o[k] = gvo; gr_l[k] = gvl; k = nt } else { k = k + 1 }
275 } else { k = k + 1 }
276 }
277 }
278 }
279 } }
280 gls = gi + 1
281 }
282 gi = gi + 1
283 }
284 }
285 // library rows: name<TAB>path<TAB>title<TAB> sym1 sym2 ... -- the searchable text is everything from
286 // the title onward, so a query for a SYMBOL finds the lib that declares it. The path field is skipped:
287 // a directory name would match every query containing "runtime".
288 if lib_present == 1 {
289 var lls: i64 = 0
290 var li: i64 = 0
291 while li <= ln {
292 var leol: i64 = 0
293 if li == ln { leol = 1 } else { if lb[li] == (10 as u8) { leol = 1 } }
294 if leol == 1 {
295 if li > lls { if lb[lls] != (35 as u8) {
296 if cs_field(lb, lls, li, 0, f2) == 1 {
297 let lno: i64 = f2[0]
298 let lnl: i64 = f2[1]
299 if lnl > 0 {
300 if nt < CS_MAXTOOLS {
301 nm_o[nt] = lno
302 nm_l[nt] = lnl
303 ti_o[nt] = 0
304 ti_l[nt] = 0
305 if cs_field(lb, lls, li, 2, f2) == 1 { ti_o[nt] = f2[0]; ti_l[nt] = li - f2[0] }
306 gr_o[nt] = 0
307 gr_l[nt] = 0
308 surf[nt] = CS_SURF_LIB
309 nt = nt + 1
310 nlibs = nlibs + 1
311 } else { dropped = dropped + 1 }
312 }
313 }
314 } }
315 lls = li + 1
316 }
317 li = li + 1
318 }
319 }
320 var corpus_complete: i64 = 1
321 if dropped > 0 { corpus_complete = 0 }
322 if lib_present == 0 { corpus_complete = 0 }
323
324 // ---- tokenize the query from argv[1..] ----
325 let tokbuf: *u8 = sys_mmap(CS_MAXTOK * CS_TOKCAP)
326 let toklen: *i64 = sys_mmap(CS_MAXTOK * 8) as *i64
327 var ntok: i64 = 0
328 let qecho: *u8 = sys_mmap(CS_MAGIC_1024)
329 var qn: i64 = 0
330 var a: i64 = 1
331 while a < argc {
332 let s: *u8 = argv[a] as *u8
333 var p: i64 = 0
334 var cur: i64 = 0
335 while s[p] != (0 as u8) {
336 let c: i64 = s[p] as i64
337 if qn < 1000 { qecho[qn] = c as u8; qn = qn + 1 }
338 if cs_isw(c) == 1 {
339 if ntok < CS_MAXTOK { if cur < CS_TOKCAP - 1 { tokbuf[ntok*CS_TOKCAP + cur] = cs_lower(c) as u8; cur = cur + 1 } }
340 } else {
341 if cur >= CS_MINTOK { if ntok < CS_MAXTOK { toklen[ntok] = cur; ntok = ntok + 1 } }
342 cur = 0
343 }
344 p = p + 1
345 }
346 if cur >= CS_MINTOK { if ntok < CS_MAXTOK { toklen[ntok] = cur; ntok = ntok + 1 } }
347 if a + 1 < argc { if qn < 1000 { qecho[qn] = 32 as u8; qn = qn + 1 } }
348 a = a + 1
349 }
350 if ntok == 0 {
351 csw("{\"organ\":\"nx_capsearch\",\"query\":\"" as *u8); csj(qecho, 0, qn)
352 csw("\",\"considered\":" as *u8); csn(nt)
353 csw(",\"tools\":" as *u8); csn(ntools); csw(",\"libs\":" as *u8); csn(nlibs)
354 csw(",\"dropped\":" as *u8); csn(dropped); csw(",\"corpus_complete\":" as *u8); csn(corpus_complete)
355 csw(",\"results\":[],\"note\":\"no usable query token (>=3 chars) -- not guessing\"}\n" as *u8)
356 sys_exit(0)
357 return 0
358 }
359
360 // ---- df per token, then score ----
361 let score: *i64 = sys_mmap(CS_MAXTOOLS * 8) as *i64
362 var z: i64 = 0
363 while z < nt { score[z] = 0; z = z + 1 }
364 var t: i64 = 0
365 while t < ntok {
366 let tk: *u8 = ((tokbuf as i64) + t * CS_TOKCAP) as *u8
367 let tl: i64 = toklen[t]
368 var df: i64 = 0
369 var d: i64 = 0
370 while d < nt {
371 let rb: *u8 = cs_buf(surf[d], sb, lb)
372 var hit: i64 = 0
373 if cs_namehit(rb, nm_o[d], nm_l[d], tk, tl) > 0 { hit = 1 }
374 if hit == 0 { if ti_l[d] > 0 { if cs_texthit(rb, ti_o[d], ti_l[d], tk, tl) > 0 { hit = 1 } } }
375 if hit == 0 { if gr_l[d] > 0 { if cs_texthit(gb, gr_o[d], gr_l[d], tk, tl) > 0 { hit = 1 } } }
376 if hit == 1 { df = df + 1 }
377 d = d + 1
378 }
379 let w: i64 = CS_SCALE / (3 + df)
380 var e: i64 = 0
381 while e < nt {
382 let eb: *u8 = cs_buf(surf[e], sb, lb)
383 var s2: i64 = 0
384 let nh: i64 = cs_namehit(eb, nm_o[e], nm_l[e], tk, tl)
385 if nh > 0 { s2 = s2 + nh * 3 * w }
386 if ti_l[e] > 0 { let th: i64 = cs_texthit(eb, ti_o[e], ti_l[e], tk, tl); if th > 0 { s2 = s2 + th * w } }
387 if gr_l[e] > 0 { let gh: i64 = cs_texthit(gb, gr_o[e], gr_l[e], tk, tl); if gh > 0 { s2 = s2 + (gh * w) / 2 } }
388 if s2 > 0 { score[e] = score[e] + s2 }
389 e = e + 1
390 }
391 t = t + 1
392 }
393
394 // ---- top-N selection (no sort: N passes of argmax, N is 8) ----
395 csw("{\"organ\":\"nx_capsearch\",\"query\":\"" as *u8); csj(qecho, 0, qn)
396 csw("\",\"considered\":" as *u8); csn(nt)
397 csw(",\"tools\":" as *u8); csn(ntools); csw(",\"libs\":" as *u8); csn(nlibs)
398 csw(",\"dropped\":" as *u8); csn(dropped); csw(",\"corpus_complete\":" as *u8); csn(corpus_complete)
399 csw(",\"ranker\":\"code-ask tooltop + call-grammar field + idf damping + morphological prefix (held-out gold: hit@1 520 permil vs 400 known-good); library surface joined 2026-08-24\",\"results\":[" as *u8)
400 var emitted: i64 = 0
401 var r: i64 = 0
402 while r < CS_TOPN {
403 var bi: i64 = 0 - 1
404 var bs: i64 = 0
405 var q: i64 = 0
406 while q < nt {
407 if score[q] > bs { bs = score[q]; bi = q }
408 q = q + 1
409 }
410 if bi < 0 { r = CS_TOPN } else {
411 let ob: *u8 = cs_buf(surf[bi], sb, lb)
412 if emitted > 0 { csw("," as *u8) }
413 csw("{\"tool\":\"" as *u8); csj(ob, nm_o[bi], nm_l[bi])
414 csw("\",\"surface\":\"" as *u8)
415 if surf[bi] == CS_SURF_LIB { csw("lib" as *u8) } else { csw("tool" as *u8) }
416 csw("\",\"score\":" as *u8); csn(bs)
417 csw(",\"what\":\"" as *u8)
418 if ti_l[bi] > 0 { csj(ob, ti_o[bi], ti_l[bi]) }
419 csw("\",\"call\":\"" as *u8)
420 if gr_l[bi] > 0 { csj(gb, gr_o[bi], gr_l[bi]) }
421 csw("\"}" as *u8)
422 emitted = emitted + 1
423 score[bi] = 0
424 r = r + 1
425 }
426 }
427 csw("],\"returned\":" as *u8); csn(emitted)
428 csw("}\n" as *u8)
429 sys_exit(0)
430 return 0
431}