code wiki / _hdl_build / nx_build_admit.nx
nx_build_admit.nx
buildroot/runtime/_hdl_build/nx_build_admit.nx
about
nx_build_admit.nx -- BUILD ADMISSION CONTROL (the missing gate that let the fleet wedge the host).
INCIDENT 2026-07-20: ~10 concurrent sessions were compiling at once (registry grew 198->236 GREEN tools in
ONE session). Every /api/build forks nx_cc_sovereign (549KB) on a memory-limited Synology and NOTHING
serialized or admitted them -> the NAS went userspace-wedged (sshd could not complete a banner, DSM could
not serve a page, every sovereign daemon refused TCP) and then off the network entirely. Measured, not
guessed. nx_swarm_admit ALREADY documents itself as the gate "BEFORE any heavy launch (model decode, codec,
image-gen, BIG BUILD)" -- but /api/build never called it. A compile IS a heavy launch. This organ is the
build-shaped admission verdict, with ZERO deps beyond nx_syscalls so it can never itself be the thing that
fails under load.
LIAR-KILLED: every number is read live from /proc on the host being protected -- no estimate, no cache.
MemAvailable from /proc/meminfo (the honest figure: reclaimable included, unlike MemFree)
1-minute load from /proc/loadavg, carried as CENTI-load (integer; no floats, sovereign law)
nx_build_admit check [floor_mb] [max_centiload]
exit 0 GRANT -- headroom exists, compile now
exit 3 DENY-MEM -- below the memory floor; compiling now risks wedging the HOST, not just the build
exit 4 QUEUE -- a pileup a compiler fork would deepen. THIS ORGAN QUEUES NOTHING ITSELF; whether
the build is absorbed is the CALLER's answer. /api/build absorbs it (nx_buildq ->
plan-build-<t>- + deployq BQ-<t>, fired by nx_orchestrate on headroom) and reports
queue_launch_rc; a DIRECT caller of this detector must re-issue itself, paced.
exit 2 usage | 5 unreadable /proc (fail-CLOSED: cannot measure => cannot admit)
✅THE ABSORBER SHIPPED, AND SO DID THE STRUCTURAL HALF -- BOTH CLAIMS THIS PARAGRAPH USED TO MAKE WERE
STALE, and they are corrected here from the CODE rather than from belief (re-measured 2026-08-20).
The sentence they justified was still telling every reader NOTHING IS QUEUED long after that stopped
being true: this organ was committing the exact defect it exists to prevent -- A GOVERNOR EXPLAINING
ITSELF WRONGLY IS WORSE THAN A GOVERNOR WITH NO EXPLANATION, because the reader acts on the sentence.
(1) ma_do_build DOES consult this BEFORE forking the compiler: it calls md_exec_build_admit() and
branches on exit 3 (DENY-MEM -> 503) and exit 4 (QUEUE). The "filed rung" is CLOSED.
(2) The wait-for-opening absorber EXISTS: nx_buildq seeds plan-build-<target>- plus a deployq
BQ-<target> row whose `headroom:` precondition nx_orchestrate evaluates by forking THIS organ,
then fires the build on its first pass with headroom. Proven end to end 2026-08-20: one call
returned PUT 1 rows=1 / PUT BQ-<t> rows=18 / BUILDQ QUEUED.
(3) THE ONE THING THAT REALLY WAS BROKEN WAS AN ADOPTION GAP, NOT A MECHANISM. /api/build launches
the enqueue through nx_job_run, which refuses any name that is not an unpinned GREEN row in
tool_allowlist.conf -- and nx_buildq was PROMOTED-UNREGISTERED. That refusal IS nx_job_run's
exit 4, and it is the queue_launch_rc=4 recorded in debt 1787178882 alongside a MISSING deployq
row and an ABSENT capture file. The launcher was correct; the absorber was uncallable. One
/api/tools/register closed it. ★A LAUNCHER'S REFUSAL OF AN UNREGISTERED ORGAN IS
dependencies 2 imports · 1 importers
imports: nx_syscalls.nxnx_ioadmit_lib.nx
imported by: nx_build_admit_gate.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 51 | const BA_MEMINFO: *u8 = "/proc/meminfo" as *u8 |
| 52 | const BA_LOADAVG: *u8 = "/proc/loadavg" as *u8 |
| 53 | const BA_READCAP: i64 = 65536 |
| 54 | const BA_OUTCAP: i64 = 4096 |
| 58 | const BA_FLOOR_MB: i64 = 512 |
| 59 | const BA_MAX_CENTILOAD: i64 = 400 |
| 80 | const BA_STAT: *u8 = "/proc/stat" as *u8 |
| 83 | const BA_PROCSRUN_OFF: i64 = 13 |
| 84 | const BA_CENTI_PER_CPU: i64 = 100 // 1.00 x ncpu, in centi-load |
| 85 | const BA_HARD_FACTOR: i64 = 2 // STORM CEILING multiplier: the run-queue excuse in ba_verdict ends |
| 99 | const BA_PROCSBLK_OFF: i64 = 13 // strlen("procs_blocked"), as BA_PROCSRUN_OFF is strlen("procs_running") |
| 101 | const BA_NCPU_FALLBACK: i64 = 4 // only if /proc/stat is unreadable; conservative, never unbounded |
| 104 | const BA_KB_PER_MB: i64 = 1024 |
| 105 | const BA_CENTI: i64 = 100 |
| 106 | const BA_STDOUT: i64 = 1 |
| 107 | const BA_STDERR: i64 = 2 |
| 108 | const BA_ZERO: i64 = 48 |
| 109 | const BA_NINE: i64 = 57 |
| 110 | const BA_DOT: i64 = 46 |
| 111 | const BA_NL: i64 = 10 |
| 112 | const BA_SP: i64 = 32 |
| 113 | const BA_EXIT_USAGE: i64 = 2 |
| 114 | const BA_EXIT_DENY: i64 = 3 |
| 115 | const BA_EXIT_QUEUE: i64 = 4 |
| 116 | const BA_EXIT_UNREADABLE: i64 = 5 |
| 129 | const BA_REASON_GRANT: i64 = 0 |
| 130 | const BA_REASON_MEMORY: i64 = 1 |
| 131 | const BA_REASON_BLOCKED: i64 = 2 |
| 132 | const BA_REASON_LOAD: i64 = 3 |
| 133 | const BA_REASON_RUN_UNREADABLE: i64 = 4 |
| 134 | const BA_REASON_RUNQUEUE: i64 = 5 |
| 255 | const BA_CONF: *u8 = "knowledge/build_admit.conf" as *u8 |
| 256 | const BA_CONF_UP: *u8 = "../knowledge/build_admit.conf" as *u8 |
| 257 | const BA_CONF_CAP: i64 = 8192 |
| 258 | const BA_CONF_UNSET: i64 = 0 - 1 |
| 269 | const BA_BLK_SAMPLES_DEFAULT: i64 = 1 |
| 270 | const BA_BLK_GAP_MS_DEFAULT: i64 = 60 |
| 271 | const BA_MEDIAN_SLOTS: i64 = 64 |
| 274 | const BA_CFV_SLOTS: i64 = 64 |
| 275 | const BA_HASH: i64 = 35 // the comment marker ba_line_key must refuse to read as a setting |
| 365 | const BA_CONFIRM: *u8 = "knowledge/status/ioconfirm.status" as *u8 |
| 366 | const BA_CONFIRM_UP: *u8 = "../knowledge/status/ioconfirm.status" as *u8 |
| 367 | const BA_CONFIRM_TS: *u8 = "ts=" as *u8 |
| 368 | const BA_CONFIRM_LVL: *u8 = "blocked_confirmed=" as *u8 |
| 369 | const BA_CONFIRM_AGE_KEY: *u8 = "confirm_max_age_s=" as *u8 |
| 370 | const BA_CONFIRM_CAP: i64 = 4096 |
| 371 | const BA_TIME_SLOTS: i64 = 64 |
functions
| 138 | func ba_reason(avail_mb: i64, floor_mb: i64, load1: i64, max_load: i64, procs_run: i64, ncpu: i64, procs_blk: i64, blocked_max: i64) -> i64 |
| 147 | func ba_reason_exit(reason: i64) -> i64 |
| 152 | func ba_reason_label(reason: i64) -> *u8 called by 1: main |
| 161 | func ba_verdict(avail_mb: i64, floor_mb: i64, load1: i64, max_load: i64, procs_run: i64, ncpu: i64, procs_blk: i64, blocked_max: i64) -> i64 |
| 165 | func ba_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 166 | func ba_werr(s: *u8) -> i64 { sys_write(BA_STDERR, s, ba_slen(s)); return 0 } |
| 167 | func ba_puts(b: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var j: i64 = 0; while s[j] != (0 as u8) { b[o] = s[j]; o = o + 1; j = j + 1 } return o } called by 1: main |
| 168 | func ba_puti(b: *u8, off: i64, v: i64) -> i64 |
| 180 | func ba_read(path: *u8, buf: *u8, cap: i64) -> i64 |
| 190 | func ba_find(buf: *u8, n: i64, needle: *u8) -> i64 |
| 204 | func ba_uint_at(buf: *u8, p: i64, n: i64) -> i64 |
| 222 | func ba_centi_head(buf: *u8, n: i64) -> i64 called by 1: main |
| 287 | func ba_line_key(buf: *u8, n: i64, needle: *u8) -> i64 |
| 323 | func ba_conf_load(out: *i64) -> i64 |
| 376 | func ba_confirmed(max_age_s: i64, out: *i64) -> i64 |
| 406 | func main(argc: i64, argv: *i64) -> i64 |