code wiki / _hdl_build / nx_vizsla_relate_gate.nx
nx_vizsla_relate_gate.nx source
↩ module page · 342 lines · 15470 B
1// nx_vizsla_relate_gate.nx -- VIZSLA V4 gate: the relationships/PRM engine is
2// gate-proven against a HAND-COMPUTED fixture (the oracle is THIS header, not
3// the module under test). No mocks: it drives the real organ over real
4// seg_store records and hand-counted civil-date arithmetic.
5//
6// contacts (soon_days=14): TIER inner 30, close 90, network 180;
7// mom(inner, bday 06-20), alex(close, bday 03-15), sam(network, bday 12-01),
8// pat(close, bday -)
9// relate_log (11 events):
10// TOUCH 2026-06-10 mom ; TOUCH 2026-02-01 alex
11// OBLIGATION 2026-05-01 OWE alex book-loan 0 borrowed-book (open)
12// OBLIGATION 2026-05-10 DUE sam favor-intro 0 made-intro (open)
13// OBLIGATION 2026-04-01 OWE mom loan-200 20000 cash-loan (SETTLED below)
14// OBLIGATION 2026-05-05 DUE pat loan-50 5000 lent-cash (open)
15// SETTLE 2026-05-20 loan-200 repaid
16// THANKS 2026-06-12 mom birthday-gift (open, 2d)
17// THANKS 2026-05-01 alex referral (open, 44d)
18// THANKS 2026-04-01 sam holiday-card (SENT below -> closed)
19// SENT 2026-04-05 sam holiday-card
20// today=2026-06-14 hand-computed:
21// cadence: mom days=4 FRESH(<=30); alex days=133 DUE(>90); sam/pat NEW
22// => contacts=4 fresh=1 due=1 new=2
23// ledger : open=3 [alex OWE 0, sam DUE 0, pat DUE 5000]; mom SETTLED
24// => open=3 owe=1 due=2 owe_amount=0 due_amount=5000 settled=1
25// thanks : mom 2d PENDING(<=14), alex 44d OVERDUE; sam SENT-closed
26// => open=2 pending=1 overdue=1 sent=1
27// brief : followups=3 (alex DUE, sam/pat NEW), birthdays=1 (mom 06-20 in 6d),
28// thankyous=2, obligations=3
29// DATA-DRIVEN: contacts with soon_days=1 (only change) => thanks
30// open=2 pending=0 overdue=2 sent=1 (mom flips PENDING->OVERDUE),
31// same binary.
32//
33// Rows:
34// 1 loud-fail-missing-contacts cadence on absent file => exit 1
35// 2 load-events scanned=11 new=11 segment=seg-4001
36// 3 idempotent-reload new=0 dup_instore=11 segment=none (law 10)
37// 4 cadence-fresh mom FRESH days=4 cadence=30
38// 5 cadence-due alex DUE days=133 reconnect
39// 6 cadence-new-verdict sam NEW + verdict 4/1/1/2
40// 7 ledger-bidirectional alex OWE + pat DUE 5000 lent-cash + verdict
41// 8 ledger-settle-closes settled loan-200 ABSENT from open ledger
42// 9 thanks-pending mom birthday-gift 2d PENDING
43// 10 thanks-overdue-sent-closes alex 44d OVERDUE + verdict + sam holiday-card ABSENT
44// 11 brief-composition verdict 3/1/2/3 + mom BIRTHDAY in=6d
45// 12 data-driven-soon-days soon_days=1 file => thanks pending=0 overdue=2
46// 13 cadence-determinism byte-identical twice
47// Evidence: VIZSLA-RELATE-GATE line -> stdout + knowledge/status/vizsla_gate.log;
48// exit 0 iff 13/13. Missing-instrument tamper run = pass a bogus instrument
49// path as argv[1] => all rows FAIL => RED (proves the gate discriminates).
50// spec: knowledge/specs/2026-06-10-nishi-vizsla-ladder.md license_tier: ORIGINAL
51import "nx_syscalls.nx"
52import "nx_gate_verdict.nx"
53
54func rg_slen(s: *u8) -> i64 {
55 var n: i64 = 0
56 while s[n] != (0 as u8) { n = n + 1 }
57 return n
58}
59
60func rg_p(s: *u8) -> i64 {
61 sys_write(1, s, rg_slen(s))
62 return 0
63}
64
65func rg_cat(dst: *u8, off: i64, s: *u8) -> i64 {
66 var i: i64 = 0
67 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
68 return off + i
69}
70
71func rg_catn(dst: *u8, off: i64, v: i64) -> i64 {
72 var o: i64 = off
73 var m: i64 = v
74 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
75 let t: *u8 = sys_mmap(28)
76 var k: i64 = 0
77 if m == 0 { t[0] = 48 as u8; k = 1 }
78 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
79 var i: i64 = 0
80 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
81 return o + k
82}
83
84func rg_write(path: *u8, content: *u8) -> i64 {
85 let fd: i64 = sys_openat_wr(path, 0x1a4)
86 if fd < 0 { return 0 - 1 }
87 sys_write(fd, content, rg_slen(content))
88 sys_close(fd)
89 return 0
90}
91
92func rg_readall(path: *u8, szout: *i64) -> *u8 {
93 let fd: i64 = sys_openat_rd(path)
94 if fd < 0 { szout[0] = 0 - 1; return 0 as *u8 }
95 let sz: i64 = sys_lseek(fd, 0, 2)
96 sys_lseek(fd, 0, 0)
97 let buf: *u8 = sys_mmap(sz + 64)
98 var got: i64 = 0
99 var n: i64 = 1
100 while n > 0 {
101 n = sys_read(fd, (buf as i64 + got) as *u8, 65536)
102 if n > 0 { got = got + n }
103 }
104 sys_close(fd)
105 szout[0] = got
106 return buf
107}
108
109func rg_run(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, outpath: *u8) -> i64 {
110 let pid: i64 = sys_fork()
111 if pid == 0 {
112 if (outpath as i64) != 0 {
113 let ofd: i64 = sys_openat_wr(outpath, 0x1a4)
114 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) }
115 }
116 let argv: *i64 = sys_mmap(64) as *i64
117 argv[0] = elf as i64
118 var i: i64 = 1
119 if (a1 as i64) != 0 { argv[i] = a1 as i64; i = i + 1 }
120 if (a2 as i64) != 0 { argv[i] = a2 as i64; i = i + 1 }
121 if (a3 as i64) != 0 { argv[i] = a3 as i64; i = i + 1 }
122 if (a4 as i64) != 0 { argv[i] = a4 as i64; i = i + 1 }
123 argv[i] = 0
124 let envp: *i64 = sys_mmap(16) as *i64
125 envp[0] = 0
126 sys_execve(elf, argv, envp)
127 sys_exit(127)
128 }
129 let st: *i64 = sys_mmap(16) as *i64
130 sys_wait4(pid, st, 0)
131 let sig: i64 = st[0] & 0x7f
132 if sig != 0 { return 128 + sig }
133 return (st[0] >> 8) & 0xff
134}
135
136func rg_has(path: *u8, needle: *u8) -> i64 {
137 let szp: *i64 = sys_mmap(16) as *i64
138 let b: *u8 = rg_readall(path, szp)
139 let sz: i64 = szp[0]
140 let n: i64 = rg_slen(needle)
141 if sz < n { return 0 }
142 var i: i64 = 0
143 while i + n <= sz {
144 var ok: i64 = 1
145 var j: i64 = 0
146 while j < n {
147 if b[i + j] != needle[j] { ok = 0; j = n } else { j = j + 1 }
148 }
149 if ok == 1 { return 1 }
150 i = i + 1
151 }
152 return 0
153}
154
155func rg_fileeq(p1: *u8, p2: *u8) -> i64 {
156 let s1: *i64 = sys_mmap(16) as *i64
157 let s2: *i64 = sys_mmap(16) as *i64
158 let b1: *u8 = rg_readall(p1, s1)
159 let b2: *u8 = rg_readall(p2, s2)
160 if s1[0] != s2[0] { return 0 }
161 if s1[0] <= 0 { return 0 }
162 var i: i64 = 0
163 while i < s1[0] {
164 if b1[i] != b2[i] { return 0 }
165 i = i + 1
166 }
167 return 1
168}
169
170func rg_row(name: *u8, pass: i64) -> i64 {
171 rg_p("ROW " as *u8)
172 rg_p(name)
173 if pass == 1 { rg_p(" PASS\n" as *u8) } else { rg_p(" FAIL\n" as *u8) }
174 return pass
175}
176
177func main(argc: i64, argv: *i64) -> i64 {
178 rg_p("=== VIZSLA RELATE GATE: hand-computed PRM KATs (bidirectional ledger + thank-yous + brief) ===\n" as *u8)
179 var rb: *u8 = "buildroot/_build/nx_vizsla_relate.sov.elf" as *u8
180 if argc > 1 { rb = argv[1] as *u8 }
181 let pr: i64 = sys_openat_rd(rb)
182 if pr >= 0 { sys_close(pr) }
183 else {
184 rg_p(" instrument missing -> rebuilding via durable runner\n" as *u8)
185 rg_run("_offc/nx_sov_build_run.elf" as *u8, "nx_vizsla_relate" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, "/tmp/vzl_rebuild.out" as *u8)
186 }
187
188 // epoch-unique virgin store prefix
189 let pfx: *u8 = sys_mmap(128)
190 var po: i64 = 0
191 po = rg_cat(pfx, po, "/tmp/vzlG" as *u8)
192 po = rg_catn(pfx, po, sys_now_us())
193 po = rg_cat(pfx, po, "-" as *u8)
194 pfx[po] = 0 as u8
195
196 let contacts: *u8 = "CONF soon_days 14\nTIER inner 30\nTIER close 90\nTIER network 180\nCONTACT mom Mom inner 06-20\nCONTACT alex Alex-Mentor close 03-15\nCONTACT sam Sam-Colleague network 12-01\nCONTACT pat Pat-Friend close -\n" as *u8
197 rg_write("/tmp/vzl_contacts.txt" as *u8, contacts)
198 // same data, soon_days 1 (the ONLY change): the window is data, not code
199 let contacts1: *u8 = "CONF soon_days 1\nTIER inner 30\nTIER close 90\nTIER network 180\nCONTACT mom Mom inner 06-20\nCONTACT alex Alex-Mentor close 03-15\nCONTACT sam Sam-Colleague network 12-01\nCONTACT pat Pat-Friend close -\n" as *u8
200 rg_write("/tmp/vzl_contacts1.txt" as *u8, contacts1)
201 let log: *u8 = "TOUCH 2026-06-10 mom weekly-call\nTOUCH 2026-02-01 alex lunch\nOBLIGATION 2026-05-01 OWE alex book-loan 0 borrowed-book\nOBLIGATION 2026-05-10 DUE sam favor-intro 0 made-intro\nOBLIGATION 2026-04-01 OWE mom loan-200 20000 cash-loan\nOBLIGATION 2026-05-05 DUE pat loan-50 5000 lent-cash\nSETTLE 2026-05-20 loan-200 repaid\nTHANKS 2026-06-12 mom birthday-gift\nTHANKS 2026-05-01 alex referral\nTHANKS 2026-04-01 sam holiday-card\nSENT 2026-04-05 sam holiday-card\n" as *u8
202 rg_write("/tmp/vzl_log.txt" as *u8, log)
203
204 let fco: *u8 = "/tmp/vzl_contacts.txt" as *u8
205 let flog: *u8 = "/tmp/vzl_log.txt" as *u8
206 var pass: i64 = 0
207 var r: i64 = 0
208
209 // row 1: loud-fail on missing contacts
210 let rc1: i64 = rg_run(rb, "cadence" as *u8, pfx, "/tmp/vzl_NOPE.txt" as *u8, "2026-06-14" as *u8, "/tmp/vzl_out1.txt" as *u8)
211 r = 0
212 if rc1 == 1 { r = 1 }
213 pass = pass + rg_row("loud-fail-missing-contacts" as *u8, r)
214
215 // row 2: load 11 events into seg-4001
216 let rc2: i64 = rg_run(rb, "load" as *u8, flog, pfx, "4001" as *u8, "/tmp/vzl_out2.txt" as *u8)
217 r = 0
218 if rc2 == 0 {
219 if rg_has("/tmp/vzl_out2.txt" as *u8, "VIZSLA-RELATE-LOAD scanned=11 new=11 dup_infile=0 dup_instore=0 segment=seg-4001" as *u8) == 1 { r = 1 }
220 }
221 pass = pass + rg_row("load-events" as *u8, r)
222
223 // row 3: idempotent re-load (law 10)
224 let rc3: i64 = rg_run(rb, "load" as *u8, flog, pfx, "4009" as *u8, "/tmp/vzl_out3.txt" as *u8)
225 r = 0
226 if rc3 == 0 {
227 if rg_has("/tmp/vzl_out3.txt" as *u8, "VIZSLA-RELATE-LOAD scanned=11 new=0 dup_infile=0 dup_instore=11 segment=none" as *u8) == 1 { r = 1 }
228 }
229 pass = pass + rg_row("idempotent-reload" as *u8, r)
230
231 // row 4-6: cadence
232 let rc4: i64 = rg_run(rb, "cadence" as *u8, pfx, fco, "2026-06-14" as *u8, "/tmp/vzl_cad.txt" as *u8)
233 r = 0
234 if rc4 == 0 {
235 if rg_has("/tmp/vzl_cad.txt" as *u8, "VIZSLA-RELATE-CADENCE contact=mom tier=inner last=2026-06-10 days=4 cadence=30 state=FRESH action=none" as *u8) == 1 { r = 1 }
236 }
237 pass = pass + rg_row("cadence-fresh" as *u8, r)
238
239 r = rg_has("/tmp/vzl_cad.txt" as *u8, "VIZSLA-RELATE-CADENCE contact=alex tier=close last=2026-02-01 days=133 cadence=90 state=DUE action=reconnect" as *u8)
240 pass = pass + rg_row("cadence-due" as *u8, r)
241
242 r = 0
243 if rg_has("/tmp/vzl_cad.txt" as *u8, "VIZSLA-RELATE-CADENCE contact=sam tier=network last=never days=-1 cadence=180 state=NEW action=introduce" as *u8) == 1 {
244 if rg_has("/tmp/vzl_cad.txt" as *u8, "VIZSLA-RELATE-CADENCE-VERDICT contacts=4 fresh=1 due=1 new=2" as *u8) == 1 { r = 1 }
245 }
246 pass = pass + rg_row("cadence-new-verdict" as *u8, r)
247
248 // row 7-8: ledger (bidirectional + settle closes)
249 let rc7: i64 = rg_run(rb, "ledger" as *u8, pfx, fco, 0 as *u8, "/tmp/vzl_led.txt" as *u8)
250 r = 0
251 if rc7 == 0 {
252 if rg_has("/tmp/vzl_led.txt" as *u8, "VIZSLA-RELATE-OBLIG contact=alex dir=OWE ref=book-loan amount=0 desc=borrowed-book state=OPEN" as *u8) == 1 {
253 if rg_has("/tmp/vzl_led.txt" as *u8, "VIZSLA-RELATE-OBLIG contact=pat dir=DUE ref=loan-50 amount=5000 desc=lent-cash state=OPEN" as *u8) == 1 {
254 if rg_has("/tmp/vzl_led.txt" as *u8, "VIZSLA-RELATE-OBLIG-VERDICT open=3 owe=1 due=2 owe_amount=0 due_amount=5000 settled=1" as *u8) == 1 { r = 1 }
255 }
256 }
257 }
258 pass = pass + rg_row("ledger-bidirectional" as *u8, r)
259
260 // row 8: the settled obligation is ABSENT from the open ledger (additive
261 // close). Guard the negative assertion with a liveness check so it cannot
262 // pass vacuously on empty output (no-cheat: a tamper must read RED here too).
263 r = 0
264 if rg_has("/tmp/vzl_led.txt" as *u8, "VIZSLA-RELATE-OBLIG-VERDICT" as *u8) == 1 {
265 if rg_has("/tmp/vzl_led.txt" as *u8, "ref=loan-200" as *u8) == 0 { r = 1 }
266 }
267 pass = pass + rg_row("ledger-settle-closes" as *u8, r)
268
269 // row 9-10: thanks (pending / overdue + sent closes)
270 let rc9: i64 = rg_run(rb, "thanks" as *u8, pfx, fco, "2026-06-14" as *u8, "/tmp/vzl_thx.txt" as *u8)
271 r = 0
272 if rc9 == 0 {
273 if rg_has("/tmp/vzl_thx.txt" as *u8, "VIZSLA-RELATE-THANKS contact=mom occasion=birthday-gift owed=2026-06-12 days=2 state=PENDING" as *u8) == 1 { r = 1 }
274 }
275 pass = pass + rg_row("thanks-pending" as *u8, r)
276
277 r = 0
278 if rg_has("/tmp/vzl_thx.txt" as *u8, "VIZSLA-RELATE-THANKS contact=alex occasion=referral owed=2026-05-01 days=44 state=OVERDUE" as *u8) == 1 {
279 if rg_has("/tmp/vzl_thx.txt" as *u8, "VIZSLA-RELATE-THANKS-VERDICT open=2 pending=1 overdue=1 sent=1" as *u8) == 1 {
280 if rg_has("/tmp/vzl_thx.txt" as *u8, "occasion=holiday-card" as *u8) == 0 { r = 1 }
281 }
282 }
283 pass = pass + rg_row("thanks-overdue-sent-closes" as *u8, r)
284
285 // row 11: brief composition
286 let rc11: i64 = rg_run(rb, "brief" as *u8, pfx, fco, "2026-06-14" as *u8, "/tmp/vzl_brf.txt" as *u8)
287 r = 0
288 if rc11 == 0 {
289 if rg_has("/tmp/vzl_brf.txt" as *u8, " BIRTHDAY contact=mom on=06-20 in=6d" as *u8) == 1 {
290 if rg_has("/tmp/vzl_brf.txt" as *u8, "VIZSLA-RELATE-BRIEF-VERDICT followups=3 birthdays=1 thankyous=2 obligations=3" as *u8) == 1 { r = 1 }
291 }
292 }
293 pass = pass + rg_row("brief-composition" as *u8, r)
294
295 // row 12: DATA-DRIVEN soon_days=1 file => thanks flips, same binary
296 let rc12: i64 = rg_run(rb, "thanks" as *u8, pfx, "/tmp/vzl_contacts1.txt" as *u8, "2026-06-14" as *u8, "/tmp/vzl_thx1.txt" as *u8)
297 r = 0
298 if rc12 == 0 {
299 if rg_has("/tmp/vzl_thx1.txt" as *u8, "VIZSLA-RELATE-THANKS-VERDICT open=2 pending=0 overdue=2 sent=1" as *u8) == 1 { r = 1 }
300 }
301 pass = pass + rg_row("data-driven-soon-days" as *u8, r)
302
303 // row 13: cadence determinism
304 rg_run(rb, "cadence" as *u8, pfx, fco, "2026-06-14" as *u8, "/tmp/vzl_cad2.txt" as *u8)
305 r = rg_fileeq("/tmp/vzl_cad.txt" as *u8, "/tmp/vzl_cad2.txt" as *u8)
306 pass = pass + rg_row("cadence-determinism" as *u8, r)
307
308 let permil: i64 = (pass * 1000) / 13
309 let logfd: i64 = sys_openat_append("knowledge/status/vizsla_gate.log" as *u8, 0x1a4)
310 var fdi: i64 = 0
311 while fdi < 2 {
312 var fd: i64 = 1
313 if fdi == 1 { fd = logfd }
314 if fd > 0 {
315 let line: *u8 = sys_mmap(256)
316 var o: i64 = 0
317 o = rg_cat(line, o, "VIZSLA-RELATE-GATE epoch=" as *u8)
318 o = rg_catn(line, o, sys_now_realtime_sec())
319 o = rg_cat(line, o, " rows=13 pass=" as *u8)
320 o = rg_catn(line, o, pass)
321 o = rg_cat(line, o, " permil=" as *u8)
322 o = rg_catn(line, o, permil)
323 if pass == 13 {
324 o = rg_cat(line, o, " verdict=GREEN\n" as *u8)
325 } else {
326 o = rg_cat(line, o, " verdict=RED\n" as *u8)
327 }
328 sys_write(fd, line, o)
329 }
330 fdi = fdi + 1
331 }
332 if logfd > 0 { sys_close(logfd) }
333 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
334 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
335 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
336 let ctr__dry: *i64 = gv_ctr()
337 ctr__dry[0] = pass
338 ctr__dry[1] = 13
339 let rc__dry: i64 = gv_verdict("VIZSLA-RELATE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
340 sys_exit(rc__dry)
341 return rc__dry
342}