code wiki / _hdl_build / nx_gate_verdict_lib.nx

nx_gate_verdict_lib.nx source

↩ module page · 193 lines · 9645 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// ★★★★★★AN INSTRUMENT THAT CANNOT READ ITS SUBJECT MUST SAY SO, NOT INDICT IT. The estate ALREADY 114// bought this law at nx_gate_bite's exit-4 split, where a subject the tool could not open used to render 115// as "UNCONTROLLED = the gate is not green at baseline" -- and reported a 13/13 GREEN gate as broken for 116// an entire evening. The same confusion lives here in mirror image: a single RED bucket folds together 117// (a) the gate genuinely FAILED, (b) the gate SUCCEEDED but spells success in a dialect this reader does 118// not know, and (c) there is no anchor at all. Those demand OPPOSITE work -- fix a subject vs migrate an 119// emitter vs fix a config -- so one bucket guarantees a misdiagnosis downstream. 120// MEASURED 2026-08-06 by running nx_gate_rollup: 73/104, and at least SIX of the 31 REDs were successes 121// in an unknown dialect -- DRIFT-WATCH=CLEAN, GOVERNANCE-GATE=GOVERNED, CMS-EXCEED=AHEAD, 122// MVAULT-WALK=DONE, MVWALK-BGTEST=DONE, LANG-MATURITY=MEASURED. The comment above this function records 123// that VALID had to be hand-added for exactly the same reason, which makes today the THIRD round. 124// ★A SUCCESS VOCABULARY THAT MUST BE WIDENED BY HAND FOR EVERY DIALECT IS AN UNBOUNDED OBLIGATION, AND 125// UNTIL SOMEONE NOTICES IT THE HEADLINE NUMBER LIES PESSIMISTICALLY -- which trains every seat to 126// discount the number, which is precisely how a genuinely RED gate goes unnoticed (one did: an escaped 127// mutation-test mutant sat behind a RED gate inverting a Rule-26 safety message, debt 1786036823). 128// So do NOT widen it a fourth time (rule 3 -- stop patching, fix the design). CLASSIFY instead, and let 129// the UNREADABLE count BE the D001 migration backlog: visible, attributable, and shrinking as gates 130// adopt nx_gate_verdict, rather than masquerading as broken subjects forever. 131// ★STILL FAIL-CLOSED: UNREADABLE is NEVER a pass. "I could not tell" must never read as "fine" -- it is 132// only attributed honestly. gv_is_pass keeps its exact old contract (rule 19); this is purely additive. 133const GV_CLASS_NOANCHOR: i64 = 0 134const GV_CLASS_PASS: i64 = 1 135const GV_CLASS_FAIL: i64 = 2 136const GV_CLASS_UNREADABLE: i64 = 3 137 138// The EXPLICIT failure vocabulary. Deliberately SHORT and closed: these are the tokens a gate emits when 139// it means "I ran and the subject is bad". Anything else with an anchor is a dialect we do not speak. 140func gv_is_explicit_fail(line: *u8, n: i64) -> i64 { 141 if gv_contains(line, n, "verdict=RED" as *u8) == 1 { return 1 } 142 if gv_contains(line, n, "VERDICT=RED" as *u8) == 1 { return 1 } 143 if gv_contains(line, n, "verdict=FAIL" as *u8) == 1 { return 1 } 144 if gv_contains(line, n, "VERDICT=FAIL" as *u8) == 1 { return 1 } 145 if gv_contains(line, n, "verdict=INVALID" as *u8) == 1 { return 1 } 146 if gv_contains(line, n, "VERDICT=INVALID" as *u8) == 1 { return 1 } 147 return 0 148} 149 150func gv_verdict_class(line: *u8, n: i64) -> i64 { 151 if gv_has_anchor(line, n) == 0 { return GV_CLASS_NOANCHOR } 152 if gv_is_pass(line, n) == 1 { return GV_CLASS_PASS } 153 if gv_is_explicit_fail(line, n) == 1 { return GV_CLASS_FAIL } 154 return GV_CLASS_UNREADABLE 155} 156 157// st_mtime SECONDS from the x86_64 struct stat. Offset 88 is the same field nx_torrent_up.nx:37 and 158// nx_torrent_daemon.nx:944 read (size is at 48); the channel itself is proven by _freshness_gate. 159// Returns -1 if the path cannot be stat'd -- fail-closed, never "0 = brand new". 160const GV_STATBUF: i64 = 256 161const GV_STAT_MTIME_OFF: i64 = 88 162 163func gv_mtime(path: *u8) -> i64 { 164 let sb: *u8 = sys_mmap(GV_STATBUF) 165 if sys_fstatat(path, sb) != 0 { sys_munmap(sb, GV_STATBUF); return 0 - 1 } 166 let mp: *i64 = ((sb as i64) + GV_STAT_MTIME_OFF) as *i64 167 let m: i64 = mp[0] 168 sys_munmap(sb, GV_STATBUF) 169 return m 170} 171 172// Whole days since a path was last written; -1 if unstattable. 173// WHY FRESHNESS BELONGS IN THE VERDICT LAYER: a gate log is a claim about NOW. One that stopped being 174// written months ago is not GREEN, it is ABANDONED -- counting its stale last line as a pass is exactly 175// the stale-green lie the ecomat DANGLING state exists to prevent. It also separates live gates from 176// one-off scratch runs without anyone hand-maintaining a denylist. 177func gv_age_days(path: *u8, now: i64) -> i64 { 178 let m: i64 = gv_mtime(path) 179 if m < 0 { return 0 - 1 } 180 if now <= m { return 0 } 181 return (now - m) / GV_MAGIC_86400 182} 183 184// classify a log file: GV_GREEN / GV_RED / GV_NONE (fail-closed) 185func gv_classify(path: *u8, buf: *u8, line: *u8) -> i64 { 186 let n: i64 = gv_slurp(path, buf, GV_LOGCAP) 187 if n <= 0 { return GV_NONE } 188 let ln: i64 = gv_last_line(buf, n, line, GV_LINECAP) 189 if ln <= 0 { return GV_NONE } 190 if gv_has_anchor(line, ln) == 0 { return GV_NONE } 191 if gv_is_pass(line, ln) == 1 { return GV_GREEN } 192 return GV_RED 193}