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}