code wiki / _hdl_build / nx_supervised_dispatch_gate.nx

nx_supervised_dispatch_gate.nx

buildroot/runtime/_hdl_build/nx_supervised_dispatch_gate.nx

18139 B331 linesdepth 6pulls 8 transitivereach 0 importersview sourcekind gate/prooftopic supervised
docsdependenciesstructsconstsfunctions

about

nx_supervised_dispatch_gate.nx -- the REFEREE for WMS rung M5 (supervisor binding). module: nishi-core.autonomy.supervised_dispatch_gate capability: GATE (prints a VERDICT computed from REAL checks; incl negative control + tamper) PROVES (the gate requirement, exactly): T1 dispatch-binds -- M4 picks+leases a READY rung -> M5 forks a REAL supervised child (dl_dispatch fd>=0 AND sd_spawn pid>0 AND job.state==RUNNING) T2 limit-applied -- the job is resource-LIMITED (Job-Object-with-a-limit): a child caps ITSELF via nx_prlimit(RLIMIT_AS) and the cap reads back == SD_AS_LIMIT (proven by an in-organ probe child that exits with a code that ENCODES "limit stuck", reaped here -- no fabrication) T3 beats-while-alive-- a supervised job emits a heartbeat: hbm_scan at a fresh `now` sees the spawned ws == HB_ALIVE (stalled==0) T4 kill->stalled -- KILL the supervised job (nx_kill SIGKILL); advance injected `now` past the threshold -> hbm_scan sees the missing heartbeat == HB_STALLED (stalled==1). M1 detects the death. T5 restart/reassign -- M5 restarts on the stall (the kernel/OS payoff): sd_supervise_tick returns ACTION_RESTART(1), job.pid is NEW (!= killed pid), restarts==1, and the fresh beat -> next hbm_scan == HB_ALIVE = PROVEN BY A SECOND SUPERVISED RUN. T6 NEG CONTROL -- a job that COMPLETES NORMALLY is NOT restarted (no thrash): spawn a CLEAN payload, reap its exit(0) (state==EXITED_OK); then sd_supervise_tick returns ACTION_NONE(0), restarts stays 0, no new pid. A buggy M5 that restarts on "pid not alive" FAILS HERE. T7 TAMPER -- restart storms are BOUNDED: drive 4 consecutive stalls (> SD_MAXRESTART=3) -> tick returns ACTION_ESCALATE(3), no further respawn = no infinite restart loop / car-alarm. VALIDITY (why the neg-control genuinely bites): T4 and T6 ride the SAME sd_supervise_tick/hbm_scan path with OPPOSITE stimuli -- a killed job (no fresh beat -> STALLED -> RESTART) vs a cleanly-exited job (state==EXITED_OK -> NONE). If M5 ignored the heartbeat it FAILS T4; if it restarted on every dead pid it FAILS T6. The two lanes cross-check the restart predicate exactly as the M1 gate cross-checks STALLED vs ALIVE. HERMETIC: own scratch heartbeat channel /tmp/sdg_<ms>.log + scratch queue /tmp/sdg_<ms>.tsv + lease dir /tmp/sdg_<ms>/ (mirrors the M4 gate's g_scratch_paths). Time is INJECTED (now/threshold = gate consts) so it is deterministic and re-runnable. WRITE DISCIPLINE: every evidence record = ONE buffer -> ONE locked fa_appendz to

dependencies 2 imports · 0 importers

nx_supervised_dispatch.nx nx_framed_append.nx nx_supervised_dispatch_gate.nx

imports: nx_supervised_dispatch.nxnx_framed_append.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main sd_getpid sys_now_realtime_sec sys_mmap sys_clock_gettime_real g_p g_unique_tmp fa_cat fa_catn sys_mmap ↻ sys_now_realtime_ms sys_mmap ↻ sys_clock_gettime_real ↻ g_scratch_paths fa_cat ↻ fa_catn ↻ sys_mkdir g_write_fixture sys_openat_wr an_newcx sys_mmap ↻ sys_fork sd_apply_limit nx_prlimit g_reap sys_wait4 g_assert g_p ↻ g_n g_log fa_cat ↻ fa_catn ↻ fa_appendz fa_append sys_mmap ↻ sys_openat_append sys_flock fa_write_all sys_write sys_close

structs

none

consts

52const SDG_LOG: *u8 = "knowledge/status/supervised_dispatch_gate.log\x00" as *u8
55const SDG_BEAT0: i64 = 1000 // epoch of the FIRST (spawn-time) beat
56const SDG_THRESH: i64 = 60 // staleness threshold
57const SDG_NOW_HOT: i64 = 1010 // "now" while the job is fresh (age 10 < 60 -> ALIVE)
58const SDG_NOW_OLD: i64 = 2000 // "now" after the kill (age 1000 > 60 -> STALLED)
59const SDG_BEAT2: i64 = 2050 // epoch of the RESTART beat (fresh, > NOW_OLD - thresh)
60const SDG_NOW_AL2: i64 = 2060 // "now" after restart (age 10 < 60 -> ALIVE again)

functions

62func g_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
called by 2: g_assertmain
63func g_n(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(1,"-\x00" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
called by 2: g_assertmain
66func g_unique_tmp(buf: *u8, epoch: i64, pid: i64) -> i64
called by 1: main calls 2: fa_catfa_catn
79func g_log(twin: *u8, name: *u8, pass: i64, detail: i64) -> i64
called by 1: g_assert calls 3: fa_catfa_catnfa_appendz
93func g_assert(twin: *u8, name: *u8, pass: i64, detail: i64) -> i64
called by 1: main calls 3: g_pg_ng_log
102func g_scratch_paths(ms: i64, qpath: *u8, ldir: *u8, hbpath: *u8) -> i64
called by 1: main calls 2: fa_catfa_catn
116func g_write_fixture(path: *u8) -> i64
called by 1: main calls 1: sys_openat_wr
128func g_reap(pid: i64) -> i64
called by 1: main calls 1: sys_wait4
134func main() -> i64