code wiki / _hdl_build / nx_gate_sentinel.nx

nx_gate_sentinel.nx source

↩ module page · 151 lines · 6655 B

1// nx_gate_sentinel.nx -- TEAM-OWNED watchdog over nx_game_gate (operator: 2// "keep building the team to handle the ask"). Runs the gate, compares the 3// fails count against the recorded baseline, and reacts WITHOUT Claude: 4// fails > baseline -> REGRESSION: escalate to the PM review log (pm_gap) 5// fails < baseline -> IMPROVEMENT: record a deliverable line 6// fails == baseline -> STEADY: quiet (one SENTINEL line in the gate log) 7// Baseline auto-updates after every report so each CHANGE escalates exactly 8// once (no spam, no silent drift). This is the loop-monitor pattern from 9// [[project-self-running-loop-governed-ingest-2026-06-05]] applied to the 10// game-engine substrate: the gate produces evidence, the sentinel watches it, 11// the PM log receives the escalation -- grades stay LIVE with no LLM in the 12// loop. 13// 14// Usage (from nxc2 repo root): 15// nx_gate_sentinel.elf # gate with default _offc compiler 16// nx_gate_sentinel.elf /path/to/nx_cc.elf # gate a candidate compiler 17// Exit: 0 steady/improvement/init, 1 regression, 2 gate-missing/crashed. 18// 19// license_tier: ORIGINAL 20import "nx_pm_review_log.nx" 21import "nx_syscalls.nx" 22 23func gs_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 24func gs_putn(v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64=v; if m<0 {m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8 = sys_mmap(28); var k: i64=0; if m==0 {t[0]=48;k=1}; while m>0 {t[k]=48+(m%10); 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 } 25func gs_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 26func gs_cat_n(dst: *u8, off: i64, v: i64) -> i64 { 27 var o: i64 = off 28 var m: i64 = v 29 if m < 0 { dst[o] = 45; o = o + 1; m = 0 - m } 30 let t: *u8 = sys_mmap(28) 31 var k: i64 = 0 32 if m == 0 { t[0] = 48; k = 1 } 33 while m > 0 { t[k] = 48 + (m % 10); m = m / 10; k = k + 1 } 34 var i: i64 = 0 35 while i < k { dst[o+i] = t[k-1-i]; i = i + 1 } 36 return o + k 37} 38 39// fork+exec the gate; returns its exit (=fails count), or 128+sig 40func gs_run_gate(path: *u8, arg1: *u8) -> i64 { 41 let pid: i64 = sys_fork() 42 if pid == 0 { 43 let devnull: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 44 if devnull >= 0 { sys_dup3(devnull, 1, 0) } // gate already logs to its own file 45 let argv: *i64 = sys_mmap(32) as *i64 46 argv[0] = path as i64; argv[1] = arg1 as i64; argv[2] = 0 47 let envp: *i64 = sys_mmap(16) as *i64 48 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 49 sys_execve(path, argv, envp); sys_exit(127) 50 } 51 let st: *i64 = sys_mmap(16) as *i64 52 sys_wait4(pid, st, 0) 53 let sig: i64 = st[0] & 0x7f 54 if sig != 0 { return 128 + sig } 55 return (st[0] >> 8) & 0xff 56} 57 58// read baseline int from file; -1 if absent/unparseable 59func gs_read_baseline(path: *u8) -> i64 { 60 let len_p: *i64 = sys_mmap(8) as *i64 61 let buf: *u8 = sys_read_file(path, len_p) 62 if (buf as i64) == 0 { return 0 - 1 } 63 let n: i64 = len_p[0] 64 if n <= 0 { return 0 - 1 } 65 var v: i64 = 0 66 var any: i64 = 0 67 var i: i64 = 0 68 while i < n { 69 let c: i64 = buf[i] as i64 70 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); any = 1 } } 71 i = i + 1 72 } 73 if any == 0 { return 0 - 1 } 74 return v 75} 76 77func gs_write_baseline(path: *u8, v: i64) -> i64 { 78 let fd: i64 = sys_openat_wr(path, 0x1a4) 79 if fd < 0 { return 0 - 1 } 80 let buf: *u8 = sys_mmap(64) 81 var o: i64 = gs_cat_n(buf, 0, v) 82 buf[o] = 0x0A as u8 83 sys_write(fd, buf, o + 1) 84 sys_close(fd) 85 return 0 86} 87 88// append a SENTINEL line to the gate log 89func gs_log(kind: *u8, fails: i64, baseline: i64, ts: i64) -> i64 { 90 let fd: i64 = sys_openat_append("/tmp/nishi_game_gate.log" as *u8, 0x1a4) 91 let line: *u8 = sys_mmap(256) 92 var o: i64 = 0 93 o = gs_cat(line, o, "SENTINEL ts=" as *u8) 94 o = gs_cat_n(line, o, ts) 95 o = gs_cat(line, o, " kind=" as *u8) 96 o = gs_cat(line, o, kind) 97 o = gs_cat(line, o, " fails=" as *u8) 98 o = gs_cat_n(line, o, fails) 99 o = gs_cat(line, o, " baseline=" as *u8) 100 o = gs_cat_n(line, o, baseline) 101 o = gs_cat(line, o, "\n" as *u8) 102 sys_write(1, line, o) 103 if fd >= 0 { sys_write(fd, line, o); sys_close(fd) } 104 return 0 105} 106 107func main(argc: i64, argv: *i64) -> i64 { 108 let gate: *u8 = "/tmp/nx_game_gate.sov.elf" as *u8 109 var arg1: *u8 = 0 as *u8 110 if argc >= 2 { arg1 = argv[1] as *u8 } 111 let base_path: *u8 = "/tmp/nishi_game_gate.baseline" as *u8 112 let ts: i64 = sys_now_realtime_sec() 113 114 let fails: i64 = gs_run_gate(gate, arg1) 115 if fails >= 127 { 116 gs_log("GATE-CRASHED" as *u8, fails, 0 - 1, ts) 117 let pm0: i64 = pm_open("/tmp/nishi_pm_review.log" as *u8) 118 pm_gap(pm0, "game-engine/gates" as *u8, "NEEDS-FIX" as *u8, "gate crashed or missing" as *u8, "nx_gate_sentinel could not run /tmp/nx_game_gate.sov.elf (exit>=127); rebuild via nx_sov_build_run nx_game_gate" as *u8) 119 sys_close(pm0) 120 return 2 121 } 122 123 let baseline: i64 = gs_read_baseline(base_path) 124 if baseline < 0 { 125 gs_write_baseline(base_path, fails) 126 gs_log("INIT" as *u8, fails, fails, ts) 127 return 0 128 } 129 if fails > baseline { 130 gs_log("REGRESSION" as *u8, fails, baseline, ts) 131 let pm: i64 = pm_open("/tmp/nishi_pm_review.log" as *u8) 132 pm_gap(pm, "game-engine/gates" as *u8, "NEEDS-FIX" as *u8, "GAMEGATE REGRESSION" as *u8, "nx_gate_sentinel: gate fails count rose above baseline -- see GAMEGATE lines in /tmp/nishi_game_gate.log; per-module LOCALIZATION (assembler vs compiler/source) auto-appended to /tmp/nishi_game_gate_triage.log by nx_gate_triage" as *u8) 133 sys_close(pm) 134 gs_write_baseline(base_path, fails) 135 // auto-LOCALIZE: fork the triage so the escalation arrives with the 136 // oracle-differential classification attached (best-effort; a missing 137 // triage binary must not break the sentinel). 138 gs_run_gate("/tmp/nx_gate_triage.sov.elf" as *u8, arg1) 139 return 1 140 } 141 if fails < baseline { 142 gs_log("IMPROVEMENT" as *u8, fails, baseline, ts) 143 let pm2: i64 = pm_open("/tmp/nishi_pm_review.log" as *u8) 144 pm_deliverable(pm2, "game-engine/gates" as *u8, "gate-improvement" as *u8, "nx_gate_sentinel: gate fails count dropped below baseline (fix landed); baseline updated" as *u8) 145 sys_close(pm2) 146 gs_write_baseline(base_path, fails) 147 return 0 148 } 149 gs_log("STEADY" as *u8, fails, baseline, ts) 150 return 0 151}