code wiki / _hdl_build / nx_gate_verdict_lib.nx

nx_gate_verdict_lib.nx source

↩ module page · 149 lines · 6510 B

1// nx_gate_verdict_lib.nx -- the SHARED reader for "what does this gate log say?". NO main() ON PURPOSE. 2// 3// WHY A MODULE AND NOT A NOTE: nx_gate_rollup.nx owns a main(), so its gr_slurp/gr_last_line cannot be 4// imported by a second entry point -- the same structural trap nx_gatelib's own header documents for 5// egb_walk/tr_walk ("that is why it kept being copied rather than shared -- a structural reason, not an 6// oversight, and the fix is a module with no main() rather than a note promising to do it later"). 7// This is that module. New readers import it instead of copying; nx_gate_rollup folds into it next. 8// 9// THE VERDICT CONTRACT, in one place so it cannot drift again: a gate log's NEWEST non-empty line 10// carries the anchor. The ecosystem emits BOTH cases -- knowledge/status/deploy_ready.log, 11// fin_spine_gate.log, coordination_gate.log and pm_dashboard_gate.log use uppercase VERDICT=, while 12// rv64_runproof_gate.log and friends use lowercase verdict=. nx_gate_rollup matched lowercase ONLY, 13// which made every uppercase gate read RED the instant it was registered. Case is a spelling detail, 14// not a verdict; both are accepted here so no future reader has to rediscover that. 15// 16// FAIL-CLOSED: missing / empty / no-anchor is GV_NONE, never GREEN. "I could not tell" must never read 17// as "fine" -- that is the failure mode the gate plane exists to remove. 18// license_tier: ORIGINAL No hw writes (Rule 26). 19import "nx_syscalls.nx" 20const GV_MAGIC_86400: i64 = 86400 21 22const GV_LOGCAP: i64 = 262144 23const GV_LINECAP: i64 = 4096 24 25// verdict states 26const GV_NONE: i64 = 0 27const GV_GREEN: i64 = 1 28const GV_RED: i64 = 2 29 30func gv_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 31 32// read a whole file into buf; returns bytes read (0 if unreadable) 33func gv_slurp(path: *u8, buf: *u8, cap: i64) -> i64 { 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { return 0 } 36 var tot: i64 = 0 37 var go: i64 = 1 38 while go == 1 { 39 let r: i64 = sys_read(fd, ((buf as i64) + tot) as *u8, cap - tot) 40 if r <= 0 { go = 0 } else { tot = tot + r } 41 if tot >= cap { go = 0 } 42 } 43 sys_close(fd) 44 return tot 45} 46 47// copy the LAST non-empty line of buf[0..n) into out (NUL-terminated); returns its length 48func gv_last_line(buf: *u8, n: i64, out: *u8, cap: i64) -> i64 { 49 if n <= 0 { return 0 } 50 var e: i64 = n 51 var trimming: i64 = 1 52 while trimming == 1 { 53 if e <= 0 { trimming = 0 } else { 54 let c: i64 = buf[e-1] as i64 55 if c == 10 { e = e - 1 } else { if c == 13 { e = e - 1 } else { trimming = 0 } } 56 } 57 } 58 if e <= 0 { return 0 } 59 var s: i64 = e 60 var back: i64 = 1 61 while back == 1 { 62 if s <= 0 { back = 0 } else { 63 if buf[s-1] == (10 as u8) { back = 0 } else { s = s - 1 } 64 } 65 } 66 var ln: i64 = e - s 67 if ln > cap - 1 { ln = cap - 1 } 68 var i: i64 = 0 69 while i < ln { out[i] = buf[s+i]; i = i + 1 } 70 out[ln] = 0 as u8 71 return ln 72} 73 74// substring search over an explicit length (the line is not NUL-safe to assume) 75func gv_contains(hay: *u8, hn: i64, needle: *u8) -> i64 { 76 let nn: i64 = gv_len(needle) 77 if nn == 0 { return 0 } 78 if nn > hn { return 0 } 79 var i: i64 = 0 80 while i + nn <= hn { 81 var k: i64 = 0 82 var hit: i64 = 1 83 while k < nn { if hay[i+k] != needle[k] { hit = 0; k = nn } else { k = k + 1 } } 84 if hit == 1 { return 1 } 85 i = i + 1 86 } 87 return 0 88} 89 90// does this line carry a verdict anchor at all (either case)? 91func gv_has_anchor(line: *u8, n: i64) -> i64 { 92 if gv_contains(line, n, "verdict=" as *u8) == 1 { return 1 } 93 if gv_contains(line, n, "VERDICT=" as *u8) == 1 { return 1 } 94 return 0 95} 96 97// THE PASS VOCABULARY, in ONE place. The ecosystem does not spell success a single way: nx_ecomat_lib's 98// el_last_green has always accepted GREEN, PASS **and VALID** -- knowledge/status/spirv_khronos_crosscheck.log 99// ends `verdict=VALID` and is a healthy compiler triangulation witness. nx_gate_rollup accepted only 100// GREEN, so it would score that gate RED. Two readers with two different success vocabularies is the 101// same silent-divergence class as the case bug; both now resolve through here. 102// MEASURED: reading VALID as RED is what made this organ's first NAS run report 31 reds instead of 4. 103func gv_is_pass(line: *u8, n: i64) -> i64 { 104 if gv_contains(line, n, "verdict=GREEN" as *u8) == 1 { return 1 } 105 if gv_contains(line, n, "VERDICT=GREEN" as *u8) == 1 { return 1 } 106 if gv_contains(line, n, "verdict=PASS" as *u8) == 1 { return 1 } 107 if gv_contains(line, n, "VERDICT=PASS" as *u8) == 1 { return 1 } 108 if gv_contains(line, n, "verdict=VALID" as *u8) == 1 { return 1 } 109 if gv_contains(line, n, "VERDICT=VALID" as *u8) == 1 { return 1 } 110 return 0 111} 112 113// st_mtime SECONDS from the x86_64 struct stat. Offset 88 is the same field nx_torrent_up.nx:37 and 114// nx_torrent_daemon.nx:944 read (size is at 48); the channel itself is proven by _freshness_gate. 115// Returns -1 if the path cannot be stat'd -- fail-closed, never "0 = brand new". 116const GV_STATBUF: i64 = 256 117const GV_STAT_MTIME_OFF: i64 = 88 118 119func gv_mtime(path: *u8) -> i64 { 120 let sb: *u8 = sys_mmap(GV_STATBUF) 121 if sys_fstatat(path, sb) != 0 { sys_munmap(sb, GV_STATBUF); return 0 - 1 } 122 let mp: *i64 = ((sb as i64) + GV_STAT_MTIME_OFF) as *i64 123 let m: i64 = mp[0] 124 sys_munmap(sb, GV_STATBUF) 125 return m 126} 127 128// Whole days since a path was last written; -1 if unstattable. 129// WHY FRESHNESS BELONGS IN THE VERDICT LAYER: a gate log is a claim about NOW. One that stopped being 130// written months ago is not GREEN, it is ABANDONED -- counting its stale last line as a pass is exactly 131// the stale-green lie the ecomat DANGLING state exists to prevent. It also separates live gates from 132// one-off scratch runs without anyone hand-maintaining a denylist. 133func gv_age_days(path: *u8, now: i64) -> i64 { 134 let m: i64 = gv_mtime(path) 135 if m < 0 { return 0 - 1 } 136 if now <= m { return 0 } 137 return (now - m) / GV_MAGIC_86400 138} 139 140// classify a log file: GV_GREEN / GV_RED / GV_NONE (fail-closed) 141func gv_classify(path: *u8, buf: *u8, line: *u8) -> i64 { 142 let n: i64 = gv_slurp(path, buf, GV_LOGCAP) 143 if n <= 0 { return GV_NONE } 144 let ln: i64 = gv_last_line(buf, n, line, GV_LINECAP) 145 if ln <= 0 { return GV_NONE } 146 if gv_has_anchor(line, ln) == 0 { return GV_NONE } 147 if gv_is_pass(line, ln) == 1 { return GV_GREEN } 148 return GV_RED 149}