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}