nx_gate_green.nx source
↩ module page · 100 lines · 4175 B
1// nx_gate_green.nx -- shared LAST-VERDICT-LINE judging (the Examiner pattern
2// as a reusable lib): "the LAST line containing <anchor> must contain <pat>".
3//
4// module: nishi-core.status.gate_green
5// capability: CORE_COMPUTE
6//
7// EXTRACTED 2026-06-10 during the standards audit of nx_sreach_scorecard,
8// which had a REAL false-green bug: its line-bounding loop discarded the
9// newline index, so the pattern window ran to END OF FILE -- a "rc=0" on a
10// LATER line could green a rung whose own line said rc=1. Fixed here with the
11// found-index pattern and pinned by nx_gate_green_test KATs (the false-green
12// construction is KAT 3). Anything judging gate logs should use THIS, not a
13// private copy.
14
15import "nx_str.nx"
16import "nx_syscalls.nx"
17
18const GG_FILE_CAP: i64 = 524288
19
20// ---- TRI-STATE JUDGMENT (seq585, 2026-07-23). The 2-valued judge below could not tell
21// "this gate FAILED" from "this gate emitted no verdict I can read" -- both were 0 -- so 953
22// runnable, genuinely PASSING gates were machine-read as NOT-GREEN: a silent false negative
23// across the whole fleet, and the reason nx_gate_migrate had to hand-roll its own absence
24// check. ABSENT IS NOW A NAMED STATE. The 1/0 wrappers keep their exact contract by clamping
25// (rule 19), so all 14 live call sites stay byte-identical -- including the comms censuses
26// that legitimately read ==0 as "a missing log is not green".
27const GG_GREEN: i64 = 1
28const GG_RED: i64 = 0
29const GG_UNJUDGEABLE: i64 = 0 - 1 // anchor absent: the log says NOTHING about a verdict
30const GG_UNREADABLE: i64 = 0 - 2 // no such file / empty: nothing to judge at all
31const GG_LF: i64 = 0x0A
32
33func gg_read(path: *u8, buf: *u8, cap: i64) -> i64 {
34 let fd: i64 = sys_openat_rd(path)
35 if fd < 0 { return 0 }
36 var total: i64 = 0
37 var go: i64 = 1
38 while go == 1 {
39 let n: i64 = sys_read(fd, (((buf as i64) + total) as *u8), cap - total)
40 if n <= 0 { go = 0 }
41 if n > 0 { total = total + n; if total >= cap { go = 0 } }
42 }
43 sys_close(fd)
44 return total
45}
46
47// last index of needle in buf[0..n) or -1
48func gg_last(buf: *u8, n: i64, needle: *u8) -> i64 {
49 let nn: i64 = nx_str_len(needle)
50 if nn == 0 { return 0 - 1 }
51 var found: i64 = 0 - 1
52 var i: i64 = 0
53 while i + nn <= n {
54 var j: i64 = 0
55 var ok: i64 = 1
56 while j < nn {
57 if buf[i + j] != needle[j] { ok = 0; j = nn } else { j = j + 1 }
58 }
59 if ok == 1 { found = i }
60 i = i + 1
61 }
62 return found
63}
64
65// GREEN / RED / UNJUDGEABLE for the LAST line containing `anchor` in buf[0..n).
66// The window is THAT LINE ONLY: [anchor_pos, newline) -- never the file tail.
67func gg_line_judge(buf: *u8, n: i64, anchor: *u8, pat: *u8) -> i64 {
68 let at: i64 = gg_last(buf, n, anchor)
69 if at < 0 { return GG_UNJUDGEABLE }
70 var nl: i64 = n // no trailing newline -> line ends at EOF
71 var s: i64 = at
72 while s < n { if (buf[s] as i64) == GG_LF { nl = s; s = n } else { s = s + 1 } }
73 let wn: i64 = nl - at
74 if wn <= 0 { return GG_RED }
75 if gg_last((((buf as i64) + at) as *u8), wn, pat) >= 0 { return GG_GREEN }
76 return GG_RED
77}
78
79// 1 iff the LAST line containing `anchor` in buf[0..n) also contains `pat`.
80// UNCHANGED CONTRACT: every non-green state clamps to 0. A caller that must tell
81// "failed" from "did not say" calls gg_line_judge instead.
82func gg_line_green(buf: *u8, n: i64, anchor: *u8, pat: *u8) -> i64 {
83 if gg_line_judge(buf, n, anchor, pat) == GG_GREEN { return 1 }
84 return 0
85}
86
87// file wrapper, tri-state: adds UNREADABLE (absent/empty file) as its own named state,
88// distinct from "file present but silent" (UNJUDGEABLE) and from a real RED.
89func gg_gate_judge(path: *u8, anchor: *u8, pat: *u8) -> i64 {
90 let buf: *u8 = sys_mmap(GG_FILE_CAP)
91 let n: i64 = gg_read(path, buf, GG_FILE_CAP)
92 if n <= 0 { return GG_UNREADABLE }
93 return gg_line_judge(buf, n, anchor, pat)
94}
95
96// file wrapper: read + judge. UNCHANGED CONTRACT (1 green, 0 otherwise).
97func gg_gate_green(path: *u8, anchor: *u8, pat: *u8) -> i64 {
98 if gg_gate_judge(path, anchor, pat) == GG_GREEN { return 1 }
99 return 0
100}