code wiki / _hdl_build / nx_subject_infer.nx

nx_subject_infer.nx source

↩ module page · 342 lines · 18933 B

1// nx_subject_infer.nx -- DERIVE a gate's subject source by MEASUREMENT, not by naming convention. 2// 3// The .gates 4th field (the subject a gate is judging) gates the whole non-vacuity apparatus: without it 4// nx_bite_sweep reports NO-SUBJECT and the domain can never be proven. 38 of 40 domains lack it. Filling 5// those by hand means GUESSING, and this session already measured what guessing costs: a guessed subject 6// that fails to open produces "no mutant killed", which reads identically to a vacuous gate -- so a wrong 7// guess does not merely fail, it SLANDERS a working gate. 8// 9// ★THE INSIGHT: a gate is not a black box about its own subject either. It IMPORTS what it tests, and it 10// NAMES the phrases it asserts on. Both are readable. So the subject can be DERIVED and then SCORED: 11// candidates = the gate's own `import "X.nx"` lines + the name-derived guess (nx_X_gate -> nx_X) 12// score(candidate) = how many of the gate's named prose needles actually appear in that file 13// The winner is the file the gate demonstrably talks about. A candidate that scores 0 is not the subject, 14// however much its name looks right -- which is exactly the nx_healthhelp_site_gate case, where the 15// name-derived guess WAS correct but a thin HTML assembler and the copy lived elsewhere. 16// 17// ★IT REFUSES RATHER THAN GUESSES. If no candidate scores above zero, it emits UNRESOLVED and says so. 18// An inferrer that always produces an answer is just a naming convention wearing a lab coat. 19// 20// usage: nx_subject_infer <domain> (CWD = nxc2 root) 21// prints a suggested `.gates` row per declared gate; exit 0 = every gate resolved, 1 = at least one UNRESOLVED 22// license_tier: ORIGINAL 23import "nx_syscalls.nx" 24import "nx_artifact_root.nx" 25const SI_MAGIC_4096: i64 = 4096 26const SI_MAGIC_4000: i64 = 4000 27const SI_MAGIC_65536: i64 = 65536 28const SI_MAGIC_65535: i64 = 65535 29 30const SI_MAXSRC: i64 = 4194304 31 32// ★MINIMUM SCORE TO CLAIM A SUBJECT. Derived from measurement, not taste: on medbilling (all five subjects 33// hand-verified) the true subjects scored 6, 9, 11 and 15, while `runtime/nx_syscalls.nx` -- a shared lib 34// imported by everything -- scored 2 for nx_healthhelp_site_gate and WON, naming the wrong file with 35// confidence. A marginal score is a ubiquitous helper leaking through, not a subject. 36// ★A FALSE POSITIVE IS STRICTLY WORSE THAN UNRESOLVED HERE. UNRESOLVED says "declare it by hand"; a wrong 37// subject gets written into .gates, mutated, and reported as INCONCLUSIVE -- which reads as a hollow gate. 38// The inferrer would then slander working gates at scale, silently. So it refuses below the floor. 39// With STEM-EXCLUSION below doing the real work, the floor only needs to demand *some* positive evidence. 40const SI_MIN_SCORE: i64 = 1 41 42// ★STEM EXCLUSION -- why a score alone can never pick the subject. 43// MEASURED on warden: the TRUE subject `runtime/_hdl_build/nx_raci.nx` scores 2, while `nx_syscalls.nx` 44// scores 4 and wins. That is not a tuning problem, it is structural: syscall wrappers and shared helpers are 45// called by EVERY organ, so infrastructure accumulates more incidental hits than any real subject, in every 46// domain, forever. Raising the floor cannot fix it -- the wrong answer scores HIGHER than the right one. 47// The fix is a structural relation, not a denylist of names (which would be taste): a subject must share the 48// gate's NAME STEM (nx_raci_gate -> nx_raci*, nx_medbill_nsa_gate -> nx_medbill_nsa*). That excludes ALL 49// shared infrastructure by construction, in one rule, without naming a single file. Score is then used for 50// what it is good at: CONFIRMING that the stem-relative candidate is really the thing the gate talks about. 51static si_stem: *u8 52 53func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 54func wn(v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(28); var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 55 56func si_read(path: *u8, buf: *u8, cap: i64) -> i64 { 57 let fd: i64 = sys_openat_rd(path) 58 if fd < 0 { return 0 - 1 } 59 var tot: i64 = 0 60 var go: i64 = 1 61 while go == 1 { 62 let n: i64 = sys_read(fd, ((buf as i64)+tot) as *u8, cap - tot) 63 if n <= 0 { go = 0 } else { tot = tot + n; if tot >= cap { go = 0 } } 64 } 65 sys_close(fd) 66 return tot 67} 68func si_find(hay: *u8, hn: i64, ned: *u8, nl: i64) -> i64 { 69 if nl <= 0 { return 0 - 1 } 70 var i: i64 = 0 71 while i + nl <= hn { 72 var j: i64 = 0 73 var m: i64 = 1 74 while j < nl { if hay[i+j] != ned[j] { m = 0; j = nl } else { j = j + 1 } } 75 if m == 1 { return i } 76 i = i + 1 77 } 78 return 0 - 1 79} 80func si_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i];o=o+1;i=i+1} return o } 81 82// gate TARGET from an elf path: strip dir, strip trailing ".elf" 83func si_target(elf: *u8, out: *u8) -> i64 { 84 var n: i64 = 0 85 while elf[n] != (0 as u8) { n = n + 1 } 86 var s: i64 = 0 87 var i: i64 = 0 88 while i < n { if elf[i] == (47 as u8) { s = i + 1 } i = i + 1 } 89 var e: i64 = n 90 if n >= 4 { if elf[n-4] == (46 as u8) { e = n - 4 } } 91 var o: i64 = 0 92 var k: i64 = s 93 while k < e { out[o] = elf[k]; o = o + 1; k = k + 1 } 94 out[o] = 0 as u8 95 return o 96} 97 98// ---- SIGNAL 2: SYMBOL REFERENCE. How many functions DEFINED in the candidate does the gate CALL? ---- 99// ★ONE SIGNAL CANNOT REACH EVERY GATE, exactly as one mutation operator could not. Prose-needle scoring 100// only works on gates that assert on TEXT. MEASURED against medbilling, where all five subjects are already 101// hand-verified correct: needles alone resolved 2 of 5 -- nx_medbill_codes_gate and nx_medbill_recon_gate 102// assert on CODES and NUMBERS, sharing no prose with their subject at all. A gate that calls mbc_get() is 103// telling you what it tests just as loudly as one that quotes a sentence. 104// Direction matters: we walk the CANDIDATE's `func NAME(` definitions and ask whether the GATE references 105// them. The reverse (gate's calls -> is it defined here) would count every syscall and shared helper. 106func si_symscore(gsrc: *u8, gn: i64, cb: *u8, cn: i64) -> i64 { 107 let nm: *u8 = sys_mmap(256) 108 var hits: i64 = 0 109 var i: i64 = 0 110 while i < cn - 6 { 111 if cb[i] == (102 as u8) { 112 let fk: *u8 = "func " as *u8 113 var m: i64 = 1 114 var k: i64 = 0 115 while k < 5 { if cb[i+k] != fk[k] { m = 0; k = 5 } else { k = k + 1 } } 116 var atline: i64 = 0 117 if i == 0 { atline = 1 } else { if cb[i-1] == (10 as u8) { atline = 1 } } 118 if m == 1 { if atline == 1 { 119 var j: i64 = i + 5 120 var l: i64 = 0 121 while j < cn { let c: i64 = cb[j] as i64; if c == 40 { break } if c == 32 { break } if c == 10 { break } if l < 250 { nm[l] = cb[j]; l = l + 1 } j = j + 1 } 122 if l >= 4 { if cb[j] == (40 as u8) { 123 nm[l] = 40 as u8 // match "NAME(" so a bare substring cannot count 124 nm[l+1] = 0 as u8 125 if si_find(gsrc, gn, nm, l+1) >= 0 { hits = hits + 1 } 126 } } 127 i = j + 1 128 } else { i = i + 1 } } else { i = i + 1 } 129 } else { i = i + 1 } 130 } 131 return hits 132} 133 134// SCORE a candidate on BOTH signals. The gate's own assertions -- prose it quotes and functions it calls -- 135// are the ground truth for what file it is actually talking about. 136func si_score(gsrc: *u8, gn: i64, cand: *u8) -> i64 { 137 // stem gate: a candidate that does not share the gate's name stem is not a subject, whatever it scores. 138 if (si_stem as i64) != 0 { 139 var sl: i64 = 0 140 while si_stem[sl] != (0 as u8) { sl = sl + 1 } 141 var cl2: i64 = 0 142 while cand[cl2] != (0 as u8) { cl2 = cl2 + 1 } 143 if si_find(cand, cl2, si_stem, sl) < 0 { return 0 - 1 } 144 } 145 let res: *u8 = sys_mmap(512) 146 if ar_resolve(cand, res) == 0 { return 0 - 1 } 147 let cb: *u8 = sys_mmap(SI_MAXSRC) 148 let cn: i64 = si_read(res, cb, SI_MAXSRC) 149 if cn <= 0 { return 0 - 1 } 150 let ned: *u8 = sys_mmap(SI_MAGIC_4096) 151 var hits: i64 = 0 152 var i: i64 = 0 153 while i < gn - 1 { 154 if gsrc[i] == (34 as u8) { 155 var j: i64 = i + 1 156 var spaces: i64 = 0 157 var ended: i64 = 0 158 while j < gn { 159 let c: i64 = gsrc[j] as i64 160 if c == 34 { ended = 1; break } 161 if c == 10 { break } 162 if c == 32 { spaces = spaces + 1 } 163 j = j + 1 164 } 165 var clen: i64 = j - i - 1 166 if clen >= 4 { if gsrc[i+1+clen-4] == (92 as u8) { clen = clen - 4 } } 167 if ended == 1 { if clen >= 10 { if spaces >= 1 { if clen < SI_MAGIC_4000 { 168 var c2: i64 = 0 169 while c2 < clen { ned[c2] = gsrc[i+1+c2]; c2 = c2 + 1 } 170 if si_find(cb, cn, ned, clen) >= 0 { hits = hits + 1 } 171 } } } } 172 i = j + 1 173 } else { i = i + 1 } 174 } 175 return hits + si_symscore(gsrc, gn, cb, cn) 176} 177 178func main(argc: i64, argv: *i64) -> i64 { 179 if argc < 2 { w("usage: nx_subject_infer <domain>\n"); sys_exit(2); return 2 } 180 let domain: *u8 = argv[1] as *u8 181 182 let gp: *u8 = sys_mmap(256) 183 var o: i64 = si_cat(gp, 0, "knowledge/compare/" as *u8) 184 o = si_cat(gp, o, domain) 185 o = si_cat(gp, o, ".gates" as *u8) 186 gp[o] = 0 as u8 187 let gbuf: *u8 = sys_mmap(SI_MAGIC_65536) 188 let gn: i64 = si_read(gp, gbuf, SI_MAGIC_65535) 189 if gn <= 0 { w(" no .gates for this domain\n"); sys_exit(2); return 2 } 190 191 w("=== nx_subject_infer -- derive each gate's subject by what the gate ASSERTS ON ===\n") 192 w(" domain="); w(domain); w("\n") 193 194 let tgt: *u8 = sys_mmap(256) 195 let elf: *u8 = sys_mmap(512) 196 let cand: *u8 = sys_mmap(512) 197 let best: *u8 = sys_mmap(512) 198 let gsrc: *u8 = sys_mmap(SI_MAXSRC) 199 let res: *u8 = sys_mmap(512) 200 var unresolved: i64 = 0 201 var total: i64 = 0 202 203 var ls: i64 = 0 204 var p: i64 = 0 205 while p <= gn { 206 var eol: i64 = 0 207 if p == gn { eol = 1 } else { if gbuf[p] == (10 as u8) { eol = 1 } } 208 if eol == 1 { 209 if p > ls { if gbuf[ls] != (35 as u8) { 210 // field 0 = elf 211 var e0: i64 = ls 212 while e0 < p { if gbuf[e0] == (124 as u8) { break } e0 = e0 + 1 } 213 if e0 > ls { 214 var c: i64 = 0 215 while ls + c < e0 { elf[c] = gbuf[ls+c]; c = c + 1 } 216 elf[c] = 0 as u8 217 si_target(elf, tgt) 218 total = total + 1 219 // load the GATE source (its two known homes) 220 var glen: i64 = 0 221 var q: i64 = si_cat(cand, 0, "runtime/" as *u8) 222 q = si_cat(cand, q, tgt) 223 q = si_cat(cand, q, ".nx" as *u8) 224 cand[q] = 0 as u8 225 if ar_resolve(cand, res) == 1 { glen = si_read(res, gsrc, SI_MAXSRC) } 226 if glen <= 0 { 227 q = si_cat(cand, 0, "runtime/_hdl_build/" as *u8) 228 q = si_cat(cand, q, tgt) 229 q = si_cat(cand, q, ".nx" as *u8) 230 cand[q] = 0 as u8 231 if ar_resolve(cand, res) == 1 { glen = si_read(res, gsrc, SI_MAXSRC) } 232 } 233 w(" "); w(tgt); w(": ") 234 if glen <= 0 { w("GATE-SOURCE-NOT-FOUND (cannot infer without it)\n"); unresolved = unresolved + 1 } else { 235 var bestscore: i64 = 0 - 1 236 best[0] = 0 as u8 237 // the gate's name stem = target minus a trailing "_gate"; every candidate must carry it 238 let stem: *u8 = sys_mmap(256) 239 var sm: i64 = 0 240 while tgt[sm] != (0 as u8) { stem[sm] = tgt[sm]; sm = sm + 1 } 241 if sm > 5 { 242 let gs2: *u8 = "_gate" as *u8 243 var ok2: i64 = 1 244 var z2: i64 = 0 245 while z2 < 5 { if stem[sm-5+z2] != gs2[z2] { ok2 = 0; z2 = 5 } else { z2 = z2 + 1 } } 246 if ok2 == 1 { sm = sm - 5 } 247 } 248 stem[sm] = 0 as u8 249 si_stem = stem 250 // ★ORDER OF HYPOTHESES MATTERS. The naming convention (nx_X_gate -> nx_X) is the 251 // dominant shape, so it is tried FIRST and measurement is used to CONFIRM OR REJECT 252 // it -- not to replace it. That is not convention-over-measurement: an unverified 253 // convention would be taste, but a convention that must clear the score floor is a 254 // hypothesis under test. Imports are the fallback for gates the convention misses. 255 // WHY THE ORDER IS LOAD-BEARING: scoring all candidates flat let `nx_syscalls.nx` 256 // -- imported by every organ alive -- WIN for nx_raci_gate with 4 symbol hits, and 257 // name the wrong file with confidence. Ubiquitous infrastructure always accumulates 258 // incidental hits; the subject is not whatever scores highest in a flat field. 259 var tl0: i64 = 0 260 while tgt[tl0] != (0 as u8) { tl0 = tl0 + 1 } 261 if tl0 > 5 { 262 let gsuf0: *u8 = "_gate" as *u8 263 var isg0: i64 = 1 264 var z0: i64 = 0 265 while z0 < 5 { if tgt[tl0-5+z0] != gsuf0[z0] { isg0 = 0; z0 = 5 } else { z0 = z0 + 1 } } 266 if isg0 == 1 { 267 // both known homes for the subject -- omitting _hdl_build/ meant warden's 268 // real subject was never even a candidate. 269 var cq0: i64 = si_cat(cand, 0, "runtime/" as *u8) 270 var y0: i64 = 0 271 while y0 < tl0 - 5 { cand[cq0] = tgt[y0]; cq0 = cq0 + 1; y0 = y0 + 1 } 272 cq0 = si_cat(cand, cq0, ".nx" as *u8) 273 cand[cq0] = 0 as u8 274 let sA: i64 = si_score(gsrc, glen, cand) 275 if sA >= 0 { w("\n cand "); w(cand); w(" score="); wn(sA) } 276 if sA > bestscore { bestscore = sA; var b1: i64 = 0; while cand[b1] != (0 as u8) { best[b1] = cand[b1]; b1 = b1 + 1 } best[b1] = 0 as u8 } 277 var cq1: i64 = si_cat(cand, 0, "runtime/_hdl_build/" as *u8) 278 var y1: i64 = 0 279 while y1 < tl0 - 5 { cand[cq1] = tgt[y1]; cq1 = cq1 + 1; y1 = y1 + 1 } 280 cq1 = si_cat(cand, cq1, ".nx" as *u8) 281 cand[cq1] = 0 as u8 282 let sB: i64 = si_score(gsrc, glen, cand) 283 if sB >= 0 { w("\n cand "); w(cand); w(" score="); wn(sB) } 284 if sB > bestscore { bestscore = sB; var b2: i64 = 0; while cand[b2] != (0 as u8) { best[b2] = cand[b2]; b2 = b2 + 1 } best[b2] = 0 as u8 } 285 } 286 } 287 // Imports are consulted ONLY if the convention failed to clear the floor. 288 var try_imports: i64 = 1 289 if bestscore >= SI_MIN_SCORE { try_imports = 0 } 290 if try_imports == 1 { 291 // CANDIDATE SET 1: everything the gate IMPORTS. A gate imports what it tests, so 292 // this covers the case the naming convention misses entirely. 293 var i: i64 = 0 294 while i < glen - 8 { 295 if gsrc[i] == (105 as u8) { 296 let im: *u8 = "import \"" as *u8 297 var m: i64 = 1 298 var k: i64 = 0 299 while k < 8 { if gsrc[i+k] != im[k] { m = 0; k = 8 } else { k = k + 1 } } 300 if m == 1 { 301 var j: i64 = i + 8 302 var cl: i64 = 0 303 let nm: *u8 = sys_mmap(256) 304 while j < glen { if gsrc[j] == (34 as u8) { break } if cl < 255 { nm[cl] = gsrc[j]; cl = cl + 1 } j = j + 1 } 305 nm[cl] = 0 as u8 306 if cl > 3 { 307 var cq: i64 = si_cat(cand, 0, "runtime/" as *u8) 308 cq = si_cat(cand, cq, nm) 309 cand[cq] = 0 as u8 310 let sc: i64 = si_score(gsrc, glen, cand) 311 if sc >= 0 { w("\n cand "); w(cand); w(" score="); wn(sc) } 312 if sc > bestscore { bestscore = sc; var bb: i64 = 0; while cand[bb] != (0 as u8) { best[bb] = cand[bb]; bb = bb + 1 } best[bb] = 0 as u8 } 313 } 314 i = j + 1 315 } else { i = i + 1 } 316 } else { i = i + 1 } 317 } 318 } 319 if bestscore < SI_MIN_SCORE { 320 var shown: i64 = bestscore 321 if shown < 0 { shown = 0 } // -1 means "no candidate even resolved"; wn has no sign 322 w("UNRESOLVED (best candidate scored "); wn(shown) 323 w(", under the floor of "); wn(SI_MIN_SCORE) 324 w(" -- declare it by hand rather than let a shared helper win)\n") 325 unresolved = unresolved + 1 326 } else { 327 w("subject="); w(best); w(" score="); wn(bestscore); w("\n") 328 } 329 } 330 } 331 } } 332 ls = p + 1 333 } 334 p = p + 1 335 } 336 337 w(" gates="); wn(total); w(" unresolved="); wn(unresolved); w("\n") 338 if unresolved == 0 { w("VERDICT: verdict=GREEN (every declared gate has a measured subject)\n"); sys_exit(0); return 0 } 339 w("VERDICT: verdict=RED (a gate's subject could not be derived -- declare it by hand, or the gate asserts on nothing this tree contains)\n") 340 sys_exit(1) 341 return 1 342}