code wiki / _hdl_build / nx_liblift.nx

nx_liblift.nx source

↩ module page · 414 lines · 16956 B

1// nx_liblift.nx -- BYTE-PRESERVING LIFT of a shared lib from _hdl_build/ into runtime/, the one file 2// operation this ecosystem could not perform and therefore never performed. 3// 4// WHY IT EXISTS (measured 2026-07-31, id 1785474294): nx_layerscan_gate found **8 misplaced libs darkening 5// 26+ organs** -- nx_ingest(5) nx_grep(5) nx_lib_ingest(4, incl the nx_lib_http daemon) nx_fft(3, PLUS the 6// entire training capability via nx_fft_f32 -> nx_autograd_tensor) nx_bm25(3, PLUS rights-enforced DMS search) 7// nx_proc_ctl(3, incl **nx_proc_kill**, the sovereign runaway-process killer) nx_json_emit(2) nx_teacher(1). 8// Each is unbuildable because runtime/ CANNOT import _hdl_build/. The whole remedy is 8 file moves -- and 9// there was NO TOOL THAT COULD MOVE A FILE IN THAT DIRECTION. nx_retire_path only moves into 10// knowledge/retired/ (fixed destination) and nx_dedupe_source moves the WRONG WAY (it deletes the runtime 11// copy and calls _hdl_build canonical, id 1785474494). So the ecosystem could diagnose this class but not 12// fix it, which is why 8 libs sat misplaced long enough to darken a training stack. 13// 14// THE DIRECTION IS NOT A PREFERENCE, IT IS FORCED BY THE ASYMMETRY: _hdl_build/ CAN import runtime/, and 15// runtime/ CANNOT import _hdl_build/. A lib that BOTH trees need therefore has exactly ONE correct home: 16// runtime/. Lifting satisfies every consumer at once and creates no duplicate that could later diverge -- 17// which is why this is a LIFT (copy down + retire the original), never a copy-and-leave. 18// 19// SAFETY, all fail-closed and all verified rather than assumed: 20// - REFUSES if the source is missing (nothing to lift) 21// - REFUSES if the destination already exists -- NEVER clobbers a runtime/ file, because the whole point 22// is to stop losing work, and a lift that overwrites is just a faster way to lose it 23// - BYTE-VERIFIES after writing: re-reads the destination and compares length AND every byte against the 24// source, reporting VERIFY-FAIL rather than success if they differ (a copy nobody checked is a rumour) 25// - DOES NOT DELETE THE ORIGINAL. Retiring it is a SEPARATE, reversible act: 26// nx_retire_path retire buildroot/runtime/_hdl_build/<name>.nx 27// Two verbs, two decisions -- so a half-finished lift leaves both copies rather than none. 28// 29// nx_liblift check <name> -- report src/dst presence + sizes, MUTATES NOTHING 30// nx_liblift lift <name> -- copy _hdl_build/<name>.nx -> runtime/<name>.nx, byte-verified 31// exit 0 ok | 2 usage | 3 source missing | 4 destination exists | 5 read fail | 6 write fail | 7 verify fail 32// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 33import "nx_syscalls.nx" 34 35const LL_SRCDIR: *u8 = "buildroot/runtime/_hdl_build" as *u8 36const LL_DSTDIR: *u8 = "buildroot/runtime" as *u8 37const LL_PATHBUF: i64 = 1024 38const LL_MODE: i64 = 420 39 40func ll_puts(s: *u8) { 41 var n: i64 = 0 42 while s[n] != (0 as u8) { n = n + 1 } 43 sys_write(1, s, n) 44} 45 46func ll_puti(x: i64) { 47 var buf: *u8 = sys_mmap(64) as *u8 48 var v: i64 = x 49 var neg: i64 = 0 50 if v < 0 { 51 neg = 1 52 v = 0 - v 53 } 54 var i: i64 = 40 55 if v == 0 { 56 i = i - 1 57 buf[i] = 48 as u8 58 } 59 while v > 0 { 60 let d: i64 = v - (v / 10) * 10 61 i = i - 1 62 buf[i] = (d + 48) as u8 63 v = v / 10 64 } 65 if neg == 1 { 66 i = i - 1 67 buf[i] = 45 as u8 68 } 69 sys_write(1, ((buf as i64) + i) as *u8, 40 - i) 70} 71 72// out = "<dir>/<name>.nx" 73func ll_path(out: *u8, dir: *u8, name: *u8) { 74 var w: i64 = 0 75 var i: i64 = 0 76 while dir[i] != (0 as u8) { 77 out[w] = dir[i] 78 w = w + 1 79 i = i + 1 80 } 81 out[w] = 47 as u8 82 w = w + 1 83 i = 0 84 while name[i] != (0 as u8) { 85 out[w] = name[i] 86 w = w + 1 87 i = i + 1 88 } 89 out[w] = 46 as u8 90 out[w + 1] = 110 as u8 91 out[w + 2] = 120 as u8 92 out[w + 3] = 0 as u8 93} 94 95func ll_exists(path: *u8) -> i64 { 96 let fd: i64 = sys_openat_rd(path) 97 if fd < 0 { return 0 } 98 sys_close(fd) 99 return 1 100} 101 102// --------------------------------------------------------------------------------------------------- 103// CLOSURE WALKER, added 2026-07-31 (id 1785477577). WHY: this tool could MOVE a lib but could not say 104// what the move DRAGGED WITH IT, so it pushed its hardest step onto every caller. Measured cost of that 105// gap: lifting nx_bm25 to runtime/ CREATED a fresh layering violation, because nx_bm25 imports 106// nx_research_extract which was _hdl_build-only. The lift succeeded, byte-verified, and the consumer 107// STILL failed with the identical expand_imports error -- indistinguishable from "the fix did not work". 108// LAW: MOVING A LIB ACROSS A LAYER BOUNDARY MOVES ITS ENTIRE IMPORT CLOSURE INTO THAT LAYER'S 109// CONSTRAINTS. A one-file move is never one file. `deps` computes that set BEFORE any bytes move. 110const LL_MAXQ: i64 = 256 111const LL_NAMELEN: i64 = 64 112 113func ll_nlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 114 115func ll_streq(a: *u8, b: *u8) -> i64 { 116 var i: i64 = 0 117 while a[i] != (0 as u8) { 118 if a[i] != b[i] { return 0 } 119 i = i + 1 120 } 121 if b[i] != (0 as u8) { return 0 } 122 return 1 123} 124 125func ll_slot(tab: *u8, idx: i64) -> *u8 { return ((tab as i64) + idx * LL_NAMELEN) as *u8 } 126 127// Append name into tab only if absent. Writes into slot cnt as scratch FIRST, then dedupes against 128// 0..cnt-1; a duplicate simply returns cnt and the scratch is overwritten next call. Bounded by 129// LL_MAXQ and LL_NAMELEN -- REFUSES rather than truncating, so the closure can never silently shrink. 130func ll_push(tab: *u8, cnt: i64, src: *u8, n: i64) -> i64 { 131 if n <= 0 { return cnt } 132 if n >= LL_NAMELEN { return cnt } 133 if cnt >= LL_MAXQ { return cnt } 134 let dstp: *u8 = ll_slot(tab, cnt) 135 var i: i64 = 0 136 while i < n { dstp[i] = src[i]; i = i + 1 } 137 dstp[n] = 0 as u8 138 var k: i64 = 0 139 while k < cnt { 140 if ll_streq(ll_slot(tab, k), dstp) == 1 { return cnt } 141 k = k + 1 142 } 143 return cnt + 1 144} 145 146// Scan buf for `import "NAME.nx"` and push each NAME (with .nx stripped) into tab. The .nx suffix is 147// only stripped when it is actually present, so an unexpected import spelling is kept verbatim and 148// shows up as MISSING-EVERYWHERE rather than being silently mangled into a name that resolves. 149func ll_scan_imports(buf: *u8, n: i64, tab: *u8, cnt: i64) -> i64 { 150 var c: i64 = cnt 151 var i: i64 = 0 152 while i + 10 < n { 153 var hit: i64 = 0 154 if buf[i] == (105 as u8) { 155 if buf[i+1] == (109 as u8) { 156 if buf[i+2] == (112 as u8) { 157 if buf[i+3] == (111 as u8) { 158 if buf[i+4] == (114 as u8) { 159 if buf[i+5] == (116 as u8) { hit = 1 } } } } } } 160 if hit == 1 { 161 var j: i64 = i + 6 162 var q: i64 = 0 - 1 163 var stop: i64 = 0 164 while stop == 0 { 165 if j >= n { stop = 1 } else { 166 if buf[j] == (34 as u8) { q = j; stop = 1 } else { 167 if buf[j] == (10 as u8) { stop = 1 } else { j = j + 1 } 168 } 169 } 170 } 171 if q >= 0 { 172 var e: i64 = q + 1 173 var stop2: i64 = 0 174 while stop2 == 0 { 175 if e >= n { stop2 = 1 } else { 176 if buf[e] == (34 as u8) { stop2 = 1 } else { e = e + 1 } 177 } 178 } 179 if e < n { 180 let p: *u8 = ((buf as i64) + q + 1) as *u8 181 var L: i64 = e - (q + 1) 182 if L > 3 { 183 if p[L-3] == (46 as u8) { 184 if p[L-2] == (110 as u8) { 185 if p[L-1] == (120 as u8) { L = L - 3 } } } 186 } 187 c = ll_push(tab, c, p, L) 188 } 189 } 190 } 191 i = i + 1 192 } 193 return c 194} 195// --------------------------------------------------------------------------------------------------- 196 197func main(argc: i64, argv: *i64) -> i64 { 198 if argc < 3 { 199 ll_puts("usage: nx_liblift check <name> | lift <name> (name WITHOUT the .nx)\n" as *u8) 200 ll_puts(" lifts _hdl_build/<name>.nx -> runtime/<name>.nx, byte-verified, never clobbers.\n" as *u8) 201 ll_puts(" retire the original SEPARATELY: nx_retire_path retire buildroot/runtime/_hdl_build/<name>.nx\n" as *u8) 202 return 2 203 } 204 let verb: *u8 = argv[1] as *u8 205 let name: *u8 = argv[2] as *u8 206 207 let src: *u8 = sys_mmap(LL_PATHBUF) 208 let dst: *u8 = sys_mmap(LL_PATHBUF) 209 ll_path(src, LL_SRCDIR, name) 210 ll_path(dst, LL_DSTDIR, name) 211 212 let slen: *i64 = sys_mmap(16) as *i64 213 slen[0] = 0 214 let sbuf: *u8 = sys_read_file(src, slen) 215 let have_src: i64 = ll_exists(src) 216 let have_dst: i64 = ll_exists(dst) 217 218 ll_puts("NX-LIBLIFT name=" as *u8); ll_puts(name) 219 ll_puts(" src_exists=" as *u8); ll_puti(have_src) 220 ll_puts(" src_bytes=" as *u8); ll_puti(slen[0]) 221 ll_puts(" dst_exists=" as *u8); ll_puti(have_dst) 222 ll_puts("\n" as *u8) 223 224 // revive <artifact-relpath> <dest-basename> -- byte-verified COPY of a RETIRED artifact back into 225 // runtime/ under a NEW name. 226 // 227 // WHY: the 2026-07-21 dedupe treated NAME COLLISIONS as copies and deleted live capabilities. Two are 228 // proven -- the nx_http_health /health EMITTER (3 importers) and the nx_teacher LESSON organ (6) -- 229 // and nx_undefscan dupescan measured 26 candidates across 116 artifacts. Restoring under the OLD name 230 // would recreate the very collision that caused the loss, so this verb REQUIRES a distinct 231 // destination and REFUSES to clobber. It NEVER deletes or renames: the artifact stays exactly where 232 // it is, so a wrong revive costs one file, not the evidence. 233 if verb[0] == (114 as u8) { 234 if argc < 4 { 235 ll_puts("REVIVE: need <artifact-relpath> <dest-basename> (dest WITHOUT .nx)\n" as *u8) 236 return 2 237 } 238 let apath: *u8 = argv[2] as *u8 239 let dname: *u8 = argv[3] as *u8 240 let dpath: *u8 = sys_mmap(LL_PATHBUF) 241 ll_path(dpath, LL_DSTDIR, dname) 242 if ll_exists(dpath) == 1 { 243 ll_puts("REVIVE REFUSED: destination EXISTS at " as *u8); ll_puts(dpath) 244 ll_puts(" -- never clobbers.\n" as *u8) 245 return 4 246 } 247 let al: *i64 = sys_mmap(16) as *i64 248 al[0] = 0 249 let ab: *u8 = sys_read_file(apath, al) 250 if al[0] <= 0 { 251 ll_puts("REVIVE REFUSED: artifact unreadable or empty at " as *u8); ll_puts(apath) 252 ll_puts("\n" as *u8) 253 return 3 254 } 255 let rfd: i64 = sys_openat_wr(dpath, LL_MODE) 256 if rfd < 0 { 257 ll_puts("REVIVE FAIL: cannot open destination for write.\n" as *u8) 258 return 6 259 } 260 sys_write(rfd, ab, al[0]) 261 sys_close(rfd) 262 let vl: *i64 = sys_mmap(16) as *i64 263 vl[0] = 0 264 let vb: *u8 = sys_read_file(dpath, vl) 265 if vl[0] != al[0] { 266 ll_puts("REVIVE FAIL: read-back size mismatch -- destination NOT trustworthy.\n" as *u8) 267 return 7 268 } 269 var vi: i64 = 0 270 while vi < al[0] { 271 if ab[vi] != vb[vi] { 272 ll_puts("REVIVE FAIL: read-back BYTE mismatch at offset " as *u8); ll_puti(vi) 273 ll_puts("\n" as *u8) 274 return 7 275 } 276 vi = vi + 1 277 } 278 ll_puts("REVIVED bytes=" as *u8); ll_puti(al[0]) 279 ll_puts(" -> runtime/" as *u8); ll_puts(dname) 280 ll_puts(".nx (byte-verified; artifact left untouched)\n" as *u8) 281 return 0 282 } 283 284 if verb[0] == (99 as u8) { 285 if have_src == 0 { ll_puts("CHECK: source ABSENT -- nothing to lift.\n" as *u8); return 0 } 286 if have_dst == 1 { ll_puts("CHECK: destination ALREADY EXISTS -- lift would REFUSE (never clobbers).\n" as *u8); return 0 } 287 ll_puts("CHECK: liftable. runtime/ consumers currently CANNOT import this lib.\n" as *u8) 288 return 0 289 } 290 291 if verb[0] == (100 as u8) { 292 if have_src == 0 { ll_puts("DEPS: source ABSENT -- nothing to walk.\n" as *u8); return 0 } 293 let tab: *u8 = sys_mmap(LL_MAXQ * LL_NAMELEN) 294 let imp: *u8 = sys_mmap(LL_MAXQ * LL_NAMELEN) 295 let fp: *u8 = sys_mmap(LL_PATHBUF) 296 let rp: *u8 = sys_mmap(LL_PATHBUF) 297 let hp: *u8 = sys_mmap(LL_PATHBUF) 298 let ln: *i64 = sys_mmap(16) as *i64 299 var cnt: i64 = ll_push(tab, 0, name, ll_nlen(name)) 300 var head: i64 = 0 301 var missing: i64 = 0 302 while head < cnt { 303 let cur: *u8 = ll_slot(tab, head) 304 ll_path(fp, LL_SRCDIR, cur) 305 ln[0] = 0 306 let b: *u8 = sys_read_file(fp, ln) 307 if ln[0] > 0 { 308 let icnt: i64 = ll_scan_imports(b, ln[0], imp, 0) 309 var k: i64 = 0 310 while k < icnt { 311 let dep: *u8 = ll_slot(imp, k) 312 ll_path(rp, LL_DSTDIR, dep) 313 ll_path(hp, LL_SRCDIR, dep) 314 if ll_exists(rp) == 0 { 315 if ll_exists(hp) == 1 { 316 cnt = ll_push(tab, cnt, dep, ll_nlen(dep)) 317 } else { 318 missing = missing + 1 319 ll_puts(" MISSING-EVERYWHERE " as *u8); ll_puts(dep) 320 ll_puts(" (in NEITHER tree -- a lift cannot fix this one)\n" as *u8) 321 } 322 } 323 k = k + 1 324 } 325 } 326 head = head + 1 327 } 328 ll_puts("NX-LIBLIFT-DEPS name=" as *u8); ll_puts(name) 329 ll_puts(" closure=" as *u8); ll_puti(cnt) 330 ll_puts(" must_lift=" as *u8); ll_puti(cnt - 1) 331 ll_puts(" missing=" as *u8); ll_puti(missing) 332 ll_puts("\n" as *u8) 333 var z: i64 = 1 334 while z < cnt { 335 ll_puts(" LIFT-ALSO " as *u8); ll_puts(ll_slot(tab, z)); ll_puts("\n" as *u8) 336 z = z + 1 337 } 338 if cnt >= LL_MAXQ { 339 ll_puts("VERDICT=OVERFLOW closure hit LL_MAXQ -- this count is a FLOOR, raise the cap and re-run\n" as *u8) 340 return 5 341 } 342 if cnt == 1 { 343 ll_puts("VERDICT=CLEAN lift this file alone; every import it needs already resolves in runtime/\n" as *u8) 344 return 0 345 } 346 ll_puts("VERDICT=MUST-LIFT-CLOSURE lift every LIFT-ALSO name too, or the move trades one dark subtree for another\n" as *u8) 347 return 0 348 } 349 350 if have_src == 0 { 351 ll_puts("REFUSED: source missing at " as *u8); ll_puts(src); ll_puts("\n" as *u8) 352 return 3 353 } 354 if have_dst == 1 { 355 ll_puts("REFUSED: destination EXISTS at " as *u8); ll_puts(dst) 356 ll_puts(" -- a lift that overwrites is a faster way to lose work. Reconcile deliberately.\n" as *u8) 357 return 4 358 } 359 if sbuf as i64 == 0 { 360 ll_puts("REFUSED: source unreadable.\n" as *u8) 361 return 5 362 } 363 if slen[0] <= 0 { 364 ll_puts("REFUSED: source is EMPTY -- refusing to create an empty lib.\n" as *u8) 365 return 5 366 } 367 368 let fd: i64 = sys_openat_wr(dst, LL_MODE) 369 if fd < 0 { 370 ll_puts("REFUSED: cannot open destination for write.\n" as *u8) 371 return 6 372 } 373 let wrote: i64 = sys_write(fd, sbuf, slen[0]) 374 sys_close(fd) 375 if wrote != slen[0] { 376 ll_puts("WRITE-SHORT wrote=" as *u8); ll_puti(wrote) 377 ll_puts(" of " as *u8); ll_puti(slen[0]); ll_puts("\n" as *u8) 378 return 6 379 } 380 381 // BYTE-VERIFY: a copy nobody checked is a rumour. 382 let vlen: *i64 = sys_mmap(16) as *i64 383 vlen[0] = 0 384 let vbuf: *u8 = sys_read_file(dst, vlen) 385 if vbuf as i64 == 0 { 386 ll_puts("VERIFY-FAIL: destination unreadable after write.\n" as *u8) 387 return 7 388 } 389 if vlen[0] != slen[0] { 390 ll_puts("VERIFY-FAIL length src=" as *u8); ll_puti(slen[0]) 391 ll_puts(" dst=" as *u8); ll_puti(vlen[0]); ll_puts("\n" as *u8) 392 return 7 393 } 394 var i: i64 = 0 395 var bad: i64 = 0 - 1 396 while i < slen[0] { 397 if sbuf[i] != vbuf[i] { 398 bad = i 399 i = slen[0] 400 } else { i = i + 1 } 401 } 402 if bad >= 0 { 403 ll_puts("VERIFY-FAIL first differing byte at offset " as *u8); ll_puti(bad); ll_puts("\n" as *u8) 404 return 7 405 } 406 407 ll_puts("LIFTED bytes=" as *u8); ll_puti(slen[0]) 408 ll_puts(" VERIFIED byte-for-byte -> " as *u8); ll_puts(dst); ll_puts("\n" as *u8) 409 ll_puts(" ORIGINAL LEFT IN PLACE ON PURPOSE. Retire it deliberately and reversibly with:\n" as *u8) 410 ll_puts(" nx_retire_path retire " as *u8); ll_puts(src); ll_puts("\n" as *u8) 411 ll_puts(" Until then BOTH copies exist; _hdl_build resolves its own first, so nothing regresses today,\n" as *u8) 412 ll_puts(" but leaving both is the divergence class -- finish the move.\n" as *u8) 413 return 0 414}