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}