code wiki / _hdl_build / nx_supervised_dispatch_gate.nx
nx_supervised_dispatch_gate.nx
buildroot/runtime/_hdl_build/nx_supervised_dispatch_gate.nx
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
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
structs
| none |
consts
| 52 | const SDG_LOG: *u8 = "knowledge/status/supervised_dispatch_gate.log\x00" as *u8 |
| 55 | const SDG_BEAT0: i64 = 1000 // epoch of the FIRST (spawn-time) beat |
| 56 | const SDG_THRESH: i64 = 60 // staleness threshold |
| 57 | const SDG_NOW_HOT: i64 = 1010 // "now" while the job is fresh (age 10 < 60 -> ALIVE) |
| 58 | const SDG_NOW_OLD: i64 = 2000 // "now" after the kill (age 1000 > 60 -> STALLED) |
| 59 | const SDG_BEAT2: i64 = 2050 // epoch of the RESTART beat (fresh, > NOW_OLD - thresh) |
| 60 | const SDG_NOW_AL2: i64 = 2060 // "now" after restart (age 10 < 60 -> ALIVE again) |
functions
| 62 | func 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 } |
| 63 | func 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 } |
| 66 | func g_unique_tmp(buf: *u8, epoch: i64, pid: i64) -> i64 |
| 79 | func g_log(twin: *u8, name: *u8, pass: i64, detail: i64) -> i64 |
| 93 | func g_assert(twin: *u8, name: *u8, pass: i64, detail: i64) -> i64 |
| 102 | func g_scratch_paths(ms: i64, qpath: *u8, ldir: *u8, hbpath: *u8) -> i64 |
| 116 | func g_write_fixture(path: *u8) -> i64 |
| 128 | func g_reap(pid: i64) -> i64 |
| 134 | func main() -> i64 |