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}