nx_gate_lib.nx source
↩ module page · 161 lines · 6955 B
1// nx_gate_lib.nx -- THE SHARED BASE FOR GATE ORGANS (gl_*), authored 2026-07-25 to eat debt seq938.
2//
3// WHY THIS EXISTS: nx_feed_gate, nx_p384_ecdh_gate and nx_tls12_prf_gate were each written with a PRIVATE
4// copy of the same six helpers (slen/puts/putn/bcat/bcatn/contains) under three different prefixes --
5// fdg_ / pkg_ / tpg_. That is the exact drift the flip family already paid to fix with nx_flip_lib.nx's
6// fx_* base: a fix to one copy fixes ONE gate, and the family silently diverges. Same mistake, same cure.
7//
8// It also fixes the contract every gate member must honour, so a new gate inherits it instead of
9// re-deriving it:
10// (1) print FAILURES ONLY -- a roster of PASS lines buries the failure the gate exists to surface,
11// and the plan-runner snippet cap (200B) will truncate it away entirely.
12// (2) emit a `verdict=GREEN|RED pass=N/M` anchor LAST so the D001 judge and the beat can read it.
13// (3) counts[0]=total counts[1]=pass, so a case can never be "counted" without being adjudicated.
14// (4) a case asserts BOTH an exit code AND a required output substring -- an exit code alone lets a
15// silently-wrong organ pass, and a substring alone lets a crashed one pass.
16//
17// license_tier: ORIGINAL No hw writes (Rule 26).
18import "nx_syscalls.nx"
19import "nx_tool_run.nx"
20
21const GL_OUTCAP: i64 = 1048576 // per-case fork capture (1 MiB: some probed pages exceed 64KB)
22const GL_BUF: i64 = 262144
23const GL_AVCAP: i64 = 192
24const GL_SCRATCH: i64 = 32
25const GL_EXIT_RED: i64 = 1
26
27func gl_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
28func gl_puts(s: *u8) -> i64 { sys_write(1, s, gl_slen(s)); return 0 }
29func gl_putn(v: i64) -> i64 {
30 let b: *u8 = sys_mmap(24)
31 var m: i64 = v
32 if m == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
33 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
34 var nd: i64 = 0
35 var t: i64 = m
36 while t > 0 { nd = nd + 1; t = t / 10 }
37 var i: i64 = nd - 1
38 while i >= 0 { b[i] = (48 + (m % 10)) as u8; m = m / 10; i = i - 1 }
39 sys_write(1, b, nd)
40 return 0
41}
42// append a NUL-terminated string into a buffer at offset o; returns the new offset
43func gl_bcat(b: *u8, o: i64, s: *u8) -> i64 {
44 var i: i64 = 0
45 var oo: i64 = o
46 while s[i] != (0 as u8) { b[oo] = s[i]; oo = oo + 1; i = i + 1 }
47 return oo
48}
49// append a signed decimal into a buffer at offset o; returns the new offset
50func gl_bcatn(b: *u8, o: i64, v: i64) -> i64 {
51 var oo: i64 = o
52 var m: i64 = v
53 if m < 0 { b[oo] = 45 as u8; oo = oo + 1; m = 0 - m }
54 if m == 0 { b[oo] = 48 as u8; return oo + 1 }
55 let tb: *u8 = sys_mmap(24)
56 var nd: i64 = 0
57 while m > 0 { tb[nd] = (48 + (m % 10)) as u8; m = m / 10; nd = nd + 1 }
58 var i: i64 = nd - 1
59 while i >= 0 { b[oo] = tb[i]; oo = oo + 1; i = i - 1 }
60 return oo
61}
62// does haystack[0..n) contain the NUL-terminated needle?
63func gl_contains(h: *u8, n: i64, needle: *u8) -> i64 {
64 var m: i64 = 0
65 while needle[m] != (0 as u8) { m = m + 1 }
66 if m == 0 { return 1 }
67 if m > n { return 0 }
68 var i: i64 = 0
69 var found: i64 = 0
70 while i <= n - m {
71 if found == 0 {
72 var j: i64 = 0
73 var ok: i64 = 1
74 while j < m {
75 if ok == 1 { if h[i+j] != needle[j] { ok = 0 } }
76 j = j + 1
77 }
78 if ok == 1 { found = 1 }
79 }
80 i = i + 1
81 }
82 return found
83}
84// adjudicate an already-computed boolean. counts[0]=total counts[1]=pass. FAILURES ONLY are printed.
85func gl_check(nm: *u8, ok: i64, counts: *i64) -> i64 {
86 counts[0] = counts[0] + 1
87 if ok == 1 { counts[1] = counts[1] + 1; return 0 }
88 gl_puts("FAIL " as *u8); gl_puts(nm); gl_puts("\n" as *u8)
89 return 0
90}
91// fork a live organ and assert BOTH its exit code AND a required output substring.
92// Buffered variant: FAIL rows accumulate into pb at offset po; returns the new offset.
93func gl_case(nm: *u8, path: *u8, av: *i64, want_exit: i64, want_sub: *u8,
94 counts: *i64, pb: *u8, po: i64) -> i64 {
95 let out: *u8 = sys_mmap(GL_OUTCAP)
96 let ol: *i64 = sys_mmap(GL_SCRATCH) as *i64
97 let rc: i64 = tr_run_capture(path, av, out, GL_OUTCAP, ol)
98 var ok: i64 = 1
99 if rc != want_exit { ok = 0 }
100 if gl_contains(out, ol[0], want_sub) == 0 { ok = 0 }
101 counts[0] = counts[0] + 1
102 if ok == 1 { counts[1] = counts[1] + 1; return po }
103 var o: i64 = po
104 o = gl_bcat(pb, o, "FAIL " as *u8)
105 o = gl_bcat(pb, o, nm)
106 o = gl_bcat(pb, o, " exit=" as *u8); o = gl_bcatn(pb, o, rc)
107 o = gl_bcat(pb, o, " want_exit=" as *u8); o = gl_bcatn(pb, o, want_exit)
108 o = gl_bcat(pb, o, " want=" as *u8); o = gl_bcat(pb, o, want_sub)
109 o = gl_bcat(pb, o, "\n" as *u8)
110 return o
111}
112// build a 1-URL argv vector for the common "fork <elf> <url>" case
113func gl_av1(path: *u8, a1: *u8) -> *i64 {
114 let av: *i64 = sys_mmap(GL_AVCAP) as *i64
115 av[0] = path as i64
116 av[1] = a1 as i64
117 av[2] = 0
118 return av
119}
120// APPEND one durable verdict line to a log path. WHY THIS EXISTS (measured 2026-07-25): the tickless clock
121// daemon's clk_dispatch_run forks a job, wait4()s it, and counts it as dispatched whenever it RAN -- it does
122// NOT distinguish exit 0 from exit 1, and the child's stdout goes NOWHERE. So an hourly gate that turns RED
123// turns red in a VACUUM: nothing records it and nothing alerts. Rather than change the shared dispatcher
124// (blast radius: every clock job in the ecosystem), each gate now writes its OWN durable evidence -- the
125// same knowledge/status/*.log idiom the auction/census organs already use.
126func gl_log(path: *u8, banner: *u8, counts: *i64) -> i64 {
127 let fd: i64 = sys_openat_append(path, 0x1a4)
128 if fd < 0 { return 0 - 1 }
129 let b: *u8 = sys_mmap(512)
130 var o: i64 = 0
131 o = gl_bcatn(b, o, sys_now_realtime_sec())
132 o = gl_bcat(b, o, " " as *u8)
133 o = gl_bcat(b, o, banner)
134 o = gl_bcat(b, o, " verdict=" as *u8)
135 if counts[1] == counts[0] { o = gl_bcat(b, o, "GREEN" as *u8) } else { o = gl_bcat(b, o, "RED" as *u8) }
136 o = gl_bcat(b, o, " pass=" as *u8)
137 o = gl_bcatn(b, o, counts[1])
138 o = gl_bcat(b, o, "/" as *u8)
139 o = gl_bcatn(b, o, counts[0])
140 b[o] = 10 as u8
141 o = o + 1
142 sys_write(fd, b, o)
143 sys_close(fd)
144 return 0
145}
146
147// emit the D001 verdict anchor LAST, then flush buffered FAIL rows. Returns the process exit code.
148func gl_verdict(banner: *u8, counts: *i64, ref: *u8, pb: *u8, po: i64) -> i64 {
149 gl_puts(banner)
150 gl_puts("\nverdict=" as *u8)
151 if counts[1] == counts[0] { gl_puts("GREEN" as *u8) } else { gl_puts("RED" as *u8) }
152 gl_puts(" pass=" as *u8); gl_putn(counts[1])
153 gl_puts("/" as *u8); gl_putn(counts[0])
154 if ref[0] != (0 as u8) { gl_puts(" " as *u8); gl_puts(ref) }
155 gl_puts("\n" as *u8)
156 if po > 0 { sys_write(1, pb, po) }
157 if counts[1] == counts[0] { return 0 }
158 return GL_EXIT_RED
159}
160
161func main() -> i64 { return 0 }