code wiki / _hdl_build / nx_vizsla_bills_gate.nx
nx_vizsla_bills_gate.nx source
↩ module page · 282 lines · 11909 B
1// nx_vizsla_bills_gate.nx -- VIZSLA bills gate: the recurring-payables tracker
2// proven against a HAND-COMPUTED fixture (oracle = this header). No mocks:
3// drives the real organ over real seg_store PAID records + civil-date math.
4//
5// bills (soon_days=7): rent $1800 due-1 MANUAL; electric $120 due-15 MANUAL;
6// netflix $15.99 due-20 AUTO; water $45 due-10 MANUAL; phone $60 due-25 MANUAL.
7// paid: PAID rent 2026-06-02 (June); PAID netflix 2026-05-20 (MAY, not June).
8// today=2026-06-14:
9// rent paid this month -> PAID, next due 2026-07-01
10// electric due-15 unpaid, 1d off -> DUE (<=soon) owed 12000
11// netflix due-20 unpaid (May!=Jun)-> DUE (6d) owed 1599
12// water due-10 unpaid, -4d -> OVERDUE owed 4500
13// phone due-25 unpaid, 11d>7 -> UPCOMING owed 6000
14// => bills=5 paid=1 upcoming=1 due=2 overdue=1 owed_cents=24099
15// DATA-DRIVEN soon_days=14: phone 11d<=14 -> DUE => due=3 upcoming=0 (owed same).
16//
17// Rows:
18// 1 loud-fail-missing-bills status on absent file => exit 1
19// 2 loud-fail-bad-cadence a non-MONTHLY bill => malformed => exit 1
20// 3 load-paid scanned=2 new=2 segment=seg-5001
21// 4 idempotent-reload new=0 dup_instore=2 segment=none (law 10)
22// 5 bill-paid rent PAID, next due 2026-07-01
23// 6 bill-due electric DUE due=2026-06-15
24// 7 bill-overdue water OVERDUE
25// 8 bill-upcoming phone UPCOMING
26// 9 paid-this-month-scoped netflix DUE (its PAID was MAY, not June)
27// 10 status-verdict 5/1/1/2/1 owed_cents=24099
28// 11 data-driven-soon-days soon_days=14 => due=3 upcoming=0, same binary
29// 12 status-determinism byte-identical twice
30// Evidence: VIZSLA-BILL-GATE -> stdout + knowledge/status/vizsla_gate.log; exit 0 iff 12/12.
31// Tamper: bogus instrument argv[1] => all rows FAIL => RED.
32// spec: knowledge/specs/2026-06-10-nishi-vizsla-ladder.md license_tier: ORIGINAL
33import "nx_syscalls.nx"
34import "nx_gate_verdict.nx"
35
36func rg_slen(s: *u8) -> i64 {
37 var n: i64 = 0
38 while s[n] != (0 as u8) { n = n + 1 }
39 return n
40}
41
42func rg_p(s: *u8) -> i64 {
43 sys_write(1, s, rg_slen(s))
44 return 0
45}
46
47func rg_cat(dst: *u8, off: i64, s: *u8) -> i64 {
48 var i: i64 = 0
49 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
50 return off + i
51}
52
53func rg_catn(dst: *u8, off: i64, v: i64) -> i64 {
54 var o: i64 = off
55 var m: i64 = v
56 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
57 let t: *u8 = sys_mmap(28)
58 var k: i64 = 0
59 if m == 0 { t[0] = 48 as u8; k = 1 }
60 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
61 var i: i64 = 0
62 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
63 return o + k
64}
65
66func rg_write(path: *u8, content: *u8) -> i64 {
67 let fd: i64 = sys_openat_wr(path, 0x1a4)
68 if fd < 0 { return 0 - 1 }
69 sys_write(fd, content, rg_slen(content))
70 sys_close(fd)
71 return 0
72}
73
74func rg_readall(path: *u8, szout: *i64) -> *u8 {
75 let fd: i64 = sys_openat_rd(path)
76 if fd < 0 { szout[0] = 0 - 1; return 0 as *u8 }
77 let sz: i64 = sys_lseek(fd, 0, 2)
78 sys_lseek(fd, 0, 0)
79 let buf: *u8 = sys_mmap(sz + 64)
80 var got: i64 = 0
81 var n: i64 = 1
82 while n > 0 {
83 n = sys_read(fd, (buf as i64 + got) as *u8, 65536)
84 if n > 0 { got = got + n }
85 }
86 sys_close(fd)
87 szout[0] = got
88 return buf
89}
90
91func rg_run(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, outpath: *u8) -> i64 {
92 let pid: i64 = sys_fork()
93 if pid == 0 {
94 if (outpath as i64) != 0 {
95 let ofd: i64 = sys_openat_wr(outpath, 0x1a4)
96 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) }
97 }
98 let argv: *i64 = sys_mmap(64) as *i64
99 argv[0] = elf as i64
100 var i: i64 = 1
101 if (a1 as i64) != 0 { argv[i] = a1 as i64; i = i + 1 }
102 if (a2 as i64) != 0 { argv[i] = a2 as i64; i = i + 1 }
103 if (a3 as i64) != 0 { argv[i] = a3 as i64; i = i + 1 }
104 if (a4 as i64) != 0 { argv[i] = a4 as i64; i = i + 1 }
105 argv[i] = 0
106 let envp: *i64 = sys_mmap(16) as *i64
107 envp[0] = 0
108 sys_execve(elf, argv, envp)
109 sys_exit(127)
110 }
111 let st: *i64 = sys_mmap(16) as *i64
112 sys_wait4(pid, st, 0)
113 let sig: i64 = st[0] & 0x7f
114 if sig != 0 { return 128 + sig }
115 return (st[0] >> 8) & 0xff
116}
117
118func rg_has(path: *u8, needle: *u8) -> i64 {
119 let szp: *i64 = sys_mmap(16) as *i64
120 let b: *u8 = rg_readall(path, szp)
121 let sz: i64 = szp[0]
122 let n: i64 = rg_slen(needle)
123 if sz < n { return 0 }
124 var i: i64 = 0
125 while i + n <= sz {
126 var ok: i64 = 1
127 var j: i64 = 0
128 while j < n {
129 if b[i + j] != needle[j] { ok = 0; j = n } else { j = j + 1 }
130 }
131 if ok == 1 { return 1 }
132 i = i + 1
133 }
134 return 0
135}
136
137func rg_fileeq(p1: *u8, p2: *u8) -> i64 {
138 let s1: *i64 = sys_mmap(16) as *i64
139 let s2: *i64 = sys_mmap(16) as *i64
140 let b1: *u8 = rg_readall(p1, s1)
141 let b2: *u8 = rg_readall(p2, s2)
142 if s1[0] != s2[0] { return 0 }
143 if s1[0] <= 0 { return 0 }
144 var i: i64 = 0
145 while i < s1[0] {
146 if b1[i] != b2[i] { return 0 }
147 i = i + 1
148 }
149 return 1
150}
151
152func rg_row(name: *u8, pass: i64) -> i64 {
153 rg_p("ROW " as *u8)
154 rg_p(name)
155 if pass == 1 { rg_p(" PASS\n" as *u8) } else { rg_p(" FAIL\n" as *u8) }
156 return pass
157}
158
159func main(argc: i64, argv: *i64) -> i64 {
160 rg_p("=== VIZSLA BILLS GATE: hand-computed recurring-payables KATs ===\n" as *u8)
161 var rb: *u8 = "buildroot/_build/nx_vizsla_bills.sov.elf" as *u8
162 if argc > 1 { rb = argv[1] as *u8 }
163 let pr: i64 = sys_openat_rd(rb)
164 if pr >= 0 { sys_close(pr) }
165 else {
166 rg_p(" instrument missing -> rebuilding via durable runner\n" as *u8)
167 rg_run("_offc/nx_sov_build_run.elf" as *u8, "nx_vizsla_bills" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, "/tmp/bl_rebuild.out" as *u8)
168 }
169
170 let pfx: *u8 = sys_mmap(128)
171 var po: i64 = 0
172 po = rg_cat(pfx, po, "/tmp/blG" as *u8); po = rg_catn(pfx, po, sys_now_us()); po = rg_cat(pfx, po, "-" as *u8); pfx[po] = 0 as u8
173
174 rg_write("/tmp/bl_bills.txt" as *u8, "CONF soon_days 7\nBILL rent Landlord 180000 1 MONTHLY MANUAL\nBILL electric PowerCo 12000 15 MONTHLY MANUAL\nBILL netflix Netflix 1599 20 MONTHLY AUTO\nBILL water CityWater 4500 10 MONTHLY MANUAL\nBILL phone TelCo 6000 25 MONTHLY MANUAL\n" as *u8)
175 rg_write("/tmp/bl_bills14.txt" as *u8, "CONF soon_days 14\nBILL rent Landlord 180000 1 MONTHLY MANUAL\nBILL electric PowerCo 12000 15 MONTHLY MANUAL\nBILL netflix Netflix 1599 20 MONTHLY AUTO\nBILL water CityWater 4500 10 MONTHLY MANUAL\nBILL phone TelCo 6000 25 MONTHLY MANUAL\n" as *u8)
176 rg_write("/tmp/bl_badcad.txt" as *u8, "CONF soon_days 7\nBILL gym Gym 3000 5 WEEKLY MANUAL\n" as *u8)
177 rg_write("/tmp/bl_log.txt" as *u8, "PAID 2026-06-02 rent 180000 june-rent\nPAID 2026-05-20 netflix 1599 may-auto\n" as *u8)
178
179 let fb: *u8 = "/tmp/bl_bills.txt" as *u8
180 let flog: *u8 = "/tmp/bl_log.txt" as *u8
181 var pass: i64 = 0
182 var r: i64 = 0
183
184 // row 1: loud-fail missing bills file
185 let rc1: i64 = rg_run(rb, "status" as *u8, pfx, "/tmp/bl_NOPE.txt" as *u8, "2026-06-14" as *u8, "/tmp/bl_o1.txt" as *u8)
186 r = 0
187 if rc1 == 1 { r = 1 }
188 pass = pass + rg_row("loud-fail-missing-bills" as *u8, r)
189
190 // row 2: a non-MONTHLY cadence is malformed => loud-fail
191 let rc2: i64 = rg_run(rb, "status" as *u8, pfx, "/tmp/bl_badcad.txt" as *u8, "2026-06-14" as *u8, "/tmp/bl_o2.txt" as *u8)
192 r = 0
193 if rc2 == 1 { r = 1 }
194 pass = pass + rg_row("loud-fail-bad-cadence" as *u8, r)
195
196 // row 3: load 2 PAID into seg-5001
197 let rc3: i64 = rg_run(rb, "load" as *u8, flog, pfx, "5001" as *u8, "/tmp/bl_o3.txt" as *u8)
198 r = 0
199 if rc3 == 0 {
200 if rg_has("/tmp/bl_o3.txt" as *u8, "VIZSLA-BILL-LOAD scanned=2 new=2 dup_infile=0 dup_instore=0 segment=seg-5001" as *u8) == 1 { r = 1 }
201 }
202 pass = pass + rg_row("load-paid" as *u8, r)
203
204 // row 4: idempotent reload
205 let rc4: i64 = rg_run(rb, "load" as *u8, flog, pfx, "5009" as *u8, "/tmp/bl_o4.txt" as *u8)
206 r = 0
207 if rc4 == 0 {
208 if rg_has("/tmp/bl_o4.txt" as *u8, "VIZSLA-BILL-LOAD scanned=2 new=0 dup_infile=0 dup_instore=2 segment=none" as *u8) == 1 { r = 1 }
209 }
210 pass = pass + rg_row("idempotent-reload" as *u8, r)
211
212 // rows 5-10: status
213 let rc5: i64 = rg_run(rb, "status" as *u8, pfx, fb, "2026-06-14" as *u8, "/tmp/bl_st.txt" as *u8)
214 r = 0
215 if rc5 == 0 {
216 if rg_has("/tmp/bl_st.txt" as *u8, "VIZSLA-BILL id=rent payee=Landlord amount=180000 due=2026-07-01 autopay=MANUAL state=PAID" as *u8) == 1 { r = 1 }
217 }
218 pass = pass + rg_row("bill-paid" as *u8, r)
219
220 r = rg_has("/tmp/bl_st.txt" as *u8, "VIZSLA-BILL id=electric payee=PowerCo amount=12000 due=2026-06-15 autopay=MANUAL state=DUE" as *u8)
221 pass = pass + rg_row("bill-due" as *u8, r)
222
223 r = rg_has("/tmp/bl_st.txt" as *u8, "VIZSLA-BILL id=water payee=CityWater amount=4500 due=2026-06-10 autopay=MANUAL state=OVERDUE" as *u8)
224 pass = pass + rg_row("bill-overdue" as *u8, r)
225
226 r = rg_has("/tmp/bl_st.txt" as *u8, "VIZSLA-BILL id=phone payee=TelCo amount=6000 due=2026-06-25 autopay=MANUAL state=UPCOMING" as *u8)
227 pass = pass + rg_row("bill-upcoming" as *u8, r)
228
229 r = rg_has("/tmp/bl_st.txt" as *u8, "VIZSLA-BILL id=netflix payee=Netflix amount=1599 due=2026-06-20 autopay=AUTO state=DUE" as *u8)
230 pass = pass + rg_row("paid-this-month-scoped" as *u8, r)
231
232 r = rg_has("/tmp/bl_st.txt" as *u8, "VIZSLA-BILL-VERDICT bills=5 paid=1 upcoming=1 due=2 overdue=1 owed_cents=24099" as *u8)
233 pass = pass + rg_row("status-verdict" as *u8, r)
234
235 // row 11: DATA-DRIVEN soon_days=14 -> phone DUE
236 let rc11: i64 = rg_run(rb, "status" as *u8, pfx, "/tmp/bl_bills14.txt" as *u8, "2026-06-14" as *u8, "/tmp/bl_st14.txt" as *u8)
237 r = 0
238 if rc11 == 0 {
239 if rg_has("/tmp/bl_st14.txt" as *u8, "VIZSLA-BILL-VERDICT bills=5 paid=1 upcoming=0 due=3 overdue=1 owed_cents=24099" as *u8) == 1 { r = 1 }
240 }
241 pass = pass + rg_row("data-driven-soon-days" as *u8, r)
242
243 // row 12: status determinism
244 rg_run(rb, "status" as *u8, pfx, fb, "2026-06-14" as *u8, "/tmp/bl_st2.txt" as *u8)
245 r = rg_fileeq("/tmp/bl_st.txt" as *u8, "/tmp/bl_st2.txt" as *u8)
246 pass = pass + rg_row("status-determinism" as *u8, r)
247
248 let permil: i64 = (pass * 1000) / 12
249 let logfd: i64 = sys_openat_append("knowledge/status/vizsla_gate.log" as *u8, 0x1a4)
250 var fdi: i64 = 0
251 while fdi < 2 {
252 var fd: i64 = 1
253 if fdi == 1 { fd = logfd }
254 if fd > 0 {
255 let line: *u8 = sys_mmap(256)
256 var o: i64 = 0
257 o = rg_cat(line, o, "VIZSLA-BILL-GATE epoch=" as *u8)
258 o = rg_catn(line, o, sys_now_realtime_sec())
259 o = rg_cat(line, o, " rows=12 pass=" as *u8)
260 o = rg_catn(line, o, pass)
261 o = rg_cat(line, o, " permil=" as *u8)
262 o = rg_catn(line, o, permil)
263 if pass == 12 {
264 o = rg_cat(line, o, " verdict=GREEN\n" as *u8)
265 } else {
266 o = rg_cat(line, o, " verdict=RED\n" as *u8)
267 }
268 sys_write(fd, line, o)
269 }
270 fdi = fdi + 1
271 }
272 if logfd > 0 { sys_close(logfd) }
273 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
274 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
275 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
276 let ctr__dry: *i64 = gv_ctr()
277 ctr__dry[0] = pass
278 ctr__dry[1] = 12
279 let rc__dry: i64 = gv_verdict("VIZSLA-BILLS-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
280 sys_exit(rc__dry)
281 return rc__dry
282}