nx_joblost_gate.nx
buildroot/runtime/nx_joblost_gate.nx
about
nx_joblost_gate.nx -- WHICH ASYNC JOBS DIED, as distinct from which are still running.
WHY THIS EXISTS, measured 2026-08-07. Under load (loadavg 18.18 on the 8-thread box,
nx_tools_api_se in D-state, /api/health 503 "upstream backend gave no response inside the edge
window") nx_tools_api claims a job id with O_CREAT|O_EXCL and can then die before the worker ever
produces output. The caller gets the bare JSON object `{}`. Two of my writes were destroyed that
way inside five minutes and I only noticed because I happened to read the row back. The estate's
standing rule for a dropped response -- VERIFY BY ARTEFACT -- fails silently here, because the
artefact was never written and the job record is EMPTY rather than ABSENT.
---------------------------------------------------------------------------------------------
v1 OF THIS GATE WAS WRONG AND SHIPPED GREEN. It counted every zero-byte .claim as a LOST job.
Reading nx_tools_api afterwards showed the claim is created EMPTY and only filled at completion
(ta_job_put -> sys_renameat), so a zero-byte claim is ALSO the normal representation of a job that
is still RUNNING. v1 therefore merged "in flight" with "died" under the alarming name.
★★★★★★ I WROTE "UNKNOWN IS ITS OWN BUCKET" INTO THIS FILE'S OWN HEADER AND THEN BROKE IT IN THE
SAME HOUR. A LAW YOU CAN RECITE IS NOT A LAW YOU HAVE APPLIED.
v1 also declared the imprecision as unavoidable -- "separating in flight from wedged needs a clock
and this gate does not have one". IT HAD ONE ALL ALONG: the job id IS the epoch second, printed in
every filename.
★★★★★★ THE INFORMATION I DECLARED UNAVAILABLE WAS ENCODED IN THE NAME OF THE FILE I WAS ALREADY
READING. "THIS CANNOT BE MEASURED" IS A CLAIM THAT MUST BE EARNED, EXACTLY LIKE ANY OTHER.
FIVE BUCKETS, each with a different remedy, none folded into another:
DONE state=DONE was written -- healthy, nothing to do
INFLIGHT empty claim, no .out, young -- probably running; NOT an alarm
LOST empty claim, no .out, OLD -- the id was reserved and nothing ever came back
ORPHAN empty claim but .out EXISTS -- the worker produced output and never renamed the claim,
so a poller waiting on state=DONE waits forever
REAPED a tombstone was APPENDED -- adjudicated and closed by nx_jobclaim_reap; the claim
keeps its original marker, so only a reader that tests
REAPED FIRST can see it (2026-08-20)
The age threshold is DECLARED and argv-overridable, and it is deliberately generous: jobs run with
tmo=0 (no timeout) precisely so they can outlive the request window, so a tight bound would accuse
healthy long jobs. ★ A HEURISTIC THAT GATES AN ALARM MUST BE WRONG IN THE DIRECTION OF SILENCE.
license_tier: ORIGINAL expect_exit: 0
dependencies 3 imports · 0 importers
imports: syscalls.nxnx_gate_verdict.nxnx_jobclaim_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 44 | const JL_JOBS: *u8 = "_jobs" as *u8 |
| 45 | const JL_RATCHET: *u8 = "knowledge/status/joblost_ratchet.conf" as *u8 |
| 46 | const JL_DEFAULT_FLOOR: i64 = 0 |
| 56 | const JL_DBUF: i64 = 65536 |
| 57 | const JL_FBUF: i64 = 4096 |
| 58 | const JL_PATH: i64 = 1024 |
| 59 | const JL_MODE: i64 = 420 |
| 61 | const JL_TOTAL: i64 = 0 |
| 62 | const JL_DONE: i64 = 1 |
| 63 | const JL_INFLIGHT: i64 = 2 |
| 64 | const JL_LOST: i64 = 3 |
| 65 | const JL_ORPHAN: i64 = 4 |
| 66 | const JL_UNKNOWN: i64 = 5 |
| 76 | const JL_REAPED: i64 = 6 |
| 80 | const JL_SLOTS: i64 = 7 |
| 86 | const JL_IDBASE: i64 = 8 |
| 87 | const JL_LOSTCAP: i64 = 32 |
functions
| 89 | func jl_slen(p: *u8) -> i64 |
| 100 | func jl_ends_claim(nm: *u8, nl: i64) -> i64 called by 1: jl_scan |
| 110 | func jl_parse_id(nm: *u8, nl: i64) -> i64 called by 1: jl_scan |
| 127 | func jl_read(path: *u8, buf: *u8, cap: i64) -> i64 |
| 140 | func jl_exists(path: *u8) -> i64 called by 1: jl_scan |
| 150 | func jl_scan(dir: *u8, now: i64, maxage: i64, out: *i64, dbuf: *u8, fbuf: *u8, pbuf: *u8) -> i64 |
| 257 | func jl_clean(dir: *u8, dbuf: *u8, pbuf: *u8) -> i64 |
| 295 | func jl_touch(path: *u8, body: *u8) -> i64 |
| 305 | func jl_mkname(buf: *u8, dir: *u8, id: i64, ext: *u8) -> i64 called by 1: main |
| 325 | func jl_floor(defaulted: *i64, fbuf: *u8, seen: *i64) -> i64 |
| 340 | func main(argc: i64, argv: *i64) -> i64 |