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}