nx_state_spool_gate.nx source
↩ module page · 131 lines · 6304 B
1// nx_state_spool_gate.nx -- liar-killed GATE for the store-and-forward state spool (CR-R1a).
2// Proves NAS-first/local-fallback semantics OFFLINE via the deterministic fixture transport
3// (nx_spool_txfix: argv[1] contains "ok" -> delivered, else fail): spooled lines persist; a
4// working transport drains them; a FAILING transport keeps them (offline = NOTHING LOST -- the
5// negative control); partial failure keeps exactly the failed lines in order without blocking
6// later lines; a MISSING transport binary keeps everything (fail-safe, never fabricates
7// delivery); empty spool is a no-op. usage: nx_state_spool_gate [txfix_elf]
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_state_spool_core.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc); explicit, not leaned on transitively
12
13func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14
15// MIGRATED to the shared emitter 2026-08-06 (debt 1785516350). The old body mmapped a 32-byte
16// scratch and never freed it -- 4096B leaked per call at page granularity. nxi_out runs the SAME
17// ccz_cat_num digits through a shim that always frees, so emitted bytes are identical.
18func t_putn(v: i64) -> i64 { nxi_out(v); return 0 }
19
20func t_check(name: *u8, got: i64, want: i64, passp: *i64) -> i64 {
21 t_puts("T " as *u8)
22 t_puts(name)
23 t_puts(" got=" as *u8)
24 t_putn(got)
25 t_puts(" want=" as *u8)
26 t_putn(want)
27 if got == want { t_puts(" PASS\n" as *u8); passp[0] = passp[0] + 1 } else { t_puts(" FAIL\n" as *u8) }
28 return 0
29}
30
31// count occurrences of needle in the spool file (bounded read)
32func t_count(path: *u8, needle: *u8) -> i64 {
33 let buf: *u8 = sys_mmap(65536)
34 let n: i64 = ccz_read(path, buf, 65535)
35 if n <= 0 { return 0 }
36 let nl: i64 = ccz_slen(needle)
37 var cnt: i64 = 0
38 var i: i64 = 0
39 while i + nl <= n {
40 var k: i64 = 0
41 var ok: i64 = 1
42 while k < nl { if buf[i+k] != needle[k] { ok = 0; k = nl } k = k + 1 }
43 if ok == 1 { cnt = cnt + 1; i = i + nl } else { i = i + 1 }
44 }
45 return cnt
46}
47
48func main(argc: i64, argv: *i64) -> i64 {
49 // TRANSPORT RESOLUTION (2026-08-06). The default was /tmp/nx_spool_txfix.sov.elf and nothing else
50 // was ever tried. NAS /tmp is mounted noexec, so the fixture could never be executed: ALL FIVE
51 // drain tests failed while the two that never exec it passed. The gate read 2/7 RED for a purely
52 // environmental reason, and that is indistinguishable from a real regression unless you notice the
53 // failures partition EXACTLY along "does this test exec the transport". Probe an executable
54 // location first and PRINT the chosen one, so this gate can never again report the subject as
55 // broken because of its own harness. An explicit argv[1] still wins.
56 var tx: *u8 = "../nx_spool_txfix.elf" as *u8
57 let x0: i64 = sys_openat_rd(tx)
58 if x0 >= 0 { sys_close(x0) } else {
59 tx = "./nx_spool_txfix.elf" as *u8
60 let x1: i64 = sys_openat_rd(tx)
61 if x1 >= 0 { sys_close(x1) } else { tx = "/tmp/nx_spool_txfix.sov.elf" as *u8 }
62 }
63 if argc >= 2 { tx = argv[1] as *u8 }
64 t_puts("SSP-GATE transport=" as *u8); t_puts(tx); t_puts("\n" as *u8)
65 let pass: *i64 = sys_mmap(16) as *i64
66 pass[0] = 0
67 let sp: *u8 = "/tmp/ssp_gate.spool" as *u8
68 let lk: *u8 = "/tmp/ssp_gate.spool.lock" as *u8
69 let tp: *u8 = "/tmp/ssp_gate.spool.tmp" as *u8
70 let sent: *i64 = sys_mmap(16) as *i64
71 let kept: *i64 = sys_mmap(16) as *i64
72
73 // reset any prior fixture state (drain-to-empty with a spool that may not exist is T6's job;
74 // here just truncate)
75 let rfd: i64 = sys_openat_wr(sp, 420)
76 if rfd >= 0 { sys_close(rfd) }
77
78 // T1 spool persists lines
79 ssp_spool(sp, lk, "beat ok alpha" as *u8)
80 ssp_spool(sp, lk, "beat ok bravo" as *u8)
81 t_check("spool-persists" as *u8, t_count(sp, "beat ok " as *u8), 2, pass)
82
83 // T2 drain with working transport -> all delivered, spool empty
84 ssp_drain(sp, lk, tp, tx, sent, kept)
85 var ok2: i64 = 0
86 if sent[0] == 2 { if kept[0] == 0 { if t_count(sp, "beat" as *u8) == 0 { ok2 = 1 } } }
87 t_check("drain-delivers-all" as *u8, ok2, 1, pass)
88
89 // T3 NEG-CONTROL: failing transport -> nothing lost (offline is a normal state)
90 ssp_spool(sp, lk, "beat FAIL charlie" as *u8)
91 ssp_drain(sp, lk, tp, tx, sent, kept)
92 var ok3: i64 = 0
93 if sent[0] == 0 { if kept[0] == 1 { if t_count(sp, "FAIL charlie" as *u8) == 1 { ok3 = 1 } } }
94 t_check("negctl-offline-keeps" as *u8, ok3, 1, pass)
95
96 // T4 partial: ok + FAIL + ok -> 2 delivered, exactly the failed one kept
97 ssp_spool(sp, lk, "beat ok delta" as *u8)
98 ssp_spool(sp, lk, "beat ok echo" as *u8)
99 ssp_drain(sp, lk, tp, tx, sent, kept)
100 var ok4: i64 = 0
101 if sent[0] == 2 { if kept[0] == 1 { if t_count(sp, "FAIL charlie" as *u8) == 1 { if t_count(sp, "ok delta" as *u8) == 0 { ok4 = 1 } } } }
102 t_check("partial-drain-order" as *u8, ok4, 1, pass)
103
104 // T5 head-of-line non-blocking: a new deliverable line drains PAST the stuck one
105 ssp_spool(sp, lk, "beat ok foxtrot" as *u8)
106 ssp_drain(sp, lk, tp, tx, sent, kept)
107 var ok5: i64 = 0
108 if sent[0] == 1 { if kept[0] == 1 { if t_count(sp, "FAIL charlie" as *u8) == 1 { ok5 = 1 } } }
109 t_check("head-of-line-nonblocking" as *u8, ok5, 1, pass)
110
111 // T6 missing transport binary -> exec fails (127) -> everything kept (never fabricates delivery)
112 ssp_drain(sp, lk, tp, "/tmp/ssp_gate_no_such_transport.elf" as *u8, sent, kept)
113 var ok6: i64 = 0
114 if sent[0] == 0 { if kept[0] == 1 { if t_count(sp, "FAIL charlie" as *u8) == 1 { ok6 = 1 } } }
115 t_check("missing-transport-failsafe" as *u8, ok6, 1, pass)
116
117 // T7 empty spool drain = clean no-op (drain the kept line away first with a fresh truncate)
118 let zfd: i64 = sys_openat_wr(sp, 420)
119 if zfd >= 0 { sys_close(zfd) }
120 ssp_drain(sp, lk, tp, tx, sent, kept)
121 var ok7: i64 = 0
122 if sent[0] == 0 { if kept[0] == 0 { ok7 = 1 } }
123 t_check("empty-spool-noop" as *u8, ok7, 1, pass)
124
125 t_puts("SSP-GATE pass=" as *u8)
126 t_putn(pass[0])
127 t_puts("/7 verdict=" as *u8)
128 if pass[0] == 7 { t_puts("GREEN\n" as *u8); return 0 }
129 t_puts("RED\n" as *u8)
130 return 1
131}