code wiki / _hdl_build / nx_vizsla_notes.nx
nx_vizsla_notes.nx source
↩ module page · 1088 lines · 42816 B
1// nx_vizsla_notes.nx -- NISHI VIZSLA V4b: the event-anchored timed MEANINGFUL
2// NOTE engine (the meaningfulnotes.com product core).
3// Operator directive 2026-06-14: "make the thank you personalized -- capturing
4// signatures or notes and letting it SIT till a timed event ... beyond thank
5// you notes to meaningfulnotes.com where it can be PRIOR, ON, or AFTER a
6// significant event to SIGNAL something."
7//
8// A NOTE is personalized (a captured signature / handwritten note / voice / text
9// reference) and ANCHORED to an EVENT with a timing relation BEFORE | ON | AFTER
10// (+ an offset in days). It SITS (state=WAITING) until its release window opens,
11// then surfaces DUE (send it now), then LATE if the moment passed unsent. A
12// thank-you is just the AFTER case -- this generalizes it. Inherits the CID /
13// seg_store / civil-date substrate from V4 (nx_vizsla_relate); a captured-artifact
14// reference is a string here (binary capture/storage = a boundary asset rung).
15//
16// Commands (argv[1]):
17// load <notes_log> <prefix> [segid] EVENT/NOTE/SENT rows -> canonical records
18// -> CID keys "note:nxc1-..." on seg_store
19// (additive, idempotent by construction).
20// schedule <prefix> <conf> <today> every NOTE: release+deadline from its
21// event & relation; WAITING / DUE / LATE /
22// SENT. today via argv = deterministic.
23// due <prefix> <conf> <today> only the notes READY TO SEND now (DUE).
24//
25// File formats (line records, # comments):
26// notes_log:
27// EVENT <event-id> <date> <contact> <kind> <title>
28// NOTE <note-id> <event-id> <relation BEFORE|ON|AFTER> <offset_days> <channel> <personalization>
29// SENT <note-id> <date> (closes that note)
30// conf: CONF window_days <n> # grace window after release (ON/AFTER) before LATE
31// CONF soon_days <n> # (reserved: "coming up" horizon)
32// (date YYYY-MM-DD; title/kind/channel/personalization = single hyphenated tokens;
33// personalization e.g. signature:img-0427 | handwritten:scan-0427 | voice:vm-0427 | text:congrats)
34//
35// release/deadline by relation (window = CONF window_days):
36// BEFORE off : release = event - off ; deadline = event (send in the run-up)
37// ON : release = event ; deadline = event + window (the day-of + grace)
38// AFTER off : release = event + off ; deadline = release + window (the follow-up)
39// state(today): today<release WAITING | release<=today<=deadline DUE | else LATE | SENT closes.
40//
41// Determinism: stdout has no clocks (today via argv; epoch headers -> logs only).
42// Loud-fail: missing files, NOTE referencing an unknown EVENT, relation not
43// BEFORE/ON/AFTER, malformed dates = exit 1.
44// EXCEED (honest, feature-class): card/print apps (Punkpost/Postable/Hallmark)
45// send an artifact NOW; none hold a personalized note ANCHORED to a future
46// moment and release it at the right time relative to that moment.
47//
48// module: nishi-core.vizsla.notes
49// depends: nishi-core.lib.syscalls, nishi-core.lib.canon_cid, nishi-core.lib.seg_store
50// capability: VIZSLA_MEANINGFUL_NOTES
51// spec: knowledge/specs/2026-06-14-thankyounotes-product-spec.md license_tier: ORIGINAL
52import "nx_syscalls.nx"
53import "nx_canon_cid.nx"
54import "nx_seg_store.nx"
55const K_MAGIC_1970: i64 = 1970
56const K_MAGIC_146097: i64 = 146097
57const K_MAGIC_719468: i64 = 719468
58const K_MAGIC_1460: i64 = 1460
59const K_MAGIC_36524: i64 = 36524
60const K_MAGIC_146096: i64 = 146096
61const K_MAGIC_4096: i64 = 4096
62const K_MAGIC_2048: i64 = 2048
63const K_MAGIC_131072: i64 = 131072
64const K_MAGIC_16384: i64 = 16384
65const K_MAGIC_65536: i64 = 65536
66
67func nv_slen(s: *u8) -> i64 {
68 var n: i64 = 0
69 while s[n] != (0 as u8) { n = n + 1 }
70 return n
71}
72
73func nv_p(s: *u8) -> i64 {
74 sys_write(1, s, nv_slen(s))
75 return 0
76}
77
78func nv_eq(a: *u8, b: *u8) -> i64 {
79 var i: i64 = 0
80 var go: i64 = 1
81 while go == 1 {
82 if a[i] != b[i] { return 0 }
83 if a[i] == (0 as u8) { return 1 }
84 i = i + 1
85 }
86 return 0
87}
88
89func nv_dup(s: *u8) -> *u8 {
90 let n: i64 = nv_slen(s)
91 let d: *u8 = sys_mmap(n + 2)
92 var i: i64 = 0
93 while i <= n { d[i] = s[i]; i = i + 1 }
94 return d
95}
96
97func nv_atoi(s: *u8) -> i64 {
98 var v: i64 = 0
99 var i: i64 = 0
100 while s[i] != (0 as u8) {
101 let d: i64 = (s[i] as i64) - 48
102 if d >= 0 { if d <= 9 { v = v * 10 + d } }
103 i = i + 1
104 }
105 return v
106}
107
108func nv_isws(c: i64) -> i64 {
109 if c == 32 { return 1 }
110 if c == 13 { return 1 }
111 if c == 9 { return 1 }
112 return 0
113}
114
115func nv_tok(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 {
116 var p: i64 = off
117 var go: i64 = 1
118 while go == 1 {
119 if p >= lend { go = 0 } else {
120 if nv_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 }
121 }
122 }
123 var t: i64 = 0
124 go = 1
125 while go == 1 {
126 if p >= lend { go = 0 } else {
127 if nv_isws(b[p] as i64) == 1 { go = 0 } else {
128 if t < cap - 1 { dst[t] = b[p]; t = t + 1 }
129 p = p + 1
130 }
131 }
132 }
133 dst[t] = 0 as u8
134 return p
135}
136
137func nv_cat(dst: *u8, off: i64, s: *u8) -> i64 {
138 var i: i64 = 0
139 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
140 return off + i
141}
142
143func nv_catn(dst: *u8, off: i64, v: i64) -> i64 {
144 var o: i64 = off
145 var m: i64 = v
146 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
147 let t: *u8 = sys_mmap(28)
148 var k: i64 = 0
149 if m == 0 { t[0] = 48 as u8; k = 1 }
150 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
151 var i: i64 = 0
152 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
153 return o + k
154}
155
156// write v as a 2-digit zero-padded number (months/days)
157func nv_cat2(dst: *u8, off: i64, v: i64) -> i64 {
158 var o: i64 = off
159 if v < 10 { dst[o] = 48 as u8; o = o + 1 }
160 return nv_catn(dst, o, v)
161}
162
163func nv_find(list: *i64, n: i64, s: *u8) -> i64 {
164 var i: i64 = 0
165 while i < n {
166 if nv_eq(list[i] as *u8, s) == 1 { return i }
167 i = i + 1
168 }
169 return 0 - 1
170}
171
172func nv_kmemeq(b: *u8, off: i64, n: i64, s: *u8) -> i64 {
173 if nv_slen(s) != n { return 0 }
174 var i: i64 = 0
175 while i < n {
176 if b[off + i] != s[i] { return 0 }
177 i = i + 1
178 }
179 return 1
180}
181
182func nv_field(rec: *u8, rl: i64, want: *u8, out: *u8, cap: i64) -> i64 {
183 if rl < 8 { return 0 }
184 let nf: i64 = ss_r32(rec, 4)
185 var off: i64 = 8
186 var fi: i64 = 0
187 while fi < nf {
188 if off + 8 > rl { return 0 }
189 let kl: i64 = ss_r32(rec, off)
190 let koff: i64 = off + 4
191 let vl: i64 = ss_r32(rec, koff + kl)
192 let voff: i64 = koff + kl + 4
193 if nv_kmemeq(rec, koff, kl, want) == 1 {
194 var t: i64 = 0
195 while t < vl {
196 if t < cap - 1 { out[t] = rec[voff + t] }
197 t = t + 1
198 }
199 if t > cap - 1 { t = cap - 1 }
200 out[t] = 0 as u8
201 return 1
202 }
203 off = voff + vl
204 fi = fi + 1
205 }
206 return 0
207}
208
209func nv_isnote(b: *u8, koff: i64, kl: i64) -> i64 {
210 if kl <= 5 { return 0 }
211 return nv_kmemeq(b, koff, 5, "note:" as *u8)
212}
213
214// civil date "YYYY-MM-DD" -> days since 1970-01-01 (Hinnant); -1 if malformed
215func nv_datedays(s: *u8) -> i64 {
216 let nums: *i64 = sys_mmap(8 * 4) as *i64
217 var nn: i64 = 0
218 var cur: i64 = 0
219 var indig: i64 = 0
220 var i: i64 = 0
221 var go: i64 = 1
222 while go == 1 {
223 let c: i64 = s[i] as i64
224 var isd: i64 = 0
225 if c >= 48 { if c <= 57 { isd = 1 } }
226 if isd == 1 { cur = cur * 10 + (c - 48); indig = 1 }
227 if isd == 0 {
228 if indig == 1 {
229 if nn < 4 { nums[nn] = cur; nn = nn + 1 }
230 cur = 0
231 indig = 0
232 }
233 }
234 if c == 0 { go = 0 }
235 i = i + 1
236 }
237 if nn != 3 { return 0 - 1 }
238 var y: i64 = nums[0]
239 let m: i64 = nums[1]
240 let d: i64 = nums[2]
241 if y < K_MAGIC_1970 { return 0 - 1 }
242 if m < 1 { return 0 - 1 }
243 if m > 12 { return 0 - 1 }
244 if d < 1 { return 0 - 1 }
245 if d > 31 { return 0 - 1 }
246 if m <= 2 { y = y - 1 }
247 let era: i64 = y / 400
248 let yoe: i64 = y - era * 400
249 var mp: i64 = m - 3
250 if m <= 2 { mp = m + 9 }
251 let doy: i64 = (153 * mp + 2) / 5 + d - 1
252 let doe: i64 = yoe * 365 + yoe / 4 - yoe / 100 + doy
253 return era * K_MAGIC_146097 + doe - K_MAGIC_719468
254}
255
256// inverse: days since 1970 -> "YYYY-MM-DD" (Hinnant civil_from_days; z>=0 here)
257func nv_daystr(days: i64, out: *u8) -> i64 {
258 let z: i64 = days + K_MAGIC_719468
259 let era: i64 = z / K_MAGIC_146097
260 let doe: i64 = z - era * K_MAGIC_146097
261 let yoe: i64 = (doe - doe / K_MAGIC_1460 + doe / K_MAGIC_36524 - doe / K_MAGIC_146096) / 365
262 var y: i64 = yoe + era * 400
263 let doy: i64 = doe - (365 * yoe + yoe / 4 - yoe / 100)
264 let mp: i64 = (5 * doy + 2) / 153
265 let d: i64 = doy - (153 * mp + 2) / 5 + 1
266 var m: i64 = mp + 3
267 if mp >= 10 { m = mp - 9 }
268 if m <= 2 { y = y + 1 }
269 var o: i64 = 0
270 o = nv_catn(out, o, y)
271 out[o] = 45 as u8; o = o + 1
272 o = nv_cat2(out, o, m)
273 out[o] = 45 as u8; o = o + 1
274 o = nv_cat2(out, o, d)
275 out[o] = 0 as u8
276 return o
277}
278
279func nv_log(lpath: *u8, tag: *u8, rep: *u8, o: i64) -> i64 {
280 let fd: i64 = sys_openat_append(lpath, 0x1a4)
281 if fd <= 0 { return 0 }
282 let hdr: *u8 = sys_mmap(256)
283 var ho: i64 = 0
284 ho = nv_cat(hdr, ho, "VIZSLA-NOTE epoch=" as *u8)
285 ho = nv_catn(hdr, ho, sys_now_realtime_sec())
286 ho = nv_cat(hdr, ho, " cmd=" as *u8)
287 ho = nv_cat(hdr, ho, tag)
288 ho = nv_cat(hdr, ho, "\n" as *u8)
289 sys_write(fd, hdr, ho)
290 sys_write(fd, rep, o)
291 sys_close(fd)
292 return 0
293}
294
295func nv_prefixlog(prefix: *u8, out: *u8) -> i64 {
296 var o: i64 = 0
297 o = nv_cat(out, o, prefix)
298 o = nv_cat(out, o, "notes.log" as *u8)
299 out[o] = 0 as u8
300 return o
301}
302
303// conf block: c[0]=window_days(global) c[1]=soon_days
304// c[2]=oc_kind c[3]=oc_rel c[4]=oc_off c[5]=oc_win c[6]=oc_etiq c[7]=noc
305// OCCASION rows = the per-kind customary timing + etiquette knowledge base (TYN-003)
306func nv_load_conf(cpath: *u8) -> *i64 {
307 let cf: *i64 = sys_mmap(8 * 8) as *i64
308 cf[0] = 3
309 cf[1] = 14
310 cf[2] = sys_mmap(8 * 64); cf[3] = sys_mmap(8 * 64); cf[4] = sys_mmap(8 * 64)
311 cf[5] = sys_mmap(8 * 64); cf[6] = sys_mmap(8 * 64); cf[7] = 0
312 let oc_kind: *i64 = cf[2] as *i64
313 let oc_rel: *i64 = cf[3] as *i64
314 let oc_off: *i64 = cf[4] as *i64
315 let oc_win: *i64 = cf[5] as *i64
316 let oc_etiq: *i64 = cf[6] as *i64
317 let szp: *i64 = sys_mmap(16) as *i64
318 let cb: *u8 = ss_readall(cpath, szp)
319 let csz: i64 = szp[0]
320 if csz <= 0 { return 0 as *i64 }
321 let t0: *u8 = sys_mmap(128)
322 let t1: *u8 = sys_mmap(128)
323 let t2: *u8 = sys_mmap(128)
324 let t3: *u8 = sys_mmap(128)
325 let t4: *u8 = sys_mmap(128)
326 let t5: *u8 = sys_mmap(128)
327 var i: i64 = 0
328 while i < csz {
329 var e: i64 = i
330 var go: i64 = 1
331 while go == 1 {
332 if e >= csz { go = 0 } else {
333 if cb[e] == (10 as u8) { go = 0 } else { e = e + 1 }
334 }
335 }
336 var p: i64 = nv_tok(cb, i, e, t0, 128)
337 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
338 if nv_eq(t0, "CONF" as *u8) == 1 {
339 p = nv_tok(cb, p, e, t1, 128)
340 p = nv_tok(cb, p, e, t2, 128)
341 if nv_eq(t1, "window_days" as *u8) == 1 { cf[0] = nv_atoi(t2) }
342 if nv_eq(t1, "soon_days" as *u8) == 1 { cf[1] = nv_atoi(t2) }
343 }
344 if nv_eq(t0, "OCCASION" as *u8) == 1 {
345 p = nv_tok(cb, p, e, t1, 128); p = nv_tok(cb, p, e, t2, 128); p = nv_tok(cb, p, e, t3, 128)
346 p = nv_tok(cb, p, e, t4, 128); p = nv_tok(cb, p, e, t5, 128)
347 if nv_eq(t2, "BEFORE" as *u8) == 0 { if nv_eq(t2, "ON" as *u8) == 0 { if nv_eq(t2, "AFTER" as *u8) == 0 { return 0 as *i64 } } }
348 if cf[7] < 64 {
349 oc_kind[cf[7]] = nv_dup(t1) as i64; oc_rel[cf[7]] = nv_dup(t2) as i64; oc_off[cf[7]] = nv_atoi(t3)
350 oc_win[cf[7]] = nv_atoi(t4); oc_etiq[cf[7]] = nv_dup(t5) as i64; cf[7] = cf[7] + 1
351 }
352 }
353 i = e + 1
354 }
355 return cf
356}
357
358func nv_occ_idx(c: *i64, kind: *u8) -> i64 {
359 let oc_kind: *i64 = c[2] as *i64
360 var i: i64 = 0
361 while i < c[7] {
362 if nv_eq(oc_kind[i] as *u8, kind) == 1 { return i }
363 i = i + 1
364 }
365 return 0 - 1
366}
367
368// the window to use for a note of this event-kind: the occasion's if defined,
369// else the global window_days default
370func nv_occ_window(c: *i64, kind: *u8) -> i64 {
371 let idx: i64 = nv_occ_idx(c, kind)
372 if idx < 0 { return c[0] }
373 let ow: *i64 = c[5] as *i64
374 return ow[idx]
375}
376
377// ---------------- LOAD ----------------
378func nv_cmd_load(argc: i64, argv: *i64) -> i64 {
379 if argc < 4 { nv_p("VIZSLA-NOTE load needs <notes_log> <prefix> -- fail loud\n" as *u8); return 1 }
380 let lpath: *u8 = argv[2] as *u8
381 let prefix: *u8 = argv[3] as *u8
382 var segid: i64 = 0
383 if argc > 4 { segid = nv_atoi(argv[4] as *u8) }
384 if segid == 0 { segid = sys_now_us() }
385
386 let szp: *i64 = sys_mmap(16) as *i64
387 let b: *u8 = ss_readall(lpath, szp)
388 let sz: i64 = szp[0]
389 if sz <= 0 { nv_p("VIZSLA-NOTE notes_log MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
390
391 let keys: *i64 = sys_mmap(8 * 8) as *i64
392 let vals: *i64 = sys_mmap(8 * 8) as *i64
393 let t0: *u8 = sys_mmap(128)
394 let f1: *u8 = sys_mmap(128)
395 let f2: *u8 = sys_mmap(128)
396 let f3: *u8 = sys_mmap(128)
397 let f4: *u8 = sys_mmap(128)
398 let f5: *u8 = sys_mmap(256)
399 let pp: *i64 = sys_mmap(16) as *i64
400 let ll: *i64 = sys_mmap(16) as *i64
401
402 let w: *i64 = ss_begin()
403 let seen: *i64 = sys_mmap(8 * K_MAGIC_4096) as *i64
404 var nseen: i64 = 0
405 var scanned: i64 = 0
406 var added: i64 = 0
407 var dup_infile: i64 = 0
408 var dup_instore: i64 = 0
409
410 var i: i64 = 0
411 while i < sz {
412 var e: i64 = i
413 var go: i64 = 1
414 while go == 1 {
415 if e >= sz { go = 0 } else {
416 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 }
417 }
418 }
419 var p: i64 = nv_tok(b, i, e, t0, 128)
420 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
421 var nf: i64 = 0
422 if nv_eq(t0, "EVENT" as *u8) == 1 {
423 p = nv_tok(b, p, e, f1, 128); p = nv_tok(b, p, e, f2, 128); p = nv_tok(b, p, e, f3, 128)
424 p = nv_tok(b, p, e, f4, 128); p = nv_tok(b, p, e, f5, 256)
425 if nv_datedays(f2) < 0 { nv_p("VIZSLA-NOTE EVENT bad date -- fail loud\n" as *u8); return 1 }
426 keys[0] = "k" as *u8 as i64; keys[1] = "a" as *u8 as i64; keys[2] = "date" as *u8 as i64
427 keys[3] = "b" as *u8 as i64; keys[4] = "c" as *u8 as i64; keys[5] = "d" as *u8 as i64
428 vals[0] = "event" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64
429 vals[3] = f3 as i64; vals[4] = f4 as i64; vals[5] = f5 as i64
430 nf = 6
431 }
432 if nv_eq(t0, "NOTE" as *u8) == 1 {
433 p = nv_tok(b, p, e, f1, 128); p = nv_tok(b, p, e, f2, 128); p = nv_tok(b, p, e, f3, 128)
434 p = nv_tok(b, p, e, f4, 128); p = nv_tok(b, p, e, f5, 256)
435 let pers: *u8 = sys_mmap(256)
436 nv_tok(b, p, e, pers, 256)
437 if nv_eq(f3, "BEFORE" as *u8) == 0 { if nv_eq(f3, "ON" as *u8) == 0 { if nv_eq(f3, "AFTER" as *u8) == 0 { nv_p("VIZSLA-NOTE relation not BEFORE/ON/AFTER -- fail loud\n" as *u8); return 1 } } }
438 keys[0] = "k" as *u8 as i64; keys[1] = "a" as *u8 as i64; keys[2] = "b" as *u8 as i64
439 keys[3] = "c" as *u8 as i64; keys[4] = "d" as *u8 as i64; keys[5] = "e" as *u8 as i64; keys[6] = "f" as *u8 as i64
440 vals[0] = "note" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64
441 vals[3] = f3 as i64; vals[4] = f4 as i64; vals[5] = f5 as i64; vals[6] = pers as i64
442 nf = 7
443 }
444 if nv_eq(t0, "SENT" as *u8) == 1 {
445 p = nv_tok(b, p, e, f1, 128); p = nv_tok(b, p, e, f2, 128)
446 if nv_datedays(f2) < 0 { nv_p("VIZSLA-NOTE SENT bad date -- fail loud\n" as *u8); return 1 }
447 keys[0] = "k" as *u8 as i64; keys[1] = "a" as *u8 as i64; keys[2] = "date" as *u8 as i64
448 vals[0] = "sent" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64
449 nf = 3
450 }
451 if nf > 0 {
452 scanned = scanned + 1
453 let enc: *u8 = sys_mmap(K_MAGIC_2048)
454 let el: i64 = canon_encode(keys, vals, nf, enc)
455 let cid: *u8 = sys_mmap(96)
456 cid_of(enc, el, cid)
457 let kbuf: *u8 = sys_mmap(128)
458 var ko: i64 = 0
459 ko = nv_cat(kbuf, ko, "note:" as *u8)
460 ko = nv_cat(kbuf, ko, cid)
461 kbuf[ko] = 0 as u8
462 var fresh: i64 = 1
463 if nv_find(seen, nseen, kbuf) >= 0 { fresh = 0; dup_infile = dup_infile + 1 }
464 if fresh == 1 {
465 if nseen < K_MAGIC_4096 { seen[nseen] = nv_dup(kbuf) as i64; nseen = nseen + 1 }
466 }
467 if fresh == 1 {
468 if ss_get_idx(prefix, kbuf, pp, ll) == 1 { fresh = 0; dup_instore = dup_instore + 1 }
469 }
470 if fresh == 1 {
471 if ss_add(w, 1, kbuf, enc, el) != 0 { nv_p("VIZSLA-NOTE writer full -- fail loud\n" as *u8); return 1 }
472 added = added + 1
473 }
474 }
475 i = e + 1
476 }
477
478 var committed: i64 = 0
479 if added > 0 {
480 if ss_commit(prefix, w, segid) != 0 { nv_p("VIZSLA-NOTE commit FAILED -- fail loud\n" as *u8); return 1 }
481 committed = 1
482 }
483
484 let rep: *u8 = sys_mmap(K_MAGIC_4096)
485 var o: i64 = 0
486 o = nv_cat(rep, o, "VIZSLA-NOTE-LOAD scanned=" as *u8)
487 o = nv_catn(rep, o, scanned)
488 o = nv_cat(rep, o, " new=" as *u8)
489 o = nv_catn(rep, o, added)
490 o = nv_cat(rep, o, " dup_infile=" as *u8)
491 o = nv_catn(rep, o, dup_infile)
492 o = nv_cat(rep, o, " dup_instore=" as *u8)
493 o = nv_catn(rep, o, dup_instore)
494 o = nv_cat(rep, o, " segment=" as *u8)
495 if committed == 1 {
496 o = nv_cat(rep, o, "seg-" as *u8)
497 o = nv_catn(rep, o, segid)
498 } else {
499 o = nv_cat(rep, o, "none" as *u8)
500 }
501 o = nv_cat(rep, o, "\n" as *u8)
502 sys_write(1, rep, o)
503 let lp: *u8 = sys_mmap(512)
504 nv_prefixlog(prefix, lp)
505 nv_log(lp, "load" as *u8, rep, o)
506 return 0
507}
508
509// collect events + notes + sent into one block by walking the store:
510// g[0]=ev_id g[1]=ev_days g[2]=ev_contact g[3]=ev_kind g[4]=nev
511// g[5]=nt_id g[6]=nt_event g[7]=nt_rel g[8]=nt_off g[9]=nt_chan g[10]=nt_pers g[11]=nnt
512// g[12]=sent_id g[13]=nsent
513func nv_collect(prefix: *u8) -> *i64 {
514 let g: *i64 = sys_mmap(8 * 16) as *i64
515 g[0] = sys_mmap(8 * 256); g[1] = sys_mmap(8 * 256); g[2] = sys_mmap(8 * 256); g[3] = sys_mmap(8 * 256); g[4] = 0
516 g[5] = sys_mmap(8 * 256); g[6] = sys_mmap(8 * 256); g[7] = sys_mmap(8 * 256); g[8] = sys_mmap(8 * 256)
517 g[9] = sys_mmap(8 * 256); g[10] = sys_mmap(8 * 256); g[11] = 0
518 g[12] = sys_mmap(8 * 256); g[13] = 0
519 let ev_id: *i64 = g[0] as *i64
520 let ev_days: *i64 = g[1] as *i64
521 let ev_ct: *i64 = g[2] as *i64
522 let ev_kind: *i64 = g[3] as *i64
523 let nt_id: *i64 = g[5] as *i64
524 let nt_ev: *i64 = g[6] as *i64
525 let nt_rel: *i64 = g[7] as *i64
526 let nt_off: *i64 = g[8] as *i64
527 let nt_chan: *i64 = g[9] as *i64
528 let nt_pers: *i64 = g[10] as *i64
529 let snt: *i64 = g[12] as *i64
530
531 let segs: *i64 = sys_mmap(8 * 260) as *i64
532 let ns: i64 = ss_manifest(prefix, segs)
533 let fk: *u8 = sys_mmap(64)
534 let fa: *u8 = sys_mmap(128)
535 let fb: *u8 = sys_mmap(128)
536 let fc: *u8 = sys_mmap(128)
537 let fd: *u8 = sys_mmap(128)
538 let fe: *u8 = sys_mmap(256)
539 let fdt: *u8 = sys_mmap(128)
540 var s: i64 = 0
541 while s < ns {
542 let path: *u8 = sys_mmap(512)
543 var po: i64 = 0
544 po = nv_cat(path, po, prefix); po = nv_cat(path, po, segs[s] as *u8); po = nv_cat(path, po, ".docs" as *u8)
545 path[po] = 0 as u8
546 let szp: *i64 = sys_mmap(16) as *i64
547 let b: *u8 = ss_readall(path, szp)
548 let sz: i64 = szp[0]
549 var j: i64 = 0
550 while j + 9 <= sz {
551 let kind: i64 = b[j]
552 let kl: i64 = ss_r32(b, j + 1)
553 let koff: i64 = j + 5
554 let vl: i64 = ss_r32(b, koff + kl)
555 let voff: i64 = koff + kl + 4
556 var take: i64 = 0
557 if kind == 1 { take = nv_isnote(b, koff, kl) }
558 if take == 1 {
559 let rec: *u8 = (b as i64 + voff) as *u8
560 if nv_field(rec, vl, "k" as *u8, fk, 64) == 1 {
561 if nv_eq(fk, "event" as *u8) == 1 {
562 nv_field(rec, vl, "a" as *u8, fa, 128)
563 nv_field(rec, vl, "date" as *u8, fdt, 128)
564 nv_field(rec, vl, "b" as *u8, fb, 128)
565 nv_field(rec, vl, "c" as *u8, fc, 128)
566 if g[4] < 256 {
567 ev_id[g[4]] = nv_dup(fa) as i64; ev_days[g[4]] = nv_datedays(fdt)
568 ev_ct[g[4]] = nv_dup(fb) as i64; ev_kind[g[4]] = nv_dup(fc) as i64
569 g[4] = g[4] + 1
570 }
571 }
572 if nv_eq(fk, "note" as *u8) == 1 {
573 nv_field(rec, vl, "a" as *u8, fa, 128)
574 nv_field(rec, vl, "b" as *u8, fb, 128)
575 nv_field(rec, vl, "c" as *u8, fc, 128)
576 nv_field(rec, vl, "d" as *u8, fd, 128)
577 nv_field(rec, vl, "e" as *u8, fe, 128)
578 let fpr: *u8 = sys_mmap(256)
579 nv_field(rec, vl, "f" as *u8, fpr, 256)
580 if g[11] < 256 {
581 nt_id[g[11]] = nv_dup(fa) as i64; nt_ev[g[11]] = nv_dup(fb) as i64
582 nt_rel[g[11]] = nv_dup(fc) as i64; nt_off[g[11]] = nv_atoi(fd)
583 nt_chan[g[11]] = nv_dup(fe) as i64; nt_pers[g[11]] = fpr as i64
584 g[11] = g[11] + 1
585 }
586 }
587 if nv_eq(fk, "sent" as *u8) == 1 {
588 nv_field(rec, vl, "a" as *u8, fa, 128)
589 if g[13] < 256 { snt[g[13]] = nv_dup(fa) as i64; g[13] = g[13] + 1 }
590 }
591 }
592 }
593 j = voff + vl
594 }
595 s = s + 1
596 }
597 return g
598}
599
600// emit schedule lines (due_only=1 -> only DUE); counts[0..4]=notes/waiting/due/late/sent
601// returns offset, or -1 if a NOTE references an unknown EVENT (loud-fail to caller)
602func nv_emit(g: *i64, today: i64, c: *i64, due_only: i64, rep: *u8, counts: *i64) -> i64 {
603 let ev_id: *i64 = g[0] as *i64
604 let ev_days: *i64 = g[1] as *i64
605 let ev_ct: *i64 = g[2] as *i64
606 let ev_kind: *i64 = g[3] as *i64
607 let nt_id: *i64 = g[5] as *i64
608 let nt_ev: *i64 = g[6] as *i64
609 let nt_rel: *i64 = g[7] as *i64
610 let nt_off: *i64 = g[8] as *i64
611 let nt_chan: *i64 = g[9] as *i64
612 let nt_pers: *i64 = g[10] as *i64
613 let snt: *i64 = g[12] as *i64
614 counts[0] = g[11]; counts[1] = 0; counts[2] = 0; counts[3] = 0; counts[4] = 0
615 var o: i64 = 0
616 var i: i64 = 0
617 while i < g[11] {
618 let ei: i64 = nv_find(ev_id, g[4], nt_ev[i] as *u8)
619 if ei < 0 { return 0 - 1 }
620 let ev: i64 = ev_days[ei]
621 let off: i64 = nt_off[i]
622 let window: i64 = nv_occ_window(c, ev_kind[ei] as *u8)
623 var release: i64 = ev
624 var deadline: i64 = ev + window
625 if nv_eq(nt_rel[i] as *u8, "BEFORE" as *u8) == 1 { release = ev - off; deadline = ev }
626 if nv_eq(nt_rel[i] as *u8, "AFTER" as *u8) == 1 { release = ev + off; deadline = ev + off + window }
627 var state: i64 = 1
628 if nv_find(snt, g[13], nt_id[i] as *u8) >= 0 { state = 4 }
629 if state == 1 {
630 if today < release { state = 0 }
631 if today >= release { if today <= deadline { state = 2 } }
632 if today > deadline { state = 3 }
633 }
634 if state == 0 { counts[1] = counts[1] + 1 }
635 if state == 2 { counts[2] = counts[2] + 1 }
636 if state == 3 { counts[3] = counts[3] + 1 }
637 if state == 4 { counts[4] = counts[4] + 1 }
638 var show: i64 = 1
639 if due_only == 1 { show = 0; if state == 2 { show = 1 } }
640 if show == 1 {
641 let rs: *u8 = sys_mmap(32)
642 nv_daystr(release, rs)
643 let ds: *u8 = sys_mmap(32)
644 nv_daystr(deadline, ds)
645 o = nv_cat(rep, o, "VIZSLA-NOTE id=" as *u8)
646 o = nv_cat(rep, o, nt_id[i] as *u8)
647 o = nv_cat(rep, o, " event=" as *u8)
648 o = nv_cat(rep, o, nt_ev[i] as *u8)
649 o = nv_cat(rep, o, " contact=" as *u8)
650 o = nv_cat(rep, o, ev_ct[ei] as *u8)
651 o = nv_cat(rep, o, " kind=" as *u8)
652 o = nv_cat(rep, o, ev_kind[ei] as *u8)
653 o = nv_cat(rep, o, " rel=" as *u8)
654 o = nv_cat(rep, o, nt_rel[i] as *u8)
655 o = nv_cat(rep, o, " release=" as *u8)
656 o = nv_cat(rep, o, rs)
657 o = nv_cat(rep, o, " deadline=" as *u8)
658 o = nv_cat(rep, o, ds)
659 o = nv_cat(rep, o, " state=" as *u8)
660 if state == 0 { o = nv_cat(rep, o, "WAITING" as *u8) }
661 if state == 2 { o = nv_cat(rep, o, "DUE" as *u8) }
662 if state == 3 { o = nv_cat(rep, o, "LATE" as *u8) }
663 if state == 4 { o = nv_cat(rep, o, "SENT" as *u8) }
664 o = nv_cat(rep, o, " channel=" as *u8)
665 o = nv_cat(rep, o, nt_chan[i] as *u8)
666 o = nv_cat(rep, o, " personal=" as *u8)
667 o = nv_cat(rep, o, nt_pers[i] as *u8)
668 o = nv_cat(rep, o, "\n" as *u8)
669 }
670 i = i + 1
671 }
672 return o
673}
674
675// ---------------- SCHEDULE ----------------
676func nv_cmd_schedule(argc: i64, argv: *i64) -> i64 {
677 if argc < 5 { nv_p("VIZSLA-NOTE schedule needs <prefix> <conf> <today> -- fail loud\n" as *u8); return 1 }
678 let prefix: *u8 = argv[2] as *u8
679 let cf: *i64 = nv_load_conf(argv[3] as *u8)
680 if (cf as i64) == 0 { nv_p("VIZSLA-NOTE conf MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
681 let today: i64 = nv_datedays(argv[4] as *u8)
682 if today < 0 { nv_p("VIZSLA-NOTE bad today date -- fail loud\n" as *u8); return 1 }
683
684 let g: *i64 = nv_collect(prefix)
685 let rep: *u8 = sys_mmap(K_MAGIC_131072)
686 let counts: *i64 = sys_mmap(8 * 8) as *i64
687 let o: i64 = nv_emit(g, today, cf, 0, rep, counts)
688 if o < 0 { nv_p("VIZSLA-NOTE a NOTE references an unknown EVENT -- fail loud\n" as *u8); return 1 }
689 var o2: i64 = o
690 o2 = nv_cat(rep, o2, "VIZSLA-NOTE-VERDICT notes=" as *u8)
691 o2 = nv_catn(rep, o2, counts[0])
692 o2 = nv_cat(rep, o2, " waiting=" as *u8)
693 o2 = nv_catn(rep, o2, counts[1])
694 o2 = nv_cat(rep, o2, " due=" as *u8)
695 o2 = nv_catn(rep, o2, counts[2])
696 o2 = nv_cat(rep, o2, " late=" as *u8)
697 o2 = nv_catn(rep, o2, counts[3])
698 o2 = nv_cat(rep, o2, " sent=" as *u8)
699 o2 = nv_catn(rep, o2, counts[4])
700 o2 = nv_cat(rep, o2, "\n" as *u8)
701 sys_write(1, rep, o2)
702 let lp: *u8 = sys_mmap(512)
703 nv_prefixlog(prefix, lp)
704 nv_log(lp, "schedule" as *u8, rep, o2)
705 return 0
706}
707
708// ---------------- DUE ----------------
709func nv_cmd_due(argc: i64, argv: *i64) -> i64 {
710 if argc < 5 { nv_p("VIZSLA-NOTE due needs <prefix> <conf> <today> -- fail loud\n" as *u8); return 1 }
711 let prefix: *u8 = argv[2] as *u8
712 let cf: *i64 = nv_load_conf(argv[3] as *u8)
713 if (cf as i64) == 0 { nv_p("VIZSLA-NOTE conf MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
714 let today: i64 = nv_datedays(argv[4] as *u8)
715 if today < 0 { nv_p("VIZSLA-NOTE bad today date -- fail loud\n" as *u8); return 1 }
716
717 let g: *i64 = nv_collect(prefix)
718 let rep: *u8 = sys_mmap(K_MAGIC_131072)
719 let counts: *i64 = sys_mmap(8 * 8) as *i64
720 let o: i64 = nv_emit(g, today, cf, 1, rep, counts)
721 if o < 0 { nv_p("VIZSLA-NOTE a NOTE references an unknown EVENT -- fail loud\n" as *u8); return 1 }
722 var o2: i64 = o
723 o2 = nv_cat(rep, o2, "VIZSLA-NOTE-DUE-VERDICT due=" as *u8)
724 o2 = nv_catn(rep, o2, counts[2])
725 o2 = nv_cat(rep, o2, "\n" as *u8)
726 sys_write(1, rep, o2)
727 let lp: *u8 = sys_mmap(512)
728 nv_prefixlog(prefix, lp)
729 nv_log(lp, "due" as *u8, rep, o2)
730 return 0
731}
732
733// copy from off..lend verbatim (the rest of a TMPL line = the template body),
734// skipping leading whitespace and trimming trailing whitespace/CR
735func nv_rest(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 {
736 var p: i64 = off
737 var go: i64 = 1
738 while go == 1 {
739 if p >= lend { go = 0 } else {
740 if nv_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 }
741 }
742 }
743 var t: i64 = 0
744 while p < lend {
745 if t < cap - 1 { dst[t] = b[p]; t = t + 1 }
746 p = p + 1
747 }
748 while t > 0 {
749 if nv_isws(dst[t - 1] as i64) == 1 { t = t - 1 } else { t = 0 - 1 }
750 }
751 if t < 0 { t = 0 }
752 var u: i64 = 0
753 var stop: i64 = 0
754 var k: i64 = 0
755 while stop == 0 {
756 if dst[k] == (0 as u8) { stop = 1 } else { k = k + 1 }
757 }
758 // re-trim properly: recompute length then strip trailing ws
759 var n: i64 = k
760 var done: i64 = 0
761 while done == 0 {
762 if n <= 0 { done = 1 } else {
763 if nv_isws(dst[n - 1] as i64) == 1 { n = n - 1 } else { done = 1 }
764 }
765 }
766 dst[n] = 0 as u8
767 return n
768}
769
770// fill {slot} markers in a template body from parallel names[]/vals[] arrays;
771// an unknown {slot} is left literal (a visible data gap, not a silent drop)
772func nv_fill(body: *u8, names: *i64, vals: *i64, nslots: i64, out: *u8) -> i64 {
773 var o: i64 = 0
774 var i: i64 = 0
775 while body[i] != (0 as u8) {
776 if body[i] == (123 as u8) {
777 var j: i64 = i + 1
778 let nm: *u8 = sys_mmap(64)
779 var k: i64 = 0
780 var go: i64 = 1
781 while go == 1 {
782 if body[j] == (0 as u8) { go = 0 } else {
783 if body[j] == (125 as u8) { go = 0 } else {
784 if k < 63 { nm[k] = body[j]; k = k + 1 }
785 j = j + 1
786 }
787 }
788 }
789 nm[k] = 0 as u8
790 if body[j] == (125 as u8) { j = j + 1 }
791 let si: i64 = nv_find(names, nslots, nm)
792 if si >= 0 {
793 o = nv_cat(out, o, vals[si] as *u8)
794 } else {
795 out[o] = 123 as u8; o = o + 1
796 o = nv_cat(out, o, nm)
797 out[o] = 125 as u8; o = o + 1
798 }
799 i = j
800 } else {
801 out[o] = body[i]; o = o + 1
802 i = i + 1
803 }
804 }
805 out[o] = 0 as u8
806 return o
807}
808
809// templates block: tm[0]=kind[] tm[1]=rel[] tm[2]=body[] tm[3]=ntm
810func nv_load_templates(tpath: *u8) -> *i64 {
811 let szp: *i64 = sys_mmap(16) as *i64
812 let b: *u8 = ss_readall(tpath, szp)
813 let sz: i64 = szp[0]
814 if sz <= 0 { return 0 as *i64 }
815 let tm: *i64 = sys_mmap(8 * 8) as *i64
816 tm[0] = sys_mmap(8 * 64); tm[1] = sys_mmap(8 * 64); tm[2] = sys_mmap(8 * 64); tm[3] = 0
817 let tk: *i64 = tm[0] as *i64
818 let tr: *i64 = tm[1] as *i64
819 let tb: *i64 = tm[2] as *i64
820 let t0: *u8 = sys_mmap(128)
821 let t1: *u8 = sys_mmap(128)
822 let t2: *u8 = sys_mmap(128)
823 var i: i64 = 0
824 while i < sz {
825 var e: i64 = i
826 var go: i64 = 1
827 while go == 1 {
828 if e >= sz { go = 0 } else {
829 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 }
830 }
831 }
832 var p: i64 = nv_tok(b, i, e, t0, 128)
833 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
834 if nv_eq(t0, "TMPL" as *u8) == 1 {
835 p = nv_tok(b, p, e, t1, 128)
836 p = nv_tok(b, p, e, t2, 128)
837 let body: *u8 = sys_mmap(K_MAGIC_4096)
838 nv_rest(b, p, e, body, K_MAGIC_4096)
839 if tm[3] < 64 { tk[tm[3]] = nv_dup(t1) as i64; tr[tm[3]] = nv_dup(t2) as i64; tb[tm[3]] = body as i64; tm[3] = tm[3] + 1 }
840 }
841 i = e + 1
842 }
843 if tm[3] < 1 { return 0 as *i64 }
844 return tm
845}
846
847func nv_find_tmpl(tm: *i64, kind: *u8, rel: *u8) -> i64 {
848 let tk: *i64 = tm[0] as *i64
849 let tr: *i64 = tm[1] as *i64
850 var i: i64 = 0
851 while i < tm[3] {
852 if nv_eq(tk[i] as *u8, kind) == 1 { if nv_eq(tr[i] as *u8, rel) == 1 { return i } }
853 i = i + 1
854 }
855 return 0 - 1
856}
857
858// draft one note (ni) using its event + the matching template; err: 0 ok,
859// 1 missing-template, 2 unknown-event. returns new offset.
860func nv_draft_one(g: *i64, ni: i64, tm: *i64, names: *i64, rep: *u8, o: i64, err: *i64) -> i64 {
861 err[0] = 0
862 let ev_id: *i64 = g[0] as *i64
863 let ev_days: *i64 = g[1] as *i64
864 let ev_ct: *i64 = g[2] as *i64
865 let ev_kind: *i64 = g[3] as *i64
866 let nt_id: *i64 = g[5] as *i64
867 let nt_ev: *i64 = g[6] as *i64
868 let nt_rel: *i64 = g[7] as *i64
869 let nt_off: *i64 = g[8] as *i64
870 let nt_chan: *i64 = g[9] as *i64
871 let nt_pers: *i64 = g[10] as *i64
872 let ei: i64 = nv_find(ev_id, g[4], nt_ev[ni] as *u8)
873 if ei < 0 { err[0] = 2; return o }
874 let kind: *u8 = ev_kind[ei] as *u8
875 let rel: *u8 = nt_rel[ni] as *u8
876 let ev: i64 = ev_days[ei]
877 var release: i64 = ev
878 if nv_eq(rel, "BEFORE" as *u8) == 1 { release = ev - nt_off[ni] }
879 if nv_eq(rel, "AFTER" as *u8) == 1 { release = ev + nt_off[ni] }
880 let ti: i64 = nv_find_tmpl(tm, kind, rel)
881 if ti < 0 { err[0] = 1; return o }
882 let datestr: *u8 = sys_mmap(32)
883 nv_daystr(ev, datestr)
884 let relstr: *u8 = sys_mmap(32)
885 nv_daystr(release, relstr)
886 let vals: *i64 = sys_mmap(8 * 8) as *i64
887 vals[0] = ev_ct[ei]; vals[1] = kind as i64; vals[2] = datestr as i64; vals[3] = nt_pers[ni]
888 vals[4] = nt_ev[ni]; vals[5] = relstr as i64; vals[6] = rel as i64
889 let tb: *i64 = tm[2] as *i64
890 var oo: i64 = o
891 oo = nv_cat(rep, oo, "VIZSLA-DRAFT id=" as *u8)
892 oo = nv_cat(rep, oo, nt_id[ni] as *u8)
893 oo = nv_cat(rep, oo, " kind=" as *u8)
894 oo = nv_cat(rep, oo, kind)
895 oo = nv_cat(rep, oo, " rel=" as *u8)
896 oo = nv_cat(rep, oo, rel)
897 oo = nv_cat(rep, oo, " channel=" as *u8)
898 oo = nv_cat(rep, oo, nt_chan[ni] as *u8)
899 oo = nv_cat(rep, oo, " personal=" as *u8)
900 oo = nv_cat(rep, oo, nt_pers[ni] as *u8)
901 oo = nv_cat(rep, oo, "\n" as *u8)
902 let filled: *u8 = sys_mmap(K_MAGIC_4096)
903 nv_fill(tb[ti] as *u8, names, vals, 7, filled)
904 oo = nv_cat(rep, oo, filled)
905 oo = nv_cat(rep, oo, "\n" as *u8)
906 oo = nv_cat(rep, oo, "VIZSLA-DRAFT-END id=" as *u8)
907 oo = nv_cat(rep, oo, nt_id[ni] as *u8)
908 oo = nv_cat(rep, oo, "\n" as *u8)
909 return oo
910}
911
912func nv_slotnames(names: *i64) -> i64 {
913 names[0] = "contact" as *u8 as i64
914 names[1] = "kind" as *u8 as i64
915 names[2] = "date" as *u8 as i64
916 names[3] = "personal" as *u8 as i64
917 names[4] = "event" as *u8 as i64
918 names[5] = "release" as *u8 as i64
919 names[6] = "rel" as *u8 as i64
920 return 7
921}
922
923// ---------------- DRAFT (one note; human reviews + sends) ----------------
924func nv_cmd_draft(argc: i64, argv: *i64) -> i64 {
925 if argc < 5 { nv_p("VIZSLA-NOTE draft needs <prefix> <templates> <note-id> -- fail loud\n" as *u8); return 1 }
926 let prefix: *u8 = argv[2] as *u8
927 let tm: *i64 = nv_load_templates(argv[3] as *u8)
928 if (tm as i64) == 0 { nv_p("VIZSLA-NOTE templates MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
929 let want: *u8 = argv[4] as *u8
930 let g: *i64 = nv_collect(prefix)
931 let nt_id: *i64 = g[5] as *i64
932 let ni: i64 = nv_find(nt_id, g[11], want)
933 if ni < 0 { nv_p("VIZSLA-NOTE draft: no such note-id -- fail loud\n" as *u8); return 1 }
934 let names: *i64 = sys_mmap(8 * 8) as *i64
935 nv_slotnames(names)
936 let rep: *u8 = sys_mmap(K_MAGIC_16384)
937 let err: *i64 = sys_mmap(16) as *i64
938 let o: i64 = nv_draft_one(g, ni, tm, names, rep, 0, err)
939 if err[0] == 2 { nv_p("VIZSLA-NOTE draft: note references unknown EVENT -- fail loud\n" as *u8); return 1 }
940 if err[0] == 1 { nv_p("VIZSLA-NOTE draft: no template for this kind+relation -- add a TMPL row (fail loud)\n" as *u8); return 1 }
941 sys_write(1, rep, o)
942 let lp: *u8 = sys_mmap(512)
943 nv_prefixlog(prefix, lp)
944 nv_log(lp, "draft" as *u8, rep, o)
945 return 0
946}
947
948// ---------------- DRAFTDUE (draft every note that is DUE today) ----------------
949func nv_cmd_draftdue(argc: i64, argv: *i64) -> i64 {
950 if argc < 5 { nv_p("VIZSLA-NOTE draftdue needs <prefix> <templates> <today> [conf] -- fail loud\n" as *u8); return 1 }
951 let prefix: *u8 = argv[2] as *u8
952 let tm: *i64 = nv_load_templates(argv[3] as *u8)
953 if (tm as i64) == 0 { nv_p("VIZSLA-NOTE templates MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
954 let today: i64 = nv_datedays(argv[4] as *u8)
955 if today < 0 { nv_p("VIZSLA-NOTE bad today date -- fail loud\n" as *u8); return 1 }
956 // conf is OPTIONAL: with it, per-occasion windows apply; without it, the
957 // global default window is used (no magic number -- the default lives here)
958 var cf: *i64 = sys_mmap(8 * 8) as *i64
959 cf[0] = 3; cf[7] = 0
960 if argc > 5 {
961 let loaded: *i64 = nv_load_conf(argv[5] as *u8)
962 if (loaded as i64) == 0 { nv_p("VIZSLA-NOTE conf MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
963 cf = loaded
964 }
965
966 let g: *i64 = nv_collect(prefix)
967 let ev_id: *i64 = g[0] as *i64
968 let ev_days: *i64 = g[1] as *i64
969 let ev_kind: *i64 = g[3] as *i64
970 let nt_ev: *i64 = g[6] as *i64
971 let nt_rel: *i64 = g[7] as *i64
972 let nt_off: *i64 = g[8] as *i64
973 let nt_pid: *i64 = g[5] as *i64
974 let snt: *i64 = g[12] as *i64
975 let names: *i64 = sys_mmap(8 * 8) as *i64
976 nv_slotnames(names)
977 let rep: *u8 = sys_mmap(K_MAGIC_65536)
978 let err: *i64 = sys_mmap(16) as *i64
979 var o: i64 = 0
980 var drafted: i64 = 0
981 var i: i64 = 0
982 while i < g[11] {
983 let ei: i64 = nv_find(ev_id, g[4], nt_ev[i] as *u8)
984 if ei < 0 { nv_p("VIZSLA-NOTE draftdue: note references unknown EVENT -- fail loud\n" as *u8); return 1 }
985 let ev: i64 = ev_days[ei]
986 let off: i64 = nt_off[i]
987 let window: i64 = nv_occ_window(cf, ev_kind[ei] as *u8)
988 var release: i64 = ev
989 var deadline: i64 = ev + window
990 if nv_eq(nt_rel[i] as *u8, "BEFORE" as *u8) == 1 { release = ev - off; deadline = ev }
991 if nv_eq(nt_rel[i] as *u8, "AFTER" as *u8) == 1 { release = ev + off; deadline = ev + off + window }
992 var due: i64 = 0
993 if nv_find(snt, g[13], nt_pid[i] as *u8) < 0 {
994 if today >= release { if today <= deadline { due = 1 } }
995 }
996 if due == 1 {
997 o = nv_draft_one(g, i, tm, names, rep, o, err)
998 if err[0] == 1 { nv_p("VIZSLA-NOTE draftdue: no template for a DUE note's kind+relation -- add a TMPL row (fail loud)\n" as *u8); return 1 }
999 drafted = drafted + 1
1000 }
1001 i = i + 1
1002 }
1003 o = nv_cat(rep, o, "VIZSLA-NOTE-DRAFT-DUE-VERDICT drafted=" as *u8)
1004 o = nv_catn(rep, o, drafted)
1005 o = nv_cat(rep, o, "\n" as *u8)
1006 sys_write(1, rep, o)
1007 let lp: *u8 = sys_mmap(512)
1008 nv_prefixlog(prefix, lp)
1009 nv_log(lp, "draftdue" as *u8, rep, o)
1010 return 0
1011}
1012
1013// ---------------- OCCASIONS (the etiquette / customary-timing reference) ----------------
1014func nv_cmd_occasions(argc: i64, argv: *i64) -> i64 {
1015 if argc < 3 { nv_p("VIZSLA-NOTE occasions needs <conf> -- fail loud\n" as *u8); return 1 }
1016 let cf: *i64 = nv_load_conf(argv[2] as *u8)
1017 if (cf as i64) == 0 { nv_p("VIZSLA-NOTE conf MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
1018 let oc_kind: *i64 = cf[2] as *i64
1019 let oc_rel: *i64 = cf[3] as *i64
1020 let oc_off: *i64 = cf[4] as *i64
1021 let oc_win: *i64 = cf[5] as *i64
1022 let oc_etiq: *i64 = cf[6] as *i64
1023 let rep: *u8 = sys_mmap(K_MAGIC_65536)
1024 var o: i64 = 0
1025 var i: i64 = 0
1026 while i < cf[7] {
1027 o = nv_cat(rep, o, "VIZSLA-OCCASION kind=" as *u8)
1028 o = nv_cat(rep, o, oc_kind[i] as *u8)
1029 o = nv_cat(rep, o, " rel=" as *u8)
1030 o = nv_cat(rep, o, oc_rel[i] as *u8)
1031 o = nv_cat(rep, o, " offset=" as *u8)
1032 o = nv_catn(rep, o, oc_off[i])
1033 o = nv_cat(rep, o, " window=" as *u8)
1034 o = nv_catn(rep, o, oc_win[i])
1035 o = nv_cat(rep, o, " etiquette=" as *u8)
1036 o = nv_cat(rep, o, oc_etiq[i] as *u8)
1037 o = nv_cat(rep, o, "\n" as *u8)
1038 i = i + 1
1039 }
1040 o = nv_cat(rep, o, "VIZSLA-OCCASION-VERDICT occasions=" as *u8)
1041 o = nv_catn(rep, o, cf[7])
1042 o = nv_cat(rep, o, "\n" as *u8)
1043 sys_write(1, rep, o)
1044 return 0
1045}
1046
1047// ---------------- SUGGEST (recommended timing + etiquette for a kind) ----------------
1048func nv_cmd_suggest(argc: i64, argv: *i64) -> i64 {
1049 if argc < 4 { nv_p("VIZSLA-NOTE suggest needs <conf> <kind> -- fail loud\n" as *u8); return 1 }
1050 let cf: *i64 = nv_load_conf(argv[2] as *u8)
1051 if (cf as i64) == 0 { nv_p("VIZSLA-NOTE conf MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
1052 let kind: *u8 = argv[3] as *u8
1053 let idx: i64 = nv_occ_idx(cf, kind)
1054 if idx < 0 { nv_p("VIZSLA-NOTE suggest: no OCCASION for this kind -- add an OCCASION row (fail loud)\n" as *u8); return 1 }
1055 let oc_rel: *i64 = cf[3] as *i64
1056 let oc_off: *i64 = cf[4] as *i64
1057 let oc_win: *i64 = cf[5] as *i64
1058 let oc_etiq: *i64 = cf[6] as *i64
1059 let rep: *u8 = sys_mmap(K_MAGIC_4096)
1060 var o: i64 = 0
1061 o = nv_cat(rep, o, "VIZSLA-SUGGEST kind=" as *u8)
1062 o = nv_cat(rep, o, kind)
1063 o = nv_cat(rep, o, " rel=" as *u8)
1064 o = nv_cat(rep, o, oc_rel[idx] as *u8)
1065 o = nv_cat(rep, o, " offset=" as *u8)
1066 o = nv_catn(rep, o, oc_off[idx])
1067 o = nv_cat(rep, o, " window=" as *u8)
1068 o = nv_catn(rep, o, oc_win[idx])
1069 o = nv_cat(rep, o, " etiquette=" as *u8)
1070 o = nv_cat(rep, o, oc_etiq[idx] as *u8)
1071 o = nv_cat(rep, o, "\n" as *u8)
1072 sys_write(1, rep, o)
1073 return 0
1074}
1075
1076func main(argc: i64, argv: *i64) -> i64 {
1077 if argc < 2 { nv_p("VIZSLA-NOTE usage: load|schedule|due|draft|draftdue|occasions|suggest -- fail loud\n" as *u8); return 1 }
1078 let cmd: *u8 = argv[1] as *u8
1079 if nv_eq(cmd, "load" as *u8) == 1 { return nv_cmd_load(argc, argv) }
1080 if nv_eq(cmd, "schedule" as *u8) == 1 { return nv_cmd_schedule(argc, argv) }
1081 if nv_eq(cmd, "due" as *u8) == 1 { return nv_cmd_due(argc, argv) }
1082 if nv_eq(cmd, "draft" as *u8) == 1 { return nv_cmd_draft(argc, argv) }
1083 if nv_eq(cmd, "draftdue" as *u8) == 1 { return nv_cmd_draftdue(argc, argv) }
1084 if nv_eq(cmd, "occasions" as *u8) == 1 { return nv_cmd_occasions(argc, argv) }
1085 if nv_eq(cmd, "suggest" as *u8) == 1 { return nv_cmd_suggest(argc, argv) }
1086 nv_p("VIZSLA-NOTE unknown command (load|schedule|due|draft|draftdue|occasions|suggest) -- fail loud\n" as *u8)
1087 return 1
1088}