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}