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}