code wiki / _hdl_build / nx_workstream_audit.nx

nx_workstream_audit.nx source

↩ module page · 364 lines · 17668 B

1// nx_workstream_audit.nx -- WMS-R4 LIB: the BOOT-TIME COMPLETENESS / CRASH AUDIT. 2// 3// WMS-R1 (nx_workstream_store) proves INTERNAL consistency: every id in ws:ids has a retrievable 4// seg-store segment with a real empire. R4 proves EXTERNAL completeness -- it cross-checks the 5// registry against the FILESYSTEM (memory .md files + code .nx organs) and the assignment_queue, 6// turning silently-lost workstreams into three LOUD verdicts (the crash-recovery durability proof): 7// orphan -- a registry row whose memory_link / code_link does NOT resolve to a real file 8// (the registry points at a lost/moved artifact). 9// untracked -- a real .nx code organ that NO registry row's code_link references (code exists 10// with no registry entry -- the hidden-workstream case = R4's neg-control hook). 11// unregistered -- a queue id (assignment_queue col0) that ws_get_p == WS_UNKNOWN (prose/queue-only, 12// never registered). 13// 14// COMPOSITION (anti-reinvention, rule 15): registry reads come from nx_workstream_store 15// (ws_manifest_p / ws_get_p / ws_field) read-only; the FS walk reuses the struct-free raw-offset 16// getdents64 idiom + dirent_reclen/dirent_type/dirent_name + dotlike-skip + last-'.' extension scan 17// PROVEN in nx_nas_book_census (the NxDirent struct path crashes signal-11 under nx_cc_sovereign -- 18// LANDMINE avoided; we use nx_syscalls.nx ONLY). memory + code + queue are FLAT scans, so no work-stack 19// is needed -- a single depth-1 getdents64 loop (simpler than the census tree walk). 20// 21// CRITICAL drvfs caveat (nx_nas_book_census line 277): getdents64 returns DT_UNKNOWN(0) for regular 22// files on WSL drvfs -- so the file candidate test accepts dtype 8 (DT_REG) AND 0 (DT_UNKNOWN). 23// 24// CRITICAL code_link path-shift hazard (verified): seed code_links like "runtime/nx_geo.nx" / 25// "runtime/nx_drv_proto_emit.nx" / "runtime/nx_root_trace.nx" do NOT exist at those literal paths -- 26// the files live in runtime/_hdl_build/. A naive "literal path missing -> orphan" check would emit 27// ~3 FALSE orphans. So the code-existence check MIRRORS harness resolution: try the literal link, 28// else fall back to the basename under runtime/ then runtime/_hdl_build/. A "-" link = intentionally 29// none (not an orphan). 30// 31// Sovereign: imports only nx_workstream_store (R1 store, read-only) + nx_seg_store + nx_syscalls. 32// Additive-only: this LIB does NOT modify R1's store. license_tier: ORIGINAL 33import "nx_workstream_store.nx" 34import "nx_seg_store.nx" 35import "nx_syscalls.nx" 36const WA_MAGIC_4096: i64 = 4096 37const WA_MAGIC_1024: i64 = 1024 38const WA_MAGIC_1048640: i64 = 1048640 39const WA_MAGIC_1048576: i64 = 1048576 40 41// ---- data-driven config (rule 11: no magic numbers / paths buried in code) ---- 42const WA_MEM_DIR: *u8 = "/mnt/c/Users/elder/.claude/projects/C--Users-elder/memory/" 43const WA_QUEUE: *u8 = "knowledge/registry/assignment_queue.tsv" 44const WA_CODE_DIR_A: *u8 = "runtime/" 45const WA_CODE_DIR_B: *u8 = "runtime/_hdl_build/" 46const WA_FLD_MEMORY: i64 = 4 47const WA_FLD_CODE: i64 = 5 48const WA_ITER_CAP: i64 = 200000 // hard bound on getdents records scanned (JPL rule 2) 49 50// ---- string helpers (mirror nx_nas_book_census bc_* / store ws_len) ---- 51func wa_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 52 53func wa_streq(a: *u8, b: *u8) -> i64 { 54 var i: i64 = 0 55 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 56 if b[i] != (0 as u8) { return 0 } 57 return 1 58} 59 60// last path segment (after the last '/') of a NUL-term path -> basename ptr (into the same buffer). 61func wa_basename(path: *u8) -> *u8 { 62 let n: i64 = wa_slen(path) 63 var last: i64 = 0 - 1 64 var i: i64 = 0 65 while i < n { if path[i] == (47 as u8) { last = i } i = i + 1 } 66 return (path as i64 + last + 1) as *u8 67} 68 69// fd>=0 existence probe (close immediately). 1 present, 0 absent. The proven file-exists idiom. 70func wa_path_exists(path: *u8) -> i64 { 71 let fd: i64 = sys_openat_rd(path) 72 if fd < 0 { return 0 } 73 sys_close(fd) 74 return 1 75} 76 77// join dir + name into out (NUL-term). dir already ends with '/' (or not -- we do NOT add one here; 78// callers that need a separator pass a dir literal ending in '/'). Returns out length. 79func wa_join(dir: *u8, name: *u8, out: *u8) -> i64 { 80 var w: i64 = 0 81 var i: i64 = 0 82 while dir[i] != (0 as u8) { out[w] = dir[i]; w = w + 1; i = i + 1 } 83 var j: i64 = 0 84 while name[j] != (0 as u8) { out[w] = name[j]; w = w + 1; j = j + 1 } 85 out[w] = 0 as u8 86 return w 87} 88 89// resolve a code_link the way the harness does: literal, else basename under runtime/ then 90// runtime/_hdl_build/. "-" => 0 (intentionally none, NOT an orphan). 1 if resolvable, 0 otherwise. 91func wa_code_link_resolves(link: *u8) -> i64 { 92 if link[0] == (45 as u8) { if link[1] == (0 as u8) { return 0 } } // "-" 93 if wa_path_exists(link) == 1 { return 1 } 94 let base: *u8 = wa_basename(link) 95 let buf: *u8 = sys_mmap(WA_MAGIC_4096) 96 wa_join(WA_CODE_DIR_A, base, buf) 97 if wa_path_exists(buf) == 1 { return 1 } 98 wa_join(WA_CODE_DIR_B, base, buf) 99 if wa_path_exists(buf) == 1 { return 1 } 100 return 0 101} 102 103// resolve a memory_link: "-" => 0 (none, not an orphan); else memory_link basename must exist under 104// mem_dir (mem_dir ends with '/'). 1 if resolvable, 0 otherwise. 105func wa_mem_link_resolves(link: *u8, mem_dir: *u8) -> i64 { 106 if link[0] == (45 as u8) { if link[1] == (0 as u8) { return 0 } } // "-" 107 let base: *u8 = wa_basename(link) 108 let buf: *u8 = sys_mmap(WA_MAGIC_4096) 109 wa_join(mem_dir, base, buf) 110 return wa_path_exists(buf) 111} 112 113// ---- ORPHAN pass: for each registry id, a non-"-" memory_link MUST resolve to a real file under 114// mem_dir AND a non-"-" code_link MUST resolve (basename-aware). Returns count of orphans, fills 115// orphan_out[] with offending ids (mmap'd NUL-term copies, LOUD). 0 == none. ---- 116func ws_audit_orphans_p(prefix: *u8, mem_dir: *u8, orphan_out: *i64, cap: i64) -> i64 { 117 let ids: *i64 = sys_mmap(8 * WA_MAGIC_4096) as *i64 118 let n: i64 = ws_manifest_p(prefix, ids, WA_MAGIC_4096) 119 if n < 0 { return 0 - 1 } 120 // SCALE FIX (post-crash hardening 2026-06-14): snapshot the registry ONCE via ss_open; the 121 // per-id lookup below is ss_hget (in-memory, zero file IO). The old per-id ws_get_p mmap'd 122 // ~100KB/call and starved the bump-allocator at ~1162 ids. Mirrors ws_audit_complete_p. 123 let h: *i64 = ss_open(prefix) 124 let pq: *i64 = sys_mmap(16) as *i64 125 let lq: *i64 = sys_mmap(16) as *i64 126 let keybuf: *u8 = sys_mmap(256) 127 let mlink: *u8 = sys_mmap(WA_MAGIC_1024) 128 let clink: *u8 = sys_mmap(WA_MAGIC_1024) 129 var orph: i64 = 0 130 var i: i64 = 0 131 while i < n { 132 let id: *u8 = ids[i] as *u8 133 // key = "ws:" + id 134 keybuf[0] = 119 as u8; keybuf[1] = 115 as u8; keybuf[2] = 58 as u8 // 'w','s',':' 135 var t: i64 = 0 136 while id[t] != (0 as u8) { keybuf[3 + t] = id[t]; t = t + 1 } 137 keybuf[3 + t] = 0 as u8 138 var bad: i64 = 0 139 if ss_hget(h, keybuf, pq, lq) == 1 { 140 ws_field(pq[0] as *u8, lq[0], WA_FLD_MEMORY, mlink) 141 ws_field(pq[0] as *u8, lq[0], WA_FLD_CODE, clink) 142 // memory_link non-"-" must resolve 143 var mneed: i64 = 1 144 if mlink[0] == (45 as u8) { if mlink[1] == (0 as u8) { mneed = 0 } } 145 if mneed == 1 { if wa_mem_link_resolves(mlink, mem_dir) == 0 { bad = 1 } } 146 // code_link non-"-" must resolve (basename-aware -- avoids the path-shift false orphans) 147 var cneed: i64 = 1 148 if clink[0] == (45 as u8) { if clink[1] == (0 as u8) { cneed = 0 } } 149 if cneed == 1 { if wa_code_link_resolves(clink) == 0 { bad = 1 } } 150 } 151 if bad == 1 { 152 if orph < cap { 153 let ob: *u8 = sys_mmap(256) 154 var z: i64 = 0 155 while id[z] != (0 as u8) { ob[z] = id[z]; z = z + 1 } 156 ob[z] = 0 as u8 157 orphan_out[orph] = ob as i64 158 } 159 orph = orph + 1 160 } 161 i = i + 1 162 } 163 return orph 164} 165 166// (wa_code_referenced REMOVED 2026-06-14: it re-scanned the WHOLE registry per organ via leaking 167// per-key ss_get -> O(N_organs x N_ids) blowup that wedged the audit at scale. ws_audit_untracked_p 168// now snapshots the referenced-basename set ONCE via ss_open/ss_hget; see its body. Note kept so the 169// footgun isn't reintroduced.) 170 171// 1 iff name ends with ".nx" (the code-organ extension test; last-'.' scan idiom). 172func wa_is_nx(name: *u8) -> i64 { 173 let n: i64 = wa_slen(name) 174 if n < 3 { return 0 } 175 if name[n - 3] != (46 as u8) { return 0 } // '.' 176 if name[n - 2] != (110 as u8) { return 0 } // 'n' 177 if name[n - 1] != (120 as u8) { return 0 } // 'x' 178 return 1 179} 180 181// ---- UNTRACKED pass: walk code_dir (struct-free getdents64), for each *.nx organ NOT referenced by 182// ANY registry row's code_link (basename match), report it. Returns count, fills untracked_out[]. 183// This is the NEG-CONTROL hook: a code file with no registry row is CAUGHT here. ---- 184func ws_audit_untracked_p(prefix: *u8, code_dir: *u8, untracked_out: *i64, cap: i64) -> i64 { 185 // --- SCALE FIX (post-crash hardening 2026-06-14): build the set of code_link basenames 186 // referenced by ANY registry row ONCE, from a single store snapshot (ss_open + ss_hget). The 187 // old path called wa_code_referenced PER organ, each re-scanning all ~1162 ids via leaking 188 // per-key ss_get -> O(N_organs x N_ids) ~2.6M file-backed mmaps over 2259 organs (minutes + 189 // OOM-edge). Now O(N_ids + N_organs) with all registry IO done once. Semantics identical. --- 190 let ids: *i64 = sys_mmap(8 * WA_MAGIC_4096) as *i64 191 let nid: i64 = ws_manifest_p(prefix, ids, WA_MAGIC_4096) 192 let refset: *i64 = sys_mmap(8 * WA_MAGIC_4096) as *i64 193 var nref: i64 = 0 194 if nid > 0 { 195 let h: *i64 = ss_open(prefix) 196 let pqr: *i64 = sys_mmap(16) as *i64 197 let lqr: *i64 = sys_mmap(16) as *i64 198 let kb: *u8 = sys_mmap(256) 199 let clink: *u8 = sys_mmap(WA_MAGIC_1024) 200 var ri: i64 = 0 201 while ri < nid { 202 let id: *u8 = ids[ri] as *u8 203 kb[0] = 119 as u8; kb[1] = 115 as u8; kb[2] = 58 as u8 204 var kt: i64 = 0 205 while id[kt] != (0 as u8) { kb[3 + kt] = id[kt]; kt = kt + 1 } 206 kb[3 + kt] = 0 as u8 207 if ss_hget(h, kb, pqr, lqr) == 1 { 208 ws_field(pqr[0] as *u8, lqr[0], WA_FLD_CODE, clink) 209 var csk: i64 = 0 210 if clink[0] == (45 as u8) { if clink[1] == (0 as u8) { csk = 1 } } // "-" 211 if csk == 0 { 212 let cbase: *u8 = wa_basename(clink) 213 if nref < WA_MAGIC_4096 { 214 let sb: *u8 = sys_mmap(256) 215 var zc: i64 = 0 216 while cbase[zc] != (0 as u8) { sb[zc] = cbase[zc]; zc = zc + 1 } 217 sb[zc] = 0 as u8 218 refset[nref] = sb as i64 219 nref = nref + 1 220 } 221 } 222 } 223 ri = ri + 1 224 } 225 } 226 let dfd: i64 = sys_openat_rd(code_dir) 227 if dfd < 0 { return 0 - 1 } 228 let dirbuf: *u8 = sys_mmap(WA_MAGIC_1048640) // getdents64 batch buffer (1 MiB) 229 var untr: i64 = 0 230 var iters: i64 = 0 231 var done: i64 = 0 232 while done == 0 { 233 let nb: i64 = sys_getdents64(dfd, dirbuf, WA_MAGIC_1048576) 234 if nb <= 0 { done = 1 } 235 else { 236 var off: i64 = 0 237 while off < nb { 238 if iters >= WA_ITER_CAP { off = nb; done = 1 } 239 else { 240 iters = iters + 1 241 let rec: *u8 = (dirbuf as i64 + off) as *u8 242 let rl: i64 = dirent_reclen(rec) 243 if rl <= 0 { off = nb } 244 else { 245 let nm: *u8 = dirent_name(rec) 246 let dt: i64 = dirent_type(rec) 247 // dotlike skip 248 var skip: i64 = 0 249 if nm[0] == (46 as u8) { 250 if nm[1] == (0 as u8) { skip = 1 } 251 else { if nm[1] == (46 as u8) { if nm[2] == (0 as u8) { skip = 1 } } } 252 } 253 // file candidate: DT_REG(8) OR DT_UNKNOWN(0) (drvfs), and a *.nx name 254 var iscand: i64 = 0 255 if dt == 8 { iscand = 1 } 256 if dt == 0 { iscand = 1 } 257 if skip == 0 { if iscand == 1 { if wa_is_nx(nm) == 1 { 258 // in-memory membership against the pre-built referenced set (zero IO) 259 var referenced: i64 = 0 260 var mr: i64 = 0 261 while mr < nref { 262 if wa_streq(refset[mr] as *u8, nm) == 1 { referenced = 1; mr = nref } 263 else { mr = mr + 1 } 264 } 265 if referenced == 0 { 266 if untr < cap { 267 let ub: *u8 = sys_mmap(256) 268 var z: i64 = 0 269 while nm[z] != (0 as u8) { ub[z] = nm[z]; z = z + 1 } 270 ub[z] = 0 as u8 271 untracked_out[untr] = ub as i64 272 } 273 untr = untr + 1 274 } 275 } } } 276 off = off + rl 277 } 278 } 279 } 280 } 281 } 282 sys_close(dfd) 283 return untr 284} 285 286// ---- UNREGISTERED pass: read assignment_queue.tsv, for each non-comment row's col0 id that 287// ws_get_p(ws:<id>) == WS_UNKNOWN, report it. Returns count, fills unreg_out[]. 288// Line model: each iteration is positioned at a line START; we extract col0 (up to TAB/newline), 289// then advance `i` to the byte AFTER the next newline (or to n at EOF). '#'-leading lines are skipped. 290func ws_audit_unregistered_p(prefix: *u8, queue_path: *u8, unreg_out: *i64, cap: i64) -> i64 { 291 let lenq: *i64 = sys_mmap(16) as *i64 292 let buf: *u8 = sys_read_file(queue_path, lenq) 293 if buf as i64 == 0 { return 0 - 1 } 294 let n: i64 = lenq[0] 295 // SCALE FIX (post-crash hardening 2026-06-14): snapshot the registry ONCE; the per-row lookup 296 // below is ss_hget (zero file IO). The old per-row ws_get_p leaked ~100KB/call over the queue. 297 let h: *i64 = ss_open(prefix) 298 let pq: *i64 = sys_mmap(16) as *i64 299 let lq: *i64 = sys_mmap(16) as *i64 300 let idbuf: *u8 = sys_mmap(256) 301 let keybuf: *u8 = sys_mmap(256) 302 var unreg: i64 = 0 303 var i: i64 = 0 304 var iters: i64 = 0 305 while i < n { 306 if iters >= WA_ITER_CAP { i = n } 307 else { 308 iters = iters + 1 309 let is_comment: i64 = (buf[i] == (35 as u8)) as i64 // '#' 310 // extract col0 id = bytes up to first TAB or newline (only if not a comment) 311 var o: i64 = 0 312 if is_comment == 0 { 313 var go: i64 = 1 314 while go == 1 { 315 if i >= n { go = 0 } 316 else { 317 if buf[i] == (9 as u8) { go = 0 } 318 else { if buf[i] == (10 as u8) { go = 0 } else { if o < 255 { idbuf[o] = buf[i]; o = o + 1 } i = i + 1 } } 319 } 320 } 321 idbuf[o] = 0 as u8 322 } 323 // advance i to the byte AFTER the next newline (consumes the rest of this line). 324 // skip=1 until we step PAST a newline; at EOF the while just terminates on i>=n. 325 var skip: i64 = 1 326 while skip == 1 { 327 if i >= n { skip = 0 } 328 else { if buf[i] == (10 as u8) { i = i + 1; skip = 0 } else { i = i + 1 } } 329 } 330 if is_comment == 0 { if o > 0 { 331 keybuf[0] = 119 as u8; keybuf[1] = 115 as u8; keybuf[2] = 58 as u8 332 var t: i64 = 0 333 while idbuf[t] != (0 as u8) { keybuf[3 + t] = idbuf[t]; t = t + 1 } 334 keybuf[3 + t] = 0 as u8 335 if ss_hget(h, keybuf, pq, lq) == WS_UNKNOWN { 336 if unreg < cap { 337 let rb: *u8 = sys_mmap(256) 338 var z: i64 = 0 339 while idbuf[z] != (0 as u8) { rb[z] = idbuf[z]; z = z + 1 } 340 rb[z] = 0 as u8 341 unreg_out[unreg] = rb as i64 342 } 343 unreg = unreg + 1 344 } 345 } } 346 } 347 } 348 return unreg 349} 350 351// ---- roll-up: total lost-things = orphans + untracked + unregistered. The orphan count is the 352// hard lost-things invariant (0 == GREEN, the registry points at nothing lost). untracked/unregistered 353// are returned to the caller too (informational on production; the GATE proves the DETECTORS work). ---- 354func ws_audit_complete_r4_p(prefix: *u8, mem_dir: *u8, code_dir: *u8, queue_path: *u8) -> i64 { 355 let scratch: *i64 = sys_mmap(8 * WA_MAGIC_4096) as *i64 356 var total: i64 = 0 357 let o: i64 = ws_audit_orphans_p(prefix, mem_dir, scratch, WA_MAGIC_4096) 358 if o > 0 { total = total + o } 359 let u: i64 = ws_audit_untracked_p(prefix, code_dir, scratch, WA_MAGIC_4096) 360 if u > 0 { total = total + u } 361 let r: i64 = ws_audit_unregistered_p(prefix, queue_path, scratch, WA_MAGIC_4096) 362 if r > 0 { total = total + r } 363 return total 364}