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}