code wiki / _hdl_build / nx_build_admit.nx

nx_build_admit.nx

buildroot/runtime/_hdl_build/nx_build_admit.nx

36420 B582 linesdepth 5pulls 6 transitivereach 1 importersview sourcekind tooltopic build
docsdependenciesstructsconstsfunctions

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

nx_syscalls.nx nx_ioadmit_lib.nx nx_build_admit.nx nx_build_admit_gate.nx

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

main ba_werr sys_write ba_slen sys_exit sys_mmap nxa_die sys_write ↻ sys_exit ↻ nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ ba_conf_load sys_mmap ↻ ba_read sys_openat_rd sys_read sys_close ba_line_key ba_slen ↻ ba_uint_at ba_slen ↻ ba_uint_at ↻ ba_slen ↻ ba_read ↻ ba_find ba_slen ↻ ioa_ncpu ioa_measure_median sys_mmap ↻ ioa_measure sys_mmap ↻

structs

none

consts

51const BA_MEMINFO: *u8 = "/proc/meminfo" as *u8
52const BA_LOADAVG: *u8 = "/proc/loadavg" as *u8
53const BA_READCAP: i64 = 65536
54const BA_OUTCAP: i64 = 4096
58const BA_FLOOR_MB: i64 = 512
59const BA_MAX_CENTILOAD: i64 = 400
80const BA_STAT: *u8 = "/proc/stat" as *u8
83const BA_PROCSRUN_OFF: i64 = 13
84const BA_CENTI_PER_CPU: i64 = 100 // 1.00 x ncpu, in centi-load
85const BA_HARD_FACTOR: i64 = 2 // STORM CEILING multiplier: the run-queue excuse in ba_verdict ends
99const BA_PROCSBLK_OFF: i64 = 13 // strlen("procs_blocked"), as BA_PROCSRUN_OFF is strlen("procs_running")
101const BA_NCPU_FALLBACK: i64 = 4 // only if /proc/stat is unreadable; conservative, never unbounded
104const BA_KB_PER_MB: i64 = 1024
105const BA_CENTI: i64 = 100
106const BA_STDOUT: i64 = 1
107const BA_STDERR: i64 = 2
108const BA_ZERO: i64 = 48
109const BA_NINE: i64 = 57
110const BA_DOT: i64 = 46
111const BA_NL: i64 = 10
112const BA_SP: i64 = 32
113const BA_EXIT_USAGE: i64 = 2
114const BA_EXIT_DENY: i64 = 3
115const BA_EXIT_QUEUE: i64 = 4
116const BA_EXIT_UNREADABLE: i64 = 5
129const BA_REASON_GRANT: i64 = 0
130const BA_REASON_MEMORY: i64 = 1
131const BA_REASON_BLOCKED: i64 = 2
132const BA_REASON_LOAD: i64 = 3
133const BA_REASON_RUN_UNREADABLE: i64 = 4
134const BA_REASON_RUNQUEUE: i64 = 5
255const BA_CONF: *u8 = "knowledge/build_admit.conf" as *u8
256const BA_CONF_UP: *u8 = "../knowledge/build_admit.conf" as *u8
257const BA_CONF_CAP: i64 = 8192
258const BA_CONF_UNSET: i64 = 0 - 1
269const BA_BLK_SAMPLES_DEFAULT: i64 = 1
270const BA_BLK_GAP_MS_DEFAULT: i64 = 60
271const BA_MEDIAN_SLOTS: i64 = 64
274const BA_CFV_SLOTS: i64 = 64
275const BA_HASH: i64 = 35 // the comment marker ba_line_key must refuse to read as a setting
365const BA_CONFIRM: *u8 = "knowledge/status/ioconfirm.status" as *u8
366const BA_CONFIRM_UP: *u8 = "../knowledge/status/ioconfirm.status" as *u8
367const BA_CONFIRM_TS: *u8 = "ts=" as *u8
368const BA_CONFIRM_LVL: *u8 = "blocked_confirmed=" as *u8
369const BA_CONFIRM_AGE_KEY: *u8 = "confirm_max_age_s=" as *u8
370const BA_CONFIRM_CAP: i64 = 4096
371const BA_TIME_SLOTS: i64 = 64

functions

138func 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
called by 3: ba_verdictmainmain
147func ba_reason_exit(reason: i64) -> i64
called by 2: ba_verdictmain
152func ba_reason_label(reason: i64) -> *u8
called by 1: main
161func 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
called by 1: bg_verdict calls 2: ba_reason_exitba_reason
165func ba_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
166func ba_werr(s: *u8) -> i64 { sys_write(BA_STDERR, s, ba_slen(s)); return 0 }
called by 1: main calls 2: sys_writeba_slen
167func 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
168func ba_puti(b: *u8, off: i64, v: i64) -> i64
called by 1: main calls 1: sys_mmap
180func ba_read(path: *u8, buf: *u8, cap: i64) -> i64
190func ba_find(buf: *u8, n: i64, needle: *u8) -> i64
called by 1: main calls 1: ba_slen
204func ba_uint_at(buf: *u8, p: i64, n: i64) -> i64
222func ba_centi_head(buf: *u8, n: i64) -> i64
called by 1: main
287func ba_line_key(buf: *u8, n: i64, needle: *u8) -> i64
called by 2: ba_conf_loadba_confirmed calls 1: ba_slen
323func ba_conf_load(out: *i64) -> i64
376func ba_confirmed(max_age_s: i64, out: *i64) -> i64
406func main(argc: i64, argv: *i64) -> i64