code wiki / _hdl_build / nx_vizsla_notify_gate.nx
nx_vizsla_notify_gate.nx source
↩ module page · 318 lines · 13647 B
1// nx_vizsla_notify_gate.nx -- VIZSLA OUTWARD-DELIVERY gate: reminders REACH you. COMPOSITION: the
2// calendar store is loaded through the blessed calendar organ; notify forks the blessed remind engine
3// for the due computation; the notification store is the once-only delivery ledger.
4//
5// fixture: calendar mtg 2026-07-08 10:00, gala 2026-08-20 18:00; contacts mom 07-14, dad 07-08.
6// deliver today=2026-07-08 leads=6,0 -> mtg(event in0) + dad(bday in0) + mom(bday in6) = 3 delivered
7// re-deliver same -> 0 delivered / 3 skipped (own store is the ledger)
8// deliver today=2026-08-14 leads=6,0 -> gala(event in6, NEW occurrence) = 1 delivered
9// feed -> 4 notifications; feed birthday -> 2 (mom, dad)
10// email today=2026-07-08 -> RFC5322 digest, 3 reminders, Date: Wed, 08 Jul 2026 09:00:00 +0000
11//
12// Rows: 1 fixture-loads 2 deliver-onsite 3 feed-lists 4 deliver-idempotent 5 deliver-new-occurrence
13// 6 feed-after 7 feed-kind-filter 8 email-compose 9 email-date-header 10 email-body-bullet
14// 11 feed-determinism 12 loud-fail-bad-today[neg] 13 loud-fail-bad-leads[neg]
15// Evidence: VIZSLA-NOTIFY-GATE -> stdout + knowledge/status/vizsla_gate.log; exit 0 iff 13/13.
16// spec: knowledge/research/2026-06-22-vizsla-sclass-roadmap.md license_tier: ORIGINAL
17import "nx_syscalls.nx"
18import "nx_gate_verdict.nx"
19
20func ng_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
21func ng_p(s: *u8) -> i64 { sys_write(1, s, ng_slen(s)); return 0 }
22
23func ng_cat(dst: *u8, off: i64, s: *u8) -> i64 {
24 var i: i64 = 0
25 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
26 return off + i
27}
28
29func ng_catn(dst: *u8, off: i64, v: i64) -> i64 {
30 var o: i64 = off
31 var m: i64 = v
32 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
33 let t: *u8 = sys_mmap(28)
34 var k: i64 = 0
35 if m == 0 { t[0] = 48 as u8; k = 1 }
36 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
37 var i: i64 = 0
38 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
39 return o + k
40}
41
42func ng_write(path: *u8, content: *u8) -> i64 {
43 let fd: i64 = sys_openat_wr(path, 0x1a4)
44 if fd < 0 { return 0 - 1 }
45 sys_write(fd, content, ng_slen(content))
46 sys_close(fd)
47 return 0
48}
49
50func ng_readall(path: *u8, szout: *i64) -> *u8 {
51 let fd: i64 = sys_openat_rd(path)
52 if fd < 0 { szout[0] = 0 - 1; return 0 as *u8 }
53 let sz: i64 = sys_lseek(fd, 0, 2)
54 sys_lseek(fd, 0, 0)
55 let buf: *u8 = sys_mmap(sz + 64)
56 var got: i64 = 0
57 var n: i64 = 1
58 while n > 0 {
59 n = sys_read(fd, (buf as i64 + got) as *u8, 65536)
60 if n > 0 { got = got + n }
61 }
62 sys_close(fd)
63 szout[0] = got
64 return buf
65}
66
67func ng_runv(elf: *u8, args: *i64, outpath: *u8) -> i64 {
68 let pid: i64 = sys_fork()
69 if pid == 0 {
70 if (outpath as i64) != 0 {
71 let ofd: i64 = sys_openat_wr(outpath, 0x1a4)
72 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) }
73 }
74 let argv: *i64 = sys_mmap(128) as *i64
75 argv[0] = elf as i64
76 var i: i64 = 0
77 var go: i64 = 1
78 while go == 1 {
79 if args[i] == 0 { go = 0 } else {
80 argv[i + 1] = args[i]
81 i = i + 1
82 }
83 }
84 argv[i + 1] = 0
85 let envp: *i64 = sys_mmap(16) as *i64
86 envp[0] = 0
87 sys_execve(elf, argv, envp)
88 sys_exit(127)
89 }
90 let st: *i64 = sys_mmap(16) as *i64
91 sys_wait4(pid, st, 0)
92 let sig: i64 = st[0] & 0x7f
93 if sig != 0 { return 128 + sig }
94 return (st[0] >> 8) & 0xff
95}
96
97func ng_has(path: *u8, needle: *u8) -> i64 {
98 let szp: *i64 = sys_mmap(16) as *i64
99 let b: *u8 = ng_readall(path, szp)
100 let sz: i64 = szp[0]
101 let n: i64 = ng_slen(needle)
102 if sz < n { return 0 }
103 var i: i64 = 0
104 while i + n <= sz {
105 var ok: i64 = 1
106 var j: i64 = 0
107 while j < n {
108 if b[i + j] != needle[j] { ok = 0; j = n } else { j = j + 1 }
109 }
110 if ok == 1 { return 1 }
111 i = i + 1
112 }
113 return 0
114}
115
116func ng_fileeq(p1: *u8, p2: *u8) -> i64 {
117 let s1: *i64 = sys_mmap(16) as *i64
118 let s2: *i64 = sys_mmap(16) as *i64
119 let b1: *u8 = ng_readall(p1, s1)
120 let b2: *u8 = ng_readall(p2, s2)
121 if s1[0] != s2[0] { return 0 }
122 if s1[0] <= 0 { return 0 }
123 var i: i64 = 0
124 while i < s1[0] {
125 if b1[i] != b2[i] { return 0 }
126 i = i + 1
127 }
128 return 1
129}
130
131func ng_row(name: *u8, pass: i64) -> i64 {
132 ng_p("ROW " as *u8)
133 ng_p(name)
134 if pass == 1 { ng_p(" PASS\n" as *u8) } else { ng_p(" FAIL\n" as *u8) }
135 return pass
136}
137
138func ng_args(a1: *u8, a2: *u8, a3: *u8, a4: *u8, a5: *u8, a6: *u8, a7: *u8, a8: *u8) -> *i64 {
139 let a: *i64 = sys_mmap(128) as *i64
140 a[0] = a1 as i64
141 a[1] = a2 as i64
142 a[2] = a3 as i64
143 a[3] = a4 as i64
144 a[4] = a5 as i64
145 a[5] = a6 as i64
146 a[6] = a7 as i64
147 a[7] = a8 as i64
148 a[8] = 0
149 return a
150}
151
152func ng_ensure(elf: *u8, organ: *u8) -> i64 {
153 let pr: i64 = sys_openat_rd(elf)
154 if pr >= 0 { sys_close(pr); return 0 }
155 ng_p(" instrument missing -> rebuilding via durable runner\n" as *u8)
156 ng_runv("_offc/nx_sov_build_run.elf" as *u8, ng_args(organ, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_rebuild.out" as *u8)
157 return 0
158}
159
160func main(argc: i64, argv: *i64) -> i64 {
161 ng_p("=== VIZSLA NOTIFY GATE: outward delivery (on-site once-only + RFC5322 email compose) ===\n" as *u8)
162 let ntf: *u8 = "buildroot/_build/nx_vizsla_notify.sov.elf" as *u8
163 let cal: *u8 = "buildroot/_build/nx_vizsla_calendar.sov.elf" as *u8
164 ng_ensure(ntf, "nx_vizsla_notify" as *u8)
165 ng_ensure(cal, "nx_vizsla_calendar" as *u8)
166 // notify forks the BLESSED _offc/nx_vizsla_remind.elf -- ensure it exists
167 let rp: i64 = sys_openat_rd("_offc/nx_vizsla_remind.elf" as *u8)
168 if rp >= 0 { sys_close(rp) } else {
169 ng_runv("_offc/nx_sov_build_run.elf" as *u8, ng_args("nx_vizsla_remind" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_rem.out" as *u8)
170 }
171
172 let us: i64 = sys_now_us()
173 let cpfx: *u8 = sys_mmap(128)
174 var po: i64 = 0
175 po = ng_cat(cpfx, po, "/tmp/vznC" as *u8)
176 po = ng_catn(cpfx, po, us)
177 po = ng_cat(cpfx, po, "-" as *u8)
178 cpfx[po] = 0 as u8
179 let npfx: *u8 = sys_mmap(128)
180 po = 0
181 po = ng_cat(npfx, po, "/tmp/vznN" as *u8)
182 po = ng_catn(npfx, po, us)
183 po = ng_cat(npfx, po, "-" as *u8)
184 npfx[po] = 0 as u8
185
186 ng_write("/tmp/vzn_cal.txt" as *u8, "EVENT mtg 2026-07-08 600 60 standup-mtg\nEVENT gala 2026-08-20 1080 120 summer-gala\n" as *u8)
187 ng_write("/tmp/vzn_ct.txt" as *u8, "TIER inner 7\nCONTACT mom Mom inner 07-14\nCONTACT dad Dad inner 07-08\n" as *u8)
188
189 var pass: i64 = 0
190 var r: i64 = 0
191
192 // row 1: calendar loads
193 let rc1: i64 = ng_runv(cal, ng_args("load" as *u8, "/tmp/vzn_cal.txt" as *u8, cpfx, "7501" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_l1.txt" as *u8)
194 r = 0
195 if rc1 == 0 { if ng_has("/tmp/vzn_l1.txt" as *u8, "VIZSLA-CAL-LOAD scanned=2 new=2" as *u8) == 1 { r = 1 } }
196 pass = pass + ng_row("fixture-loads" as *u8, r)
197
198 // row 2: deliver on-site
199 let rc2: i64 = ng_runv(ntf, ng_args("deliver" as *u8, npfx, cpfx, "/tmp/vzn_ct.txt" as *u8, "2026-07-08" as *u8, "6,0" as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r2.txt" as *u8)
200 r = 0
201 if rc2 == 0 { if ng_has("/tmp/vzn_r2.txt" as *u8, "VIZSLA-NOTIFY-DELIVER channel=onsite today=2026-07-08 delivered=3 skipped=0" as *u8) == 1 { r = 1 } }
202 pass = pass + ng_row("deliver-onsite" as *u8, r)
203
204 // row 3: feed lists the delivered notices
205 let rc3: i64 = ng_runv(ntf, ng_args("feed" as *u8, npfx, "-" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r3.txt" as *u8)
206 r = 0
207 if rc3 == 0 {
208 if ng_has("/tmp/vzn_r3.txt" as *u8, "VIZSLA-NOTIFY channel=onsite kind=birthday id=mom date=2026-07-14 on=2026-07-08 title=Mom" as *u8) == 1 {
209 if ng_has("/tmp/vzn_r3.txt" as *u8, "VIZSLA-NOTIFY-FEED kind=- notifications=3" as *u8) == 1 { r = 1 }
210 }
211 }
212 pass = pass + ng_row("feed-lists" as *u8, r)
213
214 // row 4: idempotent re-deliver
215 let rc4: i64 = ng_runv(ntf, ng_args("deliver" as *u8, npfx, cpfx, "/tmp/vzn_ct.txt" as *u8, "2026-07-08" as *u8, "6,0" as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r4.txt" as *u8)
216 r = 0
217 if rc4 == 0 { if ng_has("/tmp/vzn_r4.txt" as *u8, "delivered=0 skipped=3 segment=none" as *u8) == 1 { r = 1 } }
218 pass = pass + ng_row("deliver-idempotent" as *u8, r)
219
220 // row 5: a genuinely new occurrence delivers
221 let rc5: i64 = ng_runv(ntf, ng_args("deliver" as *u8, npfx, cpfx, "/tmp/vzn_ct.txt" as *u8, "2026-08-14" as *u8, "6,0" as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r5.txt" as *u8)
222 r = 0
223 if rc5 == 0 { if ng_has("/tmp/vzn_r5.txt" as *u8, "today=2026-08-14 delivered=1 skipped=0" as *u8) == 1 { r = 1 } }
224 pass = pass + ng_row("deliver-new-occurrence" as *u8, r)
225
226 // row 6: feed now shows 4
227 let rc6: i64 = ng_runv(ntf, ng_args("feed" as *u8, npfx, "-" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r6.txt" as *u8)
228 r = 0
229 if rc6 == 0 { if ng_has("/tmp/vzn_r6.txt" as *u8, "VIZSLA-NOTIFY-FEED kind=- notifications=4" as *u8) == 1 { r = 1 } }
230 pass = pass + ng_row("feed-after" as *u8, r)
231
232 // row 7: kind filter
233 let rc7: i64 = ng_runv(ntf, ng_args("feed" as *u8, npfx, "birthday" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r7.txt" as *u8)
234 r = 0
235 if rc7 == 0 {
236 if ng_has("/tmp/vzn_r7.txt" as *u8, "VIZSLA-NOTIFY-FEED kind=birthday notifications=2" as *u8) == 1 {
237 if ng_has("/tmp/vzn_r7.txt" as *u8, "kind=event" as *u8) == 0 { r = 1 }
238 }
239 }
240 pass = pass + ng_row("feed-kind-filter" as *u8, r)
241
242 // row 8: email compose
243 let rc8: i64 = ng_runv(ntf, ng_args("email" as *u8, cpfx, "/tmp/vzn_ct.txt" as *u8, "2026-07-08" as *u8, "6,0" as *u8, "vizsla@jasonewest.com" as *u8, "you@example.com" as *u8, "/tmp/vzn_out.eml" as *u8), "/tmp/vzn_r8.txt" as *u8)
244 r = 0
245 if rc8 == 0 {
246 if ng_has("/tmp/vzn_r8.txt" as *u8, "VIZSLA-NOTIFY-EMAIL today=2026-07-08 reminders=3" as *u8) == 1 {
247 if ng_has("/tmp/vzn_out.eml" as *u8, "From: vizsla@jasonewest.com" as *u8) == 1 {
248 if ng_has("/tmp/vzn_out.eml" as *u8, "To: you@example.com" as *u8) == 1 {
249 if ng_has("/tmp/vzn_out.eml" as *u8, "Subject: Your reminders for 2026-07-08" as *u8) == 1 { r = 1 }
250 }
251 }
252 }
253 }
254 pass = pass + ng_row("email-compose" as *u8, r)
255
256 // row 9: valid RFC5322 Date header (DOW math end-to-end: 2026-07-08 = Wednesday)
257 r = ng_has("/tmp/vzn_out.eml" as *u8, "Date: Wed, 08 Jul 2026 09:00:00 +0000" as *u8)
258 pass = pass + ng_row("email-date-header" as *u8, r)
259
260 // row 10: the digest body carries the reminders
261 r = 0
262 if ng_has("/tmp/vzn_out.eml" as *u8, "Nishi Vizsla reminders:" as *u8) == 1 {
263 if ng_has("/tmp/vzn_out.eml" as *u8, "* Mom (birthday) on 2026-07-14 -- in 6 days" as *u8) == 1 { r = 1 }
264 }
265 pass = pass + ng_row("email-body-bullet" as *u8, r)
266
267 // row 11: feed determinism
268 ng_runv(ntf, ng_args("feed" as *u8, npfx, "-" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r11.txt" as *u8)
269 r = ng_fileeq("/tmp/vzn_r6.txt" as *u8, "/tmp/vzn_r11.txt" as *u8)
270 pass = pass + ng_row("feed-determinism" as *u8, r)
271
272 // row 12: bad today (negative control)
273 let rc12: i64 = ng_runv(ntf, ng_args("deliver" as *u8, npfx, cpfx, "/tmp/vzn_ct.txt" as *u8, "2026-13-99" as *u8, "6,0" as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r12.txt" as *u8)
274 r = 0
275 if rc12 == 1 { r = 1 }
276 pass = pass + ng_row("loud-fail-bad-today" as *u8, r)
277
278 // row 13: bad leads propagate from remind (negative control)
279 let rc13: i64 = ng_runv(ntf, ng_args("deliver" as *u8, npfx, cpfx, "/tmp/vzn_ct.txt" as *u8, "2026-07-08" as *u8, "x,1" as *u8, 0 as *u8, 0 as *u8), "/tmp/vzn_r13.txt" as *u8)
280 r = 0
281 if rc13 == 1 { r = 1 }
282 pass = pass + ng_row("loud-fail-bad-leads" as *u8, r)
283
284 let permil: i64 = (pass * 1000) / 13
285 let logfd: i64 = sys_openat_append("knowledge/status/vizsla_gate.log" as *u8, 0x1a4)
286 var fdi: i64 = 0
287 while fdi < 2 {
288 var fd: i64 = 1
289 if fdi == 1 { fd = logfd }
290 if fd > 0 {
291 let line: *u8 = sys_mmap(256)
292 var o: i64 = 0
293 o = ng_cat(line, o, "VIZSLA-NOTIFY-GATE epoch=" as *u8)
294 o = ng_catn(line, o, sys_now_realtime_sec())
295 o = ng_cat(line, o, " rows=13 pass=" as *u8)
296 o = ng_catn(line, o, pass)
297 o = ng_cat(line, o, " permil=" as *u8)
298 o = ng_catn(line, o, permil)
299 if pass == 13 {
300 o = ng_cat(line, o, " verdict=GREEN\n" as *u8)
301 } else {
302 o = ng_cat(line, o, " verdict=RED\n" as *u8)
303 }
304 sys_write(fd, line, o)
305 }
306 fdi = fdi + 1
307 }
308 if logfd > 0 { sys_close(logfd) }
309 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
310 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
311 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
312 let ctr__dry: *i64 = gv_ctr()
313 ctr__dry[0] = pass
314 ctr__dry[1] = 13
315 let rc__dry: i64 = gv_verdict("VIZSLA-NOTIFY-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
316 sys_exit(rc__dry)
317 return rc__dry
318}