code wiki / _hdl_build / nx_gatekit_lib.nx
nx_gatekit_lib.nx source
↩ module page · 855 lines · 38559 B
1// nx_gatekit_lib.nx -- build fixtures and DRIVE OTHER ORGANS, entirely through syscalls.
2//
3// WHY THIS UNBLOCKS THE LAST SOVEREIGNTY DEBT
4// -------------------------------------------
5// Every end-to-end gate for the memory plane was a `sh` script run by WSL from PowerShell: Linux
6// shell + Windows host + third-party runtime on the program path, in an estate whose doctrine is
7// bits-up sovereignty and whose own gates are ELF organs.
8//
9// The blocker was real, not laziness: an in-process gate can test a LIB, but an END-TO-END gate must
10// RUN the organ -- create fixtures, invoke the binary with arguments, inspect exit code and resulting
11// files -- and nothing in the plane could spawn a process. Shell was the only tool that could.
12// nx_syscalls has had fork/execve/wait4 the whole time; the gates simply never used them.
13// ★ THE REASON A THIRD-PARTY RUNTIME PERSISTS IS USUALLY ONE MISSING PRIMITIVE, NOT A REAL DEPENDENCY.
14//
15// The shell dependency also CORRUPTED WORK repeatedly in one session, which is the argument made
16// concrete rather than aesthetic:
17// * a generated .sh written with CRLF made `2>&1` a syntax error in dash;
18// * a PowerShell single-quoted Replace put a LITERAL backtick-n into an organ's source, producing
19// "UNRESOLVED identifier 'n'" -- source and deployed binary silently diverged;
20// * a fixture built with `cp` from another test's OUTPUT measured that test instead of its own.
21// None of those failure modes exist when the fixture is written and the child is spawned by syscall.
22//
23// Sovereign: imports only nx_syscalls. license_tier: ORIGINAL
24import "nx_syscalls.nx"
25const GK_MAGIC_65536: i64 = 65536
26const GK_MAGIC_4294967295: i64 = 4294967295
27const GK_MAGIC_4096: i64 = 4096
28
29const GK_BUF: i64 = 1048576
30
31// Resolve a build target to the source file that actually exists. Sources live in BOTH
32// `runtime/_hdl_build/` and `runtime/`, and hardcoding one directory has now produced the same defect
33// in two organs on the same day: nx_gatesubj emitted rows naming files that could not be opened, and
34// nx_gatestale reported 32 deployed gates as NO-SOURCE when all 32 were simply in the other directory.
35// ★★★★★ "NOT FOUND" MEANS "NOT WHERE I LOOKED" UNTIL YOU HAVE LOOKED EVERYWHERE — AND THE SECOND ORGAN
36// TO HARDCODE THE SAME ASSUMPTION IS THE SIGNAL IT WAS ALWAYS A SHARED PRIMITIVE.
37// ⚠AND THE ROOT WAS THE SAME MISTAKE ONE LEVEL UP, FIXED 2026-09-03. This function shipped with the
38// LAPTOP's absolute prefix (/mnt/c/Users/elder/nishi-core/nxc2/) baked into both probes, which makes a
39// SHARED primitive that can only ever succeed on one host: run it anywhere else -- the NAS included --
40// and every probe misses, so it returns 0 and its callers report "no source anywhere" for sources that
41// are sitting right there. That is the estate's banked cross-host defect verbatim:
42// ★★★★★ AN UNTRANSLATED PATH CHECK RETURNS A CONFIDENT, USELESS "NO", AND ITS CALLER PUBLISHES THAT AS A
43// PROPERTY OF THE SUBJECT. The function's own comment already said the second organ to hardcode a
44// DIRECTORY was the signal it should be shared -- the identical argument applies to the ROOT, and
45// keeping it host-specific is what forked this lib between the laptop and the NAS in the first place.
46// ⇒ PROBE THE ROOTS, DO NOT ASSUME ONE. Relative first (correct from either tree's own cwd), then the
47// nishihost-root spelling, then the laptop absolute prefix LAST so existing laptop callers that run from
48// an unrelated cwd keep working exactly as before. Every candidate is STAT'd, never guessed, so the
49// answer is evidence in all four cases and identical on both hosts.
50// Returns 1 and fills `out` with a path that EXISTS, or 0 if the target has no source under any root.
51const GK_SRC_ROOTS: i64 = 4
52func gk_src_root(i: i64) -> *u8 {
53 if i == 0 { return "" as *u8 }
54 if i == 1 { return "buildroot/" as *u8 }
55 if i == 2 { return "../" as *u8 }
56 return "/mnt/c/Users/elder/nishi-core/nxc2/" as *u8
57}
58func gk_srcpath(target: *u8, out: *u8) -> i64 {
59 var r: i64 = 0
60 while r < GK_SRC_ROOTS {
61 var d: i64 = 0
62 while d < 2 {
63 var o: i64 = gk_cat(out, 0, gk_src_root(r))
64 o = gk_cat(out, o, "runtime/" as *u8)
65 if d == 0 { o = gk_cat(out, o, "_hdl_build/" as *u8) }
66 o = gk_cat(out, o, target)
67 o = gk_cat(out, o, ".nx" as *u8)
68 out[o] = 0 as u8
69 if gk_exists(out) == 1 { return 1 }
70 d = d + 1
71 }
72 r = r + 1
73 }
74 out[0] = 0 as u8
75 return 0
76}
77
78func gk_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
79
80// Index of the newline at or after `from`, else `n`. This exists because the ad-hoc idiom for it --
81// writing a sentinel (`e = n + 9`) into the cursor to leave the loop -- DESTROYS THE POSITION BEING
82// SEARCHED FOR, and the restore (`e - 9`) silently lands on `n`. That made a whole file parse as one
83// line in three separate organs in a single session, each time producing plausible output.
84// ★★★★★ A LOOP-EXIT SENTINEL WRITTEN INTO THE SEARCH VARIABLE ERASES THE ANSWER -- AND THE THIRD TIME
85// YOU FIX IT BY HAND IS THE SIGNAL IT WAS MISSING A PRIMITIVE, NOT ATTENTION.
86func gk_eol(b: *u8, from: i64, n: i64) -> i64 {
87 var e: i64 = from
88 var fin: i64 = 0
89 while fin == 0 {
90 if e >= n { fin = 1 }
91 if fin == 0 {
92 if b[e] == 10 as u8 { fin = 1 }
93 if fin == 0 { e = e + 1 }
94 }
95 }
96 return e
97}
98
99// ---------------------------------------------------------------------------------------------
100// THE COMPILE CORPUS, ENUMERATED ONCE, FOR EVERY CENSUS THAT CLAIMS TO MEASURE "THE TREE".
101//
102// gk_srcpath above fixed the RESOLVER (given a name, look in both dirs). It did not fix the
103// ENUMERATOR, and four organs went on hardcoding one directory as the population:
104// nx_unwired -- header claims "FULL ECOSYSTEM SCOPE, NOT A SAMPLE: every .nx in the tree",
105// const UW_DIR names _hdl_build ONLY: 7,159 of 18,559 files = 38.6%.
106// nx_srclint, nx_gatestale, nx_work_retro -- same single-dir assumption.
107// Measured 2026-08-08: 30 of the 403 names nx_unwired called UNWIRED have call sites in the
108// 11,430 files it never opened, and 3,379 functions defined in those files were never examined
109// at all. ★★★★★★ TRUNCATION CORRUPTS BOTH DIRECTIONS: AN UNSCANNED CALL SITE INVENTS A FALSE
110// POSITIVE, AND AN UNSCANNED DEFINITION HIDES A TRUE ONE.
111// ★★★★★★ AN INSTRUMENT THAT DECLARES FULL-TREE SCOPE IN ITS HEADER AND HARDCODES ONE DIRECTORY
112// IN ITS CONSTANT MEASURES A SUBSET AND REPORTS IT AS THE POPULATION.
113// gk_srcpath's own comment already said the second organ to hardcode this was the signal it was
114// a shared primitive. It was right, and the primitive it named was never built -- so the law was
115// banked in one organ and absent from the next.
116//
117// THE EDGE DEFINITION IS THE QUESTION, so it is stated once, here, and it is exactly the set
118// gk_srcpath can resolve: _hdl_build/ overlays runtime/. Files in nested sub-directories are
119// deliberately OUT (gk_srcpath cannot resolve them either) -- one definition, both directions.
120// Refuses rather than truncating: a partial census is more dangerous than none.
121
122// ⚠ A PATH CONSTANT IS A DEPLOYMENT ASSUMPTION. The first version of this primitive hardcoded the
123// laptop tree; shipped to the NAS buildroot it would have opened a directory that does not exist and
124// returned REFUSED -- a fix that travels to the host that actually builds and stops working there.
125// ★★★★★★ SHIPPING A HARDCODED ROOT TO A SECOND HOST SHIPS A REFUSAL, AND A REFUSAL READS AS "THE TREE
126// IS BROKEN" RATHER THAN "THE TOOL IS ON THE WRONG MACHINE."
127// So the root is RESOLVED, not assumed: the first candidate that exists on THIS host wins.
128const GK_ROOT_LAPTOP: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/runtime/"
129const GK_ROOT_NAS: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/runtime/"
130
131// Fill `out` with the runtime/ root of the SOURCE TREE on this host. Returns 1, or 0 if none.
132//
133// ⚠ VALIDATED BY CONTENT, NOT BY EXISTENCE. The first version asked only "does this path exist" and
134// was WRONG ON THE FIRST HOST IT MET: `/mnt/c/Users/elder/nishi-core/nxc2/runtime/` ALSO EXISTS on the
135// NAS -- as a directory of `.elf` artifacts with not one `.nx` in it. The probe opened it happily,
136// getdents returned 168 bytes whose first record was `nx_mkv2fmp4.elf`, and the corpus came back 0.
137// ★★★★★★ AN EXISTENCE CHECK IS NOT AN IDENTITY CHECK -- A ROOT PROBE THAT ASKS ONLY "IS THERE
138// SOMETHING HERE" WILL ADOPT A DIFFERENT DIRECTORY THAT HAPPENS TO SHARE THE NAME, AND THEN
139// REPORT AN EMPTY POPULATION AS A FACT ABOUT THE TREE.
140// So a candidate must contain the WITNESS below -- this very library -- which no artifact directory
141// has. The witness is chosen to be the file that defines this function: if it is missing, the thing
142// executing cannot have come from that tree.
143func gk_corpus_root(out: *u8) -> i64 {
144 let probe: *u8 = sys_mmap(GK_MAGIC_4096)
145 var o: i64 = gk_cat(out, 0, GK_ROOT_LAPTOP)
146 out[o] = 0 as u8
147 var p: i64 = gk_cat(probe, 0, out)
148 p = gk_cat(probe, p, "_hdl_build/nx_gatekit_lib.nx" as *u8)
149 probe[p] = 0 as u8
150 if gk_exists(probe) == 1 { return 1 }
151 o = gk_cat(out, 0, GK_ROOT_NAS)
152 out[o] = 0 as u8
153 p = gk_cat(probe, 0, out)
154 p = gk_cat(probe, p, "_hdl_build/nx_gatekit_lib.nx" as *u8)
155 probe[p] = 0 as u8
156 if gk_exists(probe) == 1 { return 1 }
157 out[0] = 0 as u8
158 return 0
159}
160
161// Where ratchet baselines live on THIS host. Derived from the resolved corpus root rather than
162// probed independently, so the baseline can never end up describing a different tree than the one
163// that was measured -- the denominator and the record of it stay bound together by construction.
164const GK_OPS_LAPTOP: *u8 = "/mnt/c/Users/elder/nishi-ops/"
165const GK_OPS_NAS: *u8 = "/volume1/homes/elderwesto/nishihost/buildroot/nishi-ops/"
166
167func gk_ops_dir(out: *u8) -> i64 {
168 let rt: *u8 = sys_mmap(GK_MAGIC_4096)
169 if gk_corpus_root(rt) == 0 { return 0 }
170 var o: i64 = 0
171 if gk_streq(rt, GK_ROOT_LAPTOP) == 1 { o = gk_cat(out, 0, GK_OPS_LAPTOP) }
172 if gk_streq(rt, GK_ROOT_LAPTOP) == 0 { o = gk_cat(out, 0, GK_OPS_NAS) }
173 out[o] = 0 as u8
174 gk_mkdir(out)
175 return 1
176}
177
178// Fill `out` with <ops-dir><name>, creating the ops dir if needed. Returns 1, or 0.
179func gk_ops_path(out: *u8, name: *u8) -> i64 {
180 if gk_ops_dir(out) == 0 { return 0 }
181 var o: i64 = gk_len(out)
182 o = gk_cat(out, o, name)
183 out[o] = 0 as u8
184 return 1
185}
186
187// Fill `out` with the _hdl_build/ overlay dir for this host. Returns 1, or 0.
188func gk_corpus_hdl(out: *u8) -> i64 {
189 if gk_corpus_root(out) == 0 { return 0 }
190 var o: i64 = gk_len(out)
191 o = gk_cat(out, o, "_hdl_build/" as *u8)
192 out[o] = 0 as u8
193 return 1
194}
195
196func gk_isnx(name: *u8) -> i64 {
197 let l: i64 = gk_len(name)
198 if l < 4 { return 0 }
199 if name[l - 3] != (46 as u8) { return 0 }
200 if name[l - 2] != (110 as u8) { return 0 }
201 if name[l - 1] != (120 as u8) { return 0 }
202 return 1
203}
204
205// Walk ONE directory, appending absolute paths of .nx files starting at index n0.
206// `shadowdir` non-zero means: skip any basename that also exists there (the overlay wins,
207// exactly as gk_srcpath resolves it -- composing that rule, never re-implementing it, so there
208// is one answer to "which file compiles" and not a second one that can drift).
209// Returns the new count, or negative: -1 dir unopenable, -2 cap reached, -3 path exceeds stride.
210// GENERALISED SUFFIX TEST. gk_isnx answers one question and answers it well; this answers the same
211// question for any declared extension, so a caller scanning a board, a conf or a journal composes the ONE
212// walker instead of writing a second one. gk_isnx is deliberately left exactly as it was -- it is the
213// hot path for every corpus scan in the estate and its call sites are not touched by this addition.
214func gk_ext_is(name: *u8, ext: *u8) -> i64 {
215 let l: i64 = gk_len(name)
216 let e: i64 = gk_len(ext)
217 if e <= 0 { return 0 }
218 if l < e + 1 { return 0 }
219 var i: i64 = 0
220 while i < e {
221 if name[l - e + i] != ext[i] { return 0 }
222 i = i + 1
223 }
224 return 1
225}
226
227// The walker, with the extension as a PARAMETER. gk_dirscan below is now a one-line delegate to this with
228// ".nx", so there is exactly ONE directory-walking implementation in this lib and no second copy that can
229// drift from it. Every pre-existing caller reaches the identical code path it always did.
230func gk_dirscan_ext(dir: *u8, shadowdir: *u8, names: *u8, stride: i64, cap: i64, n0: i64, ext: *u8) -> i64 {
231 let fd: i64 = sys_openat_rd(dir)
232 if fd < 0 { return 0 - 1 }
233 let gbuf: *u8 = sys_mmap(GK_MAGIC_65536)
234 let probe: *u8 = sys_mmap(GK_MAGIC_4096)
235 var cnt: i64 = n0
236 var bad: i64 = 0
237 // ⚠ ONE getdents64 CALL IS NOT A DIRECTORY LISTING -- loop until it returns 0, or a big
238 // directory is silently read as a prefix and its total published as fact.
239 var nread: i64 = __syscall(217, fd, gbuf, GK_MAGIC_65536, 0, 0, 0)
240 while nread > 0 {
241 var off: i64 = 0
242 while off < nread {
243 let reclen: i64 = (gbuf[off + 16] as i64) | ((gbuf[off + 17] as i64) << 8)
244 if reclen <= 0 { off = nread }
245 if reclen > 0 {
246 let nm: *u8 = ((gbuf as i64) + off + 19) as *u8
247 if gk_ext_is(nm, ext) == 1 {
248 var take: i64 = 1
249 if shadowdir != (0 as *u8) {
250 var sp: i64 = gk_cat(probe, 0, shadowdir)
251 sp = gk_cat(probe, sp, nm)
252 probe[sp] = 0 as u8
253 if gk_exists(probe) == 1 { take = 0 }
254 }
255 if take == 1 {
256 if cnt >= cap { bad = 0 - 2 }
257 if bad == 0 {
258 // Bound-check BEFORE writing: a path silently clipped to the stride
259 // becomes a file nothing can open, reported as a scanned file.
260 let need: i64 = gk_len(dir) + gk_len(nm) + 1
261 if need > stride { bad = 0 - 3 }
262 if bad == 0 {
263 let dst: *u8 = ((names as i64) + cnt * stride) as *u8
264 var o: i64 = gk_cat(dst, 0, dir)
265 o = gk_cat(dst, o, nm)
266 dst[o] = 0 as u8
267 cnt = cnt + 1
268 }
269 }
270 }
271 }
272 off = off + reclen
273 }
274 }
275 if bad != 0 { nread = 0 }
276 if bad == 0 { nread = __syscall(217, fd, gbuf, GK_MAGIC_65536, 0, 0, 0) }
277 }
278 sys_close(fd)
279 if bad != 0 { return bad }
280 return cnt
281}
282
283// The original entry point, preserved byte-for-byte in BEHAVIOUR: same name, same six arguments, same
284// ".nx" subject. It is a delegate rather than a copy precisely so this lib cannot grow two walkers.
285func gk_dirscan(dir: *u8, shadowdir: *u8, names: *u8, stride: i64, cap: i64, n0: i64) -> i64 {
286 return gk_dirscan_ext(dir, shadowdir, names, stride, cap, n0, ".nx" as *u8)
287}
288
289// COUNT the corpus without storing it, and MEASURE the longest path while doing so.
290//
291// WHY THIS EXISTS (operator standing order, restated 2026-09-03: "stop magic number bullshit"). A caller
292// that guesses a cap for gk_corpus_scan gets the choice between two defects: too small and the scan
293// REFUSES (-2), too large and it reserves address space nobody measured. Raising the guess only moves it --
294// that is the failure this estate has already paid for once, and a seat repeated it here by taking a cap
295// from 12000 to 40000 rather than removing it.
296//
297// With these two, the bound is DERIVED: count first, size the buffer to exactly what is there, then scan.
298// There is no number to tune and no ceiling to hit, so gk_corpus_scan's refusal becomes unreachable by
299// construction rather than avoided by luck. `maxlen` returns the longest FULL path seen, so the stride is
300// measured too instead of being a second guess.
301func gk_dircount(dir: *u8, shadowdir: *u8, maxlen: *i64) -> i64 {
302 let fd: i64 = sys_openat_rd(dir)
303 if fd < 0 { return 0 - 1 }
304 let gbuf: *u8 = sys_mmap(GK_MAGIC_65536)
305 let probe: *u8 = sys_mmap(GK_MAGIC_4096)
306 let dl: i64 = gk_len(dir)
307 var cnt: i64 = 0
308 var nread: i64 = __syscall(217, fd, gbuf, GK_MAGIC_65536, 0, 0, 0)
309 while nread > 0 {
310 var off: i64 = 0
311 while off < nread {
312 let reclen: i64 = (gbuf[off + 16] as i64) | ((gbuf[off + 17] as i64) << 8)
313 if reclen <= 0 { off = nread }
314 if reclen > 0 {
315 let nm: *u8 = ((gbuf as i64) + off + 19) as *u8
316 if gk_isnx(nm) == 1 {
317 var take: i64 = 1
318 if shadowdir != (0 as *u8) {
319 var sp: i64 = gk_cat(probe, 0, shadowdir)
320 sp = gk_cat(probe, sp, nm)
321 probe[sp] = 0 as u8
322 if gk_exists(probe) == 1 { take = 0 }
323 }
324 if take == 1 {
325 let need: i64 = dl + gk_len(nm) + 1
326 if need > maxlen[0] { maxlen[0] = need }
327 cnt = cnt + 1
328 }
329 }
330 off = off + reclen
331 }
332 }
333 nread = __syscall(217, fd, gbuf, GK_MAGIC_65536, 0, 0, 0)
334 }
335 sys_close(fd)
336 return cnt
337}
338func gk_corpus_count(maxlen: *i64) -> i64 {
339 let rt: *u8 = sys_mmap(GK_MAGIC_4096)
340 let hdl: *u8 = sys_mmap(GK_MAGIC_4096)
341 if gk_corpus_root(rt) == 0 { return 0 - 1 }
342 if gk_corpus_hdl(hdl) == 0 { return 0 - 1 }
343 maxlen[0] = 0
344 let n1: i64 = gk_dircount(hdl, 0 as *u8, maxlen)
345 if n1 < 0 { return n1 }
346 let n2: i64 = gk_dircount(rt, hdl, maxlen)
347 if n2 < 0 { return n2 }
348 return n1 + n2
349}
350
351// Enumerate the whole compile corpus into `names` (stride-sized slots, absolute paths).
352// Returns the count, or negative as gk_dirscan. Callers MUST branch on the negative -- the
353// whole point of this primitive is that it refuses instead of under-reporting.
354func gk_corpus_scan(names: *u8, stride: i64, cap: i64) -> i64 {
355 let rt: *u8 = sys_mmap(GK_MAGIC_4096)
356 let hdl: *u8 = sys_mmap(GK_MAGIC_4096)
357 if gk_corpus_root(rt) == 0 { return 0 - 1 }
358 if gk_corpus_hdl(hdl) == 0 { return 0 - 1 }
359 let n1: i64 = gk_dirscan(hdl, 0 as *u8, names, stride, cap, 0)
360 if n1 < 0 { return n1 }
361 let n2: i64 = gk_dirscan(rt, hdl, names, stride, cap, n1)
362 return n2
363}
364
365func gk_streq(a: *u8, b: *u8) -> i64 {
366 var i: i64 = 0
367 while a[i] != (0 as u8) {
368 if a[i] != b[i] { return 0 }
369 i = i + 1
370 }
371 if b[i] != (0 as u8) { return 0 }
372 return 1
373}
374
375// substring test on NUL-terminated strings. gk_contains takes a PATH and reads a file; this takes the
376// string itself -- two different questions that a shared name would have conflated.
377func gk_has(hay: *u8, ned: *u8) -> i64 {
378 let hl: i64 = gk_len(hay)
379 let nl: i64 = gk_len(ned)
380 if nl == 0 { return 1 }
381 if nl > hl { return 0 }
382 var i: i64 = 0
383 while i <= hl - nl {
384 var j: i64 = 0
385 while j < nl {
386 if hay[i + j] != ned[j] { j = nl + 9 }
387 if j < nl { j = j + 1 }
388 }
389 if j == nl { return 1 }
390 i = i + 1
391 }
392 return 0
393}
394
395func gk_cat(buf: *u8, off: i64, s: *u8) -> i64 {
396 var o: i64 = off
397 var i: i64 = 0
398 while s[i] != (0 as u8) { buf[o] = s[i]; o = o + 1; i = i + 1 }
399 return o
400}
401
402func gk_catn(buf: *u8, off: i64, v: i64) -> i64 {
403 var o: i64 = off
404 var m: i64 = v
405 if m == 0 { buf[o] = 48 as u8; return o + 1 }
406 if m < 0 { buf[o] = 45 as u8; o = o + 1; m = 0 - m }
407 let t: *u8 = sys_mmap(32)
408 var k: i64 = 0
409 while m > 0 { t[k] = ((48 + (m % 10)) as u8); m = m / 10; k = k + 1 }
410 var i: i64 = 0
411 while i < k { buf[o] = t[k - 1 - i]; o = o + 1; i = i + 1 }
412 return o
413}
414
415func gk_write_all(fd: i64, buf: *u8, n: i64) -> i64 {
416 var done: i64 = 0
417 var go: i64 = 1
418 while go == 1 {
419 if done >= n { go = 0 } else {
420 let w: i64 = sys_write(fd, ((buf as i64) + done) as *u8, n - done)
421 if w <= 0 { go = 0 } else { done = done + w }
422 }
423 }
424 return done
425}
426
427func gk_say(buf: *u8, n: i64) -> i64 { return gk_write_all(1, buf, n) }
428
429func gk_join(dst: *u8, dir: *u8, name: *u8) -> i64 {
430 var o: i64 = 0
431 var i: i64 = 0
432 while dir[i] != (0 as u8) { dst[o] = dir[i]; o = o + 1; i = i + 1 }
433 dst[o] = 47 as u8; o = o + 1
434 i = 0
435 while name[i] != (0 as u8) { dst[o] = name[i]; o = o + 1; i = i + 1 }
436 dst[o] = 0 as u8
437 return o
438}
439
440// ---- fixtures -----------------------------------------------------------------------------------
441
442func gk_mkdir(path: *u8) -> i64 { return sys_mkdir(path, 493) } // 0755
443
444// THE PER-RUN FIXTURE DIRECTORY, and the reason it is a shared primitive rather than a habit.
445//
446// The estate already holds half this law: gate scratch belongs in /tmp/<gate>/ and must never share a
447// fixture with a production beat (nx_gate_fixture_ratchet_gate enforces it). That half makes the directory
448// per-GATE. It is NOT per-RUN, and the missing half was measured on 2026-09-03: two runs of
449// nx_bck_elide_gate launched in the same second against two DIFFERENT compilers both returned verdict=SKIP
450// with "the named compiler built none of the fixtures". Neither run was faulty -- they overwrote each
451// other's fixtures in the one directory both had hardcoded. Run sequentially, the same gate on the same
452// box is 13/13 GREEN.
453//
454// WHY IT SURVIVED UNNOTICED: it fails in the ABSTAINING direction. A false SKIP acquits nothing and
455// raises no alarm -- it silently destroys the evidence and charges the next reader the whole
456// investigation. That is strictly worse than a false RED, which someone would have chased.
457//
458// The discriminator is a MICROSECOND CLOCK READING, deliberately not a pid: sys_getpid is absent from this
459// tree's syscall shim, and nx_getpid_const_probe exists precisely because a pid here has been observed to
460// read as a constant. A uniqueness token that can silently collapse to a constant is worse than none,
461// because every collision then looks exactly like the bug this function exists to remove.
462//
463// Compose it as: gk_fixture_dir("nx_my_gate", root) then gk_join(path, root, "case_a.s").
464// The directory is CREATED by this call, so a caller cannot forget to make it.
465func gk_fixture_dir(gate: *u8, out: *u8) -> i64 {
466 var o: i64 = gk_cat(out, 0, "/tmp/" as *u8)
467 o = gk_cat(out, o, gate)
468 o = gk_cat(out, o, "_" as *u8)
469 o = gk_catn(out, o, sys_clock_now_us())
470 gk_mkdir(out)
471 return o
472}
473
474// write `text` to `path`, truncating. Returns bytes written, or -1.
475func gk_write(path: *u8, text: *u8) -> i64 {
476 let fd: i64 = sys_openat_wr(path, 420) // 0644
477 if fd < 0 { return 0 - 1 }
478 let n: i64 = gk_len(text)
479 let w: i64 = gk_write_all(fd, text, n)
480 sys_close(fd)
481 if w != n { return 0 - 1 }
482 return w
483}
484
485// gk_append removed 2026-08-07: nx_unwired measured it as defined-and-never-called. A library
486// primitive with no consumer is speculation, not capability -- if a gate needs it, it comes back with
487// the caller that justifies it. ★ AN UNUSED HELPER IS NOT "AVAILABLE", IT IS UNTESTED.
488
489func gk_read(path: *u8, buf: *u8, cap: i64) -> i64 {
490 let fd: i64 = sys_openat_rd(path)
491 if fd < 0 { return 0 - 1 }
492 var total: i64 = 0
493 var go: i64 = 1
494 while go == 1 {
495 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, cap - total)
496 if r <= 0 { go = 0 } else {
497 total = total + r
498 if total >= cap { go = 0 }
499 }
500 }
501 sys_close(fd)
502 return total
503}
504
505func gk_size(path: *u8) -> i64 {
506 let fd: i64 = sys_openat_rd(path)
507 if fd < 0 { return 0 - 1 }
508 let n: i64 = sys_lseek(fd, 0, 2)
509 sys_close(fd)
510 return n
511}
512
513func gk_exists(path: *u8) -> i64 {
514 let fd: i64 = sys_openat_rd(path)
515 if fd < 0 { return 0 }
516 sys_close(fd)
517 return 1
518}
519
520func gk_rm(path: *u8) -> i64 { return sys_unlinkat(path) }
521
522// A symlink is how a gate FORCES a concurrent-write race deterministically without putting test-only
523// scaffolding in production code: point an organ's output at its own input, and its preserve-append
524// mutates the file in the exact window between the read and the install.
525func gk_symlink(target: *u8, linkpath: *u8) -> i64 { return sys_symlinkat(target, linkpath) }
526
527// does `path` contain `needle`?
528func gk_contains(path: *u8, needle: *u8) -> i64 {
529 let buf: *u8 = sys_mmap(GK_BUF)
530 let n: i64 = gk_read(path, buf, GK_BUF)
531 if n <= 0 { return 0 }
532 let pl: i64 = gk_len(needle)
533 if pl == 0 { return 0 }
534 if pl > n { return 0 }
535 var i: i64 = 0
536 let lim: i64 = n - pl
537 while i <= lim {
538 var j: i64 = 0
539 var ok: i64 = 1
540 while j < pl { if buf[i + j] != needle[j] { ok = 0; j = pl } else { j = j + 1 } }
541 if ok == 1 { return 1 }
542 i = i + 1
543 }
544 return 0
545}
546
547// count occurrences of `needle` in `path`
548func gk_count(path: *u8, needle: *u8) -> i64 {
549 let buf: *u8 = sys_mmap(GK_BUF)
550 let n: i64 = gk_read(path, buf, GK_BUF)
551 if n <= 0 { return 0 }
552 let pl: i64 = gk_len(needle)
553 if pl == 0 { return 0 }
554 if pl > n { return 0 }
555 var hits: i64 = 0
556 var i: i64 = 0
557 let lim: i64 = n - pl
558 while i <= lim {
559 var j: i64 = 0
560 var ok: i64 = 1
561 while j < pl { if buf[i + j] != needle[j] { ok = 0; j = pl } else { j = j + 1 } }
562 if ok == 1 { hits = hits + 1; i = i + pl } else { i = i + 1 }
563 }
564 return hits
565}
566
567// ---- run another organ ---------------------------------------------------------------------------
568// fork + execve + wait4. The child's stdout/stderr go to /dev/null so a gate's own report stays
569// readable; assertions are made on EXIT CODE and resulting FILE STATE, which is what the shell gates
570// were really checking anyway.
571// Up to four arguments; pass 0 for unused. Returns the child's exit code, or -1 if it could not run.
572// A child that DIED BY SIGNAL has no exit status: WEXITSTATUS is 0, so wait_exit_code reports the exit
573// code of a CLEAN run. MEASURED 2026-08-18 (lane B, nx_gate_roster_run trial): two gates the 60 s watchdog
574// KILLED journaled `GREEN exit=0 ms=60443` -- the bounded runner turned every timeout into a pass, and
575// gk_run reports a SEGFAULTING subject the same way. Shell convention 128+signal (137 SIGKILL, 139 SIGSEGV)
576// makes the death visible AND non-zero, so every caller that branches on rc != 0 sees it without change.
577// ★★★★★★ A WATCHDOG-KILLED SUBJECT THAT RETURNS THE EXIT CODE OF A CLEAN ONE TURNS EVERY TIMEOUT INTO A
578// PASS -- BOUNDING THE WAIT WITHOUT REPORTING THE KILL IS HALF A FIX, AND THE MISSING HALF READS GREEN.
579func gk_wait_code(status: i64) -> i64 {
580 // DELEGATES to nx_syscalls' wait_status_rc since 2026-08-25. This body was RIGHT, and it was
581 // the estate's ONLY correct copy of the rule while nx_tool_run -- the exec primitive behind
582 // /api/gate_run and 51 other consumers -- returned 0 for a SEGFAULTING child. The rule now
583 // lives beside wait_exit_code and wait_term_signal, where every consumer can reach it. The
584 // NAME is kept so gatekit's callers are byte-untouched: ONE RULER, TWO NAMES, never two rulers.
585 return wait_status_rc(status)
586}
587
588func gk_run(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8) -> i64 {
589 let argv: *i64 = sys_mmap(64) as *i64
590 var n: i64 = 0
591 argv[n] = elf as i64; n = n + 1
592 if a1 != (0 as *u8) { argv[n] = a1 as i64; n = n + 1 }
593 if a2 != (0 as *u8) { argv[n] = a2 as i64; n = n + 1 }
594 if a3 != (0 as *u8) { argv[n] = a3 as i64; n = n + 1 }
595 if a4 != (0 as *u8) { argv[n] = a4 as i64; n = n + 1 }
596 argv[n] = 0
597 let envp: *i64 = sys_mmap(16) as *i64
598 envp[0] = 0
599
600 let pid: i64 = sys_fork()
601 if pid < 0 { return 0 - 1 }
602 if pid == 0 {
603 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 420)
604 if dn >= 0 {
605 sys_dup3(dn, 1, 0)
606 sys_dup3(dn, 2, 0)
607 }
608 sys_execve(elf, argv, envp)
609 sys_exit(127) // exec failed -- the parent sees 127, never a false PASS
610 }
611 let st: *i64 = sys_mmap(16) as *i64
612 st[0] = 0
613 sys_wait4(pid, st, 0)
614 return gk_wait_code(st[0])
615}
616
617// ---- run and CAPTURE the child's output -----------------------------------------------------------
618// gk_run asserts on exit code and file state, which covered every tooth in the first two conversions.
619// The remaining gates assert on MESSAGES -- ranking order, reported df, the exact refusal text -- so
620// without capture they cannot be converted and the shell scripts survive on a technicality.
621// ★ THE LAST THIRD-PARTY DEPENDENCY IS ALWAYS HELD UP BY THE ONE PRIMITIVE NOBODY BUILT.
622//
623// ⚠ sys_pipe2 takes `*i64`, but the kernel writes an int[2] -- TWO 32-BIT ints into eight bytes. So
624// both descriptors arrive PACKED IN ONE i64: read fd in the low half, write fd in the high half.
625// Reading fds[1] would hand you uninitialised memory as a file descriptor.
626// Same, but the child's stdin is `infile`. A hook organ's whole input arrives on fd 0, so without
627// this a gate can only test the half of it that comes from argv -- and the untested half is where the
628// parsing lives. Pass 0 for infile to inherit.
629func gk_run_capture_in(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, infile: *u8, outbuf: *u8, cap: i64, outlen: *i64) -> i64 {
630 return gk_run_cap2(elf, a1, a2, a3, a4, infile, outbuf, cap, outlen)
631}
632
633func gk_run_capture(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, outbuf: *u8, cap: i64, outlen: *i64) -> i64 {
634 return gk_run_cap2(elf, a1, a2, a3, a4, 0 as *u8, outbuf, cap, outlen)
635}
636
637// Same, but the child is KILLED after `ms` milliseconds. Pass 0 for ms to inherit the old
638// unbounded behaviour EXACTLY -- no watchdog, no poll, no setpgid, byte-for-byte as before.
639// A HANGING SUBJECT IS NOT A SLOW SUBJECT: IT IS A DENIAL OF SERVICE ON EVERY SWEEP THAT
640// INCLUDES IT, AND IT PRODUCES NO VERDICT TO EXPLAIN ITSELF.
641func gk_run_capture_ms(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, ms: i64, outbuf: *u8, cap: i64, outlen: *i64) -> i64 {
642 return gk_run_cap3(elf, a1, a2, a3, a4, 0 as *u8, ms, outbuf, cap, outlen)
643}
644
645// cap2 keeps its exact previous semantics by passing ms = 0. ONE implementation, not two: a bug
646// fixed in one copy of a 45-line runner is a bug still live in the other.
647func gk_run_cap2(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, infile: *u8, outbuf: *u8, cap: i64, outlen: *i64) -> i64 {
648 return gk_run_cap3(elf, a1, a2, a3, a4, infile, 0, outbuf, cap, outlen)
649}
650
651func gk_run_cap3(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, infile: *u8, ms: i64, outbuf: *u8, cap: i64, outlen: *i64) -> i64 {
652 let fds: *i64 = sys_mmap(64) as *i64
653 fds[0] = 0
654 if sys_pipe2(fds, 0) != 0 { outlen[0] = 0; return 0 - 1 }
655 let rfd: i64 = fds[0] & GK_MAGIC_4294967295
656 let wfd: i64 = (fds[0] >> 32) & GK_MAGIC_4294967295
657
658 let argv: *i64 = sys_mmap(64) as *i64
659 var n: i64 = 0
660 argv[n] = elf as i64; n = n + 1
661 if a1 != (0 as *u8) { argv[n] = a1 as i64; n = n + 1 }
662 if a2 != (0 as *u8) { argv[n] = a2 as i64; n = n + 1 }
663 if a3 != (0 as *u8) { argv[n] = a3 as i64; n = n + 1 }
664 if a4 != (0 as *u8) { argv[n] = a4 as i64; n = n + 1 }
665 argv[n] = 0
666 let envp: *i64 = sys_mmap(16) as *i64
667 envp[0] = 0
668
669 let pid: i64 = sys_fork()
670 if pid < 0 { sys_close(rfd); sys_close(wfd); outlen[0] = 0; return 0 - 1 }
671 if pid == 0 {
672 // Own process group so the watchdog can reach EVERYTHING this subject starts.
673 // GUARDED on ms > 0: every unbounded caller keeps its exact previous semantics.
674 // Best-effort -- ENOSYS on lanes whose syscall table lacks setpgid; the poll
675 // deadline below bounds the wait regardless.
676 if ms > 0 { sys_setpgid(0, 0) }
677 if infile != (0 as *u8) {
678 let ifd: i64 = sys_openat_rd(infile)
679 if ifd >= 0 { sys_dup3(ifd, 0, 0); sys_close(ifd) }
680 }
681 sys_dup3(wfd, 1, 0)
682 sys_dup3(wfd, 2, 0)
683 sys_close(rfd)
684 sys_close(wfd)
685 sys_execve(elf, argv, envp)
686 sys_exit(127)
687 }
688 // ⚠ the parent MUST close the write end, or the read never sees EOF and the gate hangs forever.
689 var wd: i64 = 0
690 if ms > 0 {
691 wd = sys_fork()
692 if wd == 0 {
693 // Close the inherited pipe ends FIRST. Holding the WRITE end keeps EOF from
694 // arriving until the watchdog exits, which makes every bounded run cost its
695 // FULL timeout even when the subject finished instantly (measured 5046ms on a
696 // 5000ms bound for a subject that exits at once).
697 // A WATCHDOG HOLDING THE PIPE IS INDISTINGUISHABLE FROM A SLOW SUBJECT.
698 sys_close(wfd)
699 sys_close(rfd)
700 sys_sleep_ms(ms)
701 nx_kill(0 - pid, 9)
702 nx_kill(pid, 9)
703 sys_exit(0)
704 }
705 }
706 sys_close(wfd)
707 var total: i64 = 0
708 var go: i64 = 1
709 // BOUND THE READ, not only the process. Killing the child does not close the pipe if
710 // the child forked: the grandchild inherited fds 1/2, so EOF never arrives and this
711 // loop blocks forever. MEASURED 2026-08-10: a 2000ms bound took 60,060ms against a
712 // subject that forks and exits. Process-group kill is the other half, but it is
713 // best-effort; a poll deadline needs no syscall the lane might lack.
714 // BOUND THE WAIT, NOT JUST THE PROCESS: THE CALLER IS BLOCKED BY THE PIPE, NOT THE PID.
715 // ms == 0 keeps the original blocking behaviour exactly: no deadline, no poll.
716 var deadline: i64 = 0
717 if ms > 0 { deadline = sys_now_realtime_ms() + ms + 250 }
718 let pfd: *u8 = sys_mmap(16)
719 while go == 1 {
720 if total >= cap { go = 0 }
721 if go == 1 {
722 if deadline > 0 {
723 let remain: i64 = deadline - sys_now_realtime_ms()
724 if remain <= 0 { go = 0 }
725 if go == 1 {
726 pfd[0] = (rfd & 255) as u8
727 pfd[1] = ((rfd >> 8) & 255) as u8
728 pfd[2] = ((rfd >> 16) & 255) as u8
729 pfd[3] = ((rfd >> 24) & 255) as u8
730 pfd[4] = 1 as u8
731 pfd[5] = 0 as u8
732 pfd[6] = 0 as u8
733 pfd[7] = 0 as u8
734 if sys_poll(pfd, 1, remain) <= 0 { go = 0 }
735 }
736 }
737 }
738 if go == 1 {
739 let r: i64 = sys_read(rfd, ((outbuf as i64) + total) as *u8, cap - total)
740 if r <= 0 { go = 0 } else { total = total + r }
741 }
742 }
743 sys_close(rfd)
744 outbuf[total] = 0 as u8
745 outlen[0] = total
746 let st: *i64 = sys_mmap(16) as *i64
747 st[0] = 0
748 sys_wait4(pid, st, 0)
749 // Retire the watchdog so it cannot outlive its subject and kill an unrelated pid later.
750 // A WATCHDOG THAT SURVIVES ITS SUBJECT IS A TIME BOMB AIMED AT WHOEVER INHERITS THE PID.
751 if wd > 0 {
752 nx_kill(wd, 9)
753 let ws: *i64 = sys_mmap(16) as *i64
754 sys_wait4(wd, ws, 0)
755 }
756 return gk_wait_code(st[0])
757}
758
759// does the captured output contain `needle`?
760func gk_out_has(buf: *u8, n: i64, needle: *u8) -> i64 {
761 let pl: i64 = gk_len(needle)
762 if pl == 0 { return 0 }
763 if pl > n { return 0 }
764 var i: i64 = 0
765 let lim: i64 = n - pl
766 while i <= lim {
767 var j: i64 = 0
768 var ok: i64 = 1
769 while j < pl { if buf[i + j] != needle[j] { ok = 0; j = pl } else { j = j + 1 } }
770 if ok == 1 { return 1 }
771 i = i + 1
772 }
773 return 0
774}
775
776// byte offset of `needle`, or -1 -- lets a gate assert on ORDER, which is what a ranker must be
777// tested on: "A ranks above B" is a statement about positions, not about presence.
778func gk_out_pos(buf: *u8, n: i64, needle: *u8) -> i64 {
779 let pl: i64 = gk_len(needle)
780 if pl == 0 { return 0 - 1 }
781 if pl > n { return 0 - 1 }
782 var i: i64 = 0
783 let lim: i64 = n - pl
784 while i <= lim {
785 var j: i64 = 0
786 var ok: i64 = 1
787 while j < pl { if buf[i + j] != needle[j] { ok = 0; j = pl } else { j = j + 1 } }
788 if ok == 1 { return i }
789 i = i + 1
790 }
791 return 0 - 1
792}
793
794// ---- assertions ---------------------------------------------------------------------------------
795// Counters live in a caller-owned 2-slot array: r[0]=pass, r[1]=fail. Every tooth REPORTS rather than
796// aborting, so one run shows every regression instead of only the first.
797
798func gk_ok(r: *i64, cond: i64, label: *u8) -> i64 {
799 let msg: *u8 = sys_mmap(GK_MAGIC_4096)
800 var o: i64 = 0
801 if cond == 1 {
802 r[0] = r[0] + 1
803 o = gk_cat(msg, 0, " PASS " as *u8)
804 } else {
805 r[1] = r[1] + 1
806 o = gk_cat(msg, 0, " FAIL " as *u8)
807 }
808 o = gk_cat(msg, o, label)
809 o = gk_cat(msg, o, "\n" as *u8)
810 gk_say(msg, o)
811 return cond
812}
813
814func gk_eq(r: *i64, got: i64, want: i64, label: *u8) -> i64 {
815 let msg: *u8 = sys_mmap(GK_MAGIC_4096)
816 var o: i64 = 0
817 if got == want {
818 r[0] = r[0] + 1
819 o = gk_cat(msg, 0, " PASS " as *u8)
820 o = gk_cat(msg, o, label)
821 } else {
822 r[1] = r[1] + 1
823 o = gk_cat(msg, 0, " FAIL " as *u8)
824 o = gk_cat(msg, o, label)
825 o = gk_cat(msg, o, " (got " as *u8)
826 o = gk_catn(msg, o, got)
827 o = gk_cat(msg, o, ", want " as *u8)
828 o = gk_catn(msg, o, want)
829 o = gk_cat(msg, o, ")" as *u8)
830 }
831 o = gk_cat(msg, o, "\n" as *u8)
832 gk_say(msg, o)
833 if got == want { return 1 }
834 return 0
835}
836
837func gk_head(label: *u8) -> i64 {
838 let msg: *u8 = sys_mmap(GK_MAGIC_4096)
839 var o: i64 = gk_cat(msg, 0, "== " as *u8)
840 o = gk_cat(msg, o, label)
841 o = gk_cat(msg, o, "\n" as *u8)
842 return gk_say(msg, o)
843}
844
845func gk_result(r: *i64) -> i64 {
846 let msg: *u8 = sys_mmap(GK_MAGIC_4096)
847 var o: i64 = gk_cat(msg, 0, "RESULT: " as *u8)
848 o = gk_catn(msg, o, r[0])
849 o = gk_cat(msg, o, " passed, " as *u8)
850 o = gk_catn(msg, o, r[1])
851 o = gk_cat(msg, o, " failed\n" as *u8)
852 gk_say(msg, o)
853 if r[1] > 0 { return 1 }
854 return 0
855}