code wiki / _hdl_build / nx_papers_beat.nx

nx_papers_beat.nx source

↩ module page · 116 lines · 5109 B

1// nx_papers_beat.nx -- the paper lane's REGRESSION BEAT body (F1131). 2// 3// ★WHY A BODY AND NOT A SCHEDULER. The ecosystem already has ONE clock: nx_clock_run reads 4// the clockjobs registry (name/interval/organ) and forks each due organ arglessly. Building a 5// second scheduler would be duplication. This is only the BODY the shared clock calls, exactly 6// parallel to nx_ecomat_beat -- one per lane, one clock for all of them. 7// 8// ★WHY IT LOGS. Standing law: a beat with no evidence line is a CLAIM, not a control. Four 9// paper gates were green but ran only when a human invoked them, so nothing would have noticed 10// a regression between sessions. This appends ONE line per beat naming every gate's exit code, 11// so "the gates are green" becomes a checkable record instead of a memory. 12// 13// Each gate is run through _offc/nx_sov_build_run.elf, which BUILDS then RUNS it and returns 14// the gate's own exit code -- so the beat also proves the lane still COMPILES, not merely that 15// a stale binary still passes. Runs from the build root (chdir'd if present). 16// 17// nx_papers_beat [logpath] default knowledge/status/papers_beat.log 18// exit 0 = every gate GREEN; 1 = at least one RED (the clock's caller can alarm on it) 19// 20// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 21import "nx_syscalls.nx" 22const PBT_MAGIC_1024: i64 = 1024 23 24const PBT_GATES: i64 = 5 25 26func bw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 27func bslen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 28func bcat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; var p: i64=o; while s[i]!=(0 as u8){ d[p]=s[i]; p=p+1; i=i+1 } return p } 29func bcatn(d: *u8, o: i64, v: i64) -> i64 { 30 var m: i64 = v; var p: i64 = o 31 if m < 0 { d[p] = 45 as u8; p = p + 1; m = 0 - m } 32 let t: *u8 = sys_mmap(24); var k: i64 = 0 33 if m == 0 { t[0] = 48 as u8; k = 1 } 34 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 35 var i: i64 = 0 36 while i < k { d[p] = t[k-1-i]; p = p + 1; i = i + 1 } 37 return p 38} 39// build+run one gate through the sovereign build runner; returns the GATE's exit code. 40// stdout/stderr are discarded: the beat's product is its evidence line, not gate chatter. 41func pbt_run(name: *u8) -> i64 { 42 let pid: i64 = sys_fork() 43 if pid == 0 { 44 let sbr: *u8 = "_offc/nx_sov_build_run.elf" as *u8 45 let argv: *i64 = sys_mmap(32) as *i64 46 let envp: *i64 = sys_mmap(16) as *i64 47 envp[0] = 0 48 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 49 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) } 50 argv[0] = sbr as i64 51 argv[1] = name as i64 52 argv[2] = 0 53 sys_execve(sbr, argv, envp) 54 sys_exit(127) 55 } 56 let st: *i64 = sys_mmap(16) as *i64 57 sys_wait4(pid, st, 0) 58 let sig: i64 = st[0] & 0x7f 59 if sig != 0 { return 128 + sig } 60 return (st[0] >> 8) & 0xff 61} 62func pbt_name(i: i64) -> *u8 { 63 if i == 0 { return "nx_paperbench_gate" as *u8 } 64 if i == 1 { return "nx_paper_forge_gate" as *u8 } 65 if i == 2 { return "nx_paper_panel_gate" as *u8 } 66 if i == 3 { return "nx_paper_audit_gate" as *u8 } 67 return "nx_papers_index_gate" as *u8 68} 69func main(argc: i64, argv: *i64) -> i64 { 70 var logp: *u8 = "knowledge/status/papers_beat.log" as *u8 71 if argc >= 2 { logp = argv[1] as *u8 } 72 // ⚠OPEN THE LOG BEFORE THE CHDIR. The log path is relative, and we are about to move 73 // into the build root; opening afterwards would bury the evidence at 74 // buildroot/knowledge/status/... on the NAS while looking correct locally (where no 75 // buildroot exists and the chdir silently fails). An fd survives chdir, a path does not. 76 let fd: i64 = sys_openat_append(logp, 0x1a4) 77 // run from the build root when one is present (the clock sparks us from nishihost) 78 sys_chdir("buildroot" as *u8) 79 80 let rc: *i64 = sys_mmap(PBT_GATES * 8) as *i64 81 var red: i64 = 0 82 var i: i64 = 0 83 while i < PBT_GATES { 84 rc[i] = pbt_run(pbt_name(i)) 85 if rc[i] != 0 { red = red + 1 } 86 i = i + 1 87 } 88 89 let line: *u8 = sys_mmap(PBT_MAGIC_1024) 90 var o: i64 = 0 91 o = bcat(line, o, "PAPERS-BEAT epoch=" as *u8) 92 o = bcatn(line, o, sys_now_realtime_sec()) 93 i = 0 94 while i < PBT_GATES { 95 o = bcat(line, o, " " as *u8) 96 o = bcat(line, o, pbt_name(i)) 97 o = bcat(line, o, "=" as *u8) 98 o = bcatn(line, o, rc[i]) 99 i = i + 1 100 } 101 o = bcat(line, o, " gates=" as *u8) 102 o = bcatn(line, o, PBT_GATES) 103 o = bcat(line, o, " red=" as *u8) 104 o = bcatn(line, o, red) 105 o = bcat(line, o, " verdict=" as *u8) 106 if red == 0 { o = bcat(line, o, "GREEN" as *u8) } else { o = bcat(line, o, "RED" as *u8) } 107 o = bcat(line, o, "\n" as *u8) 108 109 // APPEND-ONLY: history is the point; a beat that overwrites its own log cannot show a 110 // regression appearing between two runs. 111 if fd >= 0 { sys_write(fd, line, o); sys_close(fd) } 112 sys_write(1, line, o) 113 if red == 0 { sys_exit(0); return 0 } 114 sys_exit(1) 115 return 1 116}