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}