code wiki / _hdl_build / nx_head_audit.nx
nx_head_audit.nx source
↩ module page · 260 lines · 11133 B
1// nx_head_audit.nx -- FLEET AUDITOR for the accessible-head migration (seq702/seq703/seq707).
2//
3// THE SCALE PROBLEM: 65+ live page emitters each hand-rolled a lang-less <html><head> literal.
4// Hand-fixing does not scale (65 flaky build/promote cycles) and a raw grep hit its 65KB output
5// cap = INCOMPLETE. This organ is the DRIVER: one call classifies the whole emitter fleet and
6// emits a deterministic migration worklist that an agent or a workflow can loop over. It is the
7// measurement half of the nx_html_head / nx_html_head_fd base libraries -- same relationship as
8// nx_law_warden to the laws it scans, or nx_gate_verdict's adoption census to the gate base class.
9//
10// DESIGN FOR HONESTY (laws L011 / seq395 / seq670 -- a scanner with no negative control is a liar):
11// * per-file scan is a bounded 8KiB PREFIX read into ONE reused buffer -> zero per-file mmap
12// growth (avoids the ~1GB/run leak that OOM'd nx_dup_source_check v2). The head literal is
13// always near the top of an emitter; a head beyond 8KiB is a DECLARED miss, not a silent one.
14// * the directory walk DRAINS getdents (loops until nr<=0) so coverage is complete within the cap.
15// * emits a DECLARED ENVELOPE: scanned / nx-files / candidates / migrated / gates / cap_hit.
16// * `selftest` runs 1 positive + 3 NEGATIVE controls through the SAME ha_candidate() the live
17// walk uses, so the gate actually guards live behaviour.
18//
19// VERBS:
20// nx_head_audit audit <dir> -> one TAB line per migration candidate: <file>\t<shape>\tCANDIDATE
21// then a final `ENVELOPE ...` line. shape = buffer|fd (which head
22// lib variant the emitter must adopt).
23// nx_head_audit selftest -> non-vacuous gate (positive + 3 negative controls) -> VERDICT
24// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
25import "nx_syscalls.nx"
26
27const HA_DIRBUF: i64 = 131072
28const HA_PREFIX: i64 = 65536
29const HA_PATH: i64 = 4096
30const HA_OUT: i64 = 4096
31const HA_FILECAP: i64 = 40000
32
33func hw(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
34
35func ha_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
36
37// does literal lit occur starting at b[i] ?
38func ha_starts(b: *u8, i: i64, n: i64, lit: *u8) -> i64 {
39 var j: i64 = 0
40 while lit[j] != (0 as u8) {
41 if i + j >= n { return 0 }
42 if b[i + j] != lit[j] { return 0 }
43 j = j + 1
44 }
45 return 1
46}
47
48// substring presence of lit anywhere in b[0..n)
49func ha_has(b: *u8, n: i64, lit: *u8) -> i64 {
50 var i: i64 = 0
51 while i < n {
52 if ha_starts(b, i, n, lit) == 1 { return 1 }
53 i = i + 1
54 }
55 return 0
56}
57
58// does nm end with suffix suf ?
59func ha_ends(nm: *u8, suf: *u8) -> i64 {
60 let ln: i64 = ha_slen(nm)
61 let ls: i64 = ha_slen(suf)
62 if ls > ln { return 0 }
63 var j: i64 = 0
64 while j < ls {
65 if nm[ln - ls + j] != suf[j] { return 0 }
66 j = j + 1
67 }
68 return 1
69}
70
71func ha_path(out: *u8, dir: *u8, nm: *u8) -> i64 {
72 var o: i64 = 0
73 var i: i64 = 0
74 while dir[i] != (0 as u8) { out[o] = dir[i]; o = o + 1; i = i + 1 }
75 out[o] = 47 as u8; o = o + 1
76 i = 0
77 while nm[i] != (0 as u8) { out[o] = nm[i]; o = o + 1; i = i + 1 }
78 out[o] = 0 as u8
79 return o
80}
81
82func ha_lit(out: *u8, o: i64, s: *u8) -> i64 {
83 var i: i64 = 0
84 while s[i] != (0 as u8) { out[o + i] = s[i]; i = i + 1 }
85 return o + i
86}
87
88func ha_num(out: *u8, o: i64, v: i64) -> i64 {
89 if v == 0 { out[o] = 48 as u8; return o + 1 }
90 var d: i64 = 0
91 var x: i64 = v
92 while x > 0 { d = d + 1; x = x / 10 }
93 var i: i64 = d - 1
94 x = v
95 while i >= 0 { out[o + i] = ((x % 10) + 48) as u8; x = x / 10; i = i - 1 }
96 return o + d
97}
98
99// ---- the detector, shared by the live walk AND the selftest (so the gate guards reality) ----
100func ha_migrated(b: *u8, n: i64) -> i64 {
101 if ha_has(b, n, "nxh_head_open" as *u8) == 1 { return 1 }
102 if ha_has(b, n, "nxh_fd_head_open" as *u8) == 1 { return 1 }
103 return 0
104}
105// candidate = carries the lang-less head literal AND has NOT adopted a head lib
106// AND does NOT also emit a clean <html lang= head. The last clause (added after a
107// measured false positive: nx_editor_board's live /editor.html was already clean at
108// a11y=0, yet its source carried a stray <html><head on a secondary path) drops the
109// "primary head already clean" class. RESIDUAL, declared: analyzers/tests that merely
110// CARRY the literal as data with no clean head of their own (e.g. nx_page_verify) still
111// flag -- substring presence cannot prove the literal is ever WRITTEN as a page.
112func ha_candidate(b: *u8, n: i64) -> i64 {
113 if ha_has(b, n, "<html><head" as *u8) == 0 { return 0 }
114 if ha_migrated(b, n) == 1 { return 0 }
115 if ha_has(b, n, "<html lang=" as *u8) == 1 { return 0 }
116 return 1
117}
118// which head-lib variant the emitter needs: fd-streaming vs buffer-accumulating
119func ha_shape_fd(b: *u8, n: i64) -> i64 {
120 if ha_has(b, n, "nxh_fd" as *u8) == 1 { return 1 }
121 if ha_has(b, n, "hw(fd" as *u8) == 1 { return 1 }
122 return 0
123}
124
125func ha_read_prefix(path: *u8, pbuf: *u8) -> i64 {
126 let fd: i64 = sys_openat_rd(path)
127 if fd < 0 { return 0 - 1 }
128 let nr: i64 = sys_read(fd, pbuf, HA_PREFIX)
129 sys_close(fd)
130 if nr < 0 { return 0 }
131 return nr
132}
133
134func ha_audit(dir: *u8) -> i64 {
135 let fd: i64 = sys_openat_rd(dir)
136 if fd < 0 { hw(2, "AUDIT-FAIL cannot open dir\n" as *u8); return 0 - 1 }
137 let dbuf: *u8 = sys_mmap(HA_DIRBUF)
138 let pbuf: *u8 = sys_mmap(HA_PREFIX)
139 let path: *u8 = sys_mmap(HA_PATH)
140 let line: *u8 = sys_mmap(HA_OUT)
141 var scanned: i64 = 0
142 var nxfiles: i64 = 0
143 var cands: i64 = 0
144 var migd: i64 = 0
145 var gates: i64 = 0
146 var caphit: i64 = 0
147 var go: i64 = 1
148 while go == 1 {
149 let nr: i64 = sys_getdents64(fd, dbuf, HA_DIRBUF)
150 if nr <= 0 { go = 0 } else {
151 var off: i64 = 0
152 while off < nr {
153 let rec: *u8 = (dbuf as i64 + off) as *u8
154 let nm: *u8 = dirent_name(rec)
155 scanned = scanned + 1
156 if ha_ends(nm, ".nx" as *u8) == 1 {
157 if ha_ends(nm, "nx_head_audit.nx" as *u8) == 1 {
158 // skip self: our own fixtures contain the lang-less literal
159 } else {
160 nxfiles = nxfiles + 1
161 if nxfiles < HA_FILECAP {
162 ha_path(path, dir, nm)
163 let pn: i64 = ha_read_prefix(path, pbuf)
164 if pn > 0 {
165 if ha_candidate(pbuf, pn) == 1 {
166 if ha_ends(nm, "_gate.nx" as *u8) == 1 {
167 gates = gates + 1
168 } else {
169 cands = cands + 1
170 var o: i64 = 0
171 o = ha_lit(line, o, nm)
172 line[o] = 9 as u8; o = o + 1
173 if ha_shape_fd(pbuf, pn) == 1 { o = ha_lit(line, o, "fd" as *u8) } else { o = ha_lit(line, o, "buffer" as *u8) }
174 o = ha_lit(line, o, "\tCANDIDATE\n" as *u8)
175 line[o] = 0 as u8
176 hw(1, line)
177 }
178 } else {
179 if ha_migrated(pbuf, pn) == 1 { migd = migd + 1 }
180 }
181 }
182 } else {
183 caphit = 1
184 }
185 }
186 }
187 off = off + dirent_reclen(rec)
188 }
189 }
190 }
191 sys_close(fd)
192 var o: i64 = 0
193 o = ha_lit(line, o, "ENVELOPE scanned=" as *u8)
194 o = ha_num(line, o, scanned)
195 o = ha_lit(line, o, " nx=" as *u8)
196 o = ha_num(line, o, nxfiles)
197 o = ha_lit(line, o, " candidates=" as *u8)
198 o = ha_num(line, o, cands)
199 o = ha_lit(line, o, " migrated=" as *u8)
200 o = ha_num(line, o, migd)
201 o = ha_lit(line, o, " gates_with_langless=" as *u8)
202 o = ha_num(line, o, gates)
203 o = ha_lit(line, o, " cap_hit=" as *u8)
204 o = ha_num(line, o, caphit)
205 o = ha_lit(line, o, " prefix_bytes=65536 coverage=" as *u8)
206 if caphit == 0 { o = ha_lit(line, o, "COMPLETE" as *u8) } else { o = ha_lit(line, o, "CAPPED" as *u8) }
207 o = ha_lit(line, o, "\n" as *u8)
208 line[o] = 0 as u8
209 hw(1, line)
210 return 0
211}
212
213func ha_selftest() -> i64 {
214 var pass: i64 = 0
215 var total: i64 = 0
216 // positive: lang-less fd emitter -> candidate=1, shape=fd
217 let a: *u8 = "func x(){ hw(fd, \"<html><head><title>g</title>\" as *u8) }" as *u8
218 total = total + 1
219 if ha_candidate(a, ha_slen(a)) == 1 { if ha_shape_fd(a, ha_slen(a)) == 1 { pass = pass + 1 } }
220 // negative 1: migrated emitter (adopted the lib) -> candidate=0
221 let b: *u8 = "o = nxh_head_open(out, o, \"g\" as *u8); o = pd_cat(out,o,\"<style>\")" as *u8
222 total = total + 1
223 if ha_candidate(b, ha_slen(b)) == 0 { pass = pass + 1 }
224 // negative 2: not a page emitter at all -> candidate=0
225 let c: *u8 = "func add(a: i64, b: i64) -> i64 { return a + b }" as *u8
226 total = total + 1
227 if ha_candidate(c, ha_slen(c)) == 0 { pass = pass + 1 }
228 // negative 3: hand-authored CLEAN head (has lang, no lang-less literal) -> candidate=0
229 let d: *u8 = "emit(\"<html lang=en><head><meta viewport></head><body><main>\")" as *u8
230 total = total + 1
231 if ha_candidate(d, ha_slen(d)) == 0 { pass = pass + 1 }
232 // negative 4: MIXED -- a clean primary head AND a stray lang-less literal on another
233 // path (the nx_editor_board false-positive class) -> candidate=0 (clean head wins)
234 let e: *u8 = "emit(\"<html lang=en><head>main</head>\"); alt(\"<html><head>secondary\")" as *u8
235 total = total + 1
236 if ha_candidate(e, ha_slen(e)) == 0 { pass = pass + 1 }
237 var o: i64 = 0
238 let line: *u8 = sys_mmap(256)
239 o = ha_lit(line, o, "SELFTEST " as *u8)
240 o = ha_num(line, o, pass)
241 o = ha_lit(line, o, "/" as *u8)
242 o = ha_num(line, o, total)
243 if pass == total { o = ha_lit(line, o, " VERDICT=GREEN (1 positive + 4 negative controls)\n" as *u8) } else { o = ha_lit(line, o, " VERDICT=RED\n" as *u8) }
244 line[o] = 0 as u8
245 hw(1, line)
246 if pass == total { return 0 }
247 return 1
248}
249
250func main(argc: i64, argv: *i64) -> i64 {
251 if argc < 2 { hw(2, "usage: nx_head_audit audit <dir> | selftest\n" as *u8); return 2 }
252 let verb: *u8 = argv[1] as *u8
253 if ha_starts(verb, 0, ha_slen(verb), "selftest" as *u8) == 1 { return ha_selftest() }
254 if ha_starts(verb, 0, ha_slen(verb), "audit" as *u8) == 1 {
255 if argc < 3 { hw(2, "usage: nx_head_audit audit <dir>\n" as *u8); return 2 }
256 return ha_audit(argv[2] as *u8)
257 }
258 hw(2, "usage: nx_head_audit audit <dir> | selftest\n" as *u8)
259 return 2
260}