code wiki / _hdl_build / nx_vizsla_relate.nx
nx_vizsla_relate.nx source
↩ module page · 1056 lines · 39928 B
1// nx_vizsla_relate.nx -- NISHI VIZSLA V4: the relationships / PRM keystone.
2// Operator directive 2026-06-14: "build the project management and PRM ... help
3// us manage our contacts, thank yous". This is the sovereign home of the old
4// nishi-prm "Tom Hagen consigliere" design: proactive counsel, a BIDIRECTIONAL
5// obligation ledger (debts I owe AND credits owed to me), and thank-you tracking
6// as a managed obligation -- folded onto the VIZSLA substrate (CID records on
7// seg_store, civil-date math, idempotent-by-construction) inherited from V2c.
8//
9// THEORY AS DATA (law 11/25): relationship tiers + their desired touch cadence,
10// and the "coming up soon" window, all live in contacts.txt -- refining the
11// model (a new tier, a tighter cadence) is a data edit, never a recompile.
12//
13// Commands (argv[1]):
14// load <relate_log> <prefix> [segid] TOUCH/OBLIGATION/SETTLE/THANKS/SENT
15// rows -> canonical records -> CID keys
16// "rel:nxc1-..." on seg_store = additive,
17// idempotent-by-construction (law 10).
18// cadence <prefix> <contacts> <today> per contact: LATEST touch date vs its
19// TIER cadence -> FRESH / DUE(reconnect) /
20// NEW(introduce). today via argv =
21// deterministic stdout.
22// ledger <prefix> <contacts> bidirectional obligation ledger: open
23// OWE (I owe) + open DUE (owed to me)
24// after applying SETTLE; per-direction
25// counts + cent totals. Closing is an
26// ADDITIVE settle event, never a delete.
27// thanks <prefix> <contacts> <today> open thank-yous (THANKS with no matching
28// SENT): days owed, PENDING(<=soon_days) /
29// OVERDUE. Gratitude as a tracked debt.
30// brief <prefix> <contacts> <today> the CONSIGLIERE daily brief: follow-ups
31// due + birthdays within soon_days + open
32// thank-yous + open obligations, composed
33// into one "what to do today".
34//
35// File formats (line records, # comments):
36// contacts: CONF soon_days <n>
37// TIER <name> <cadence_days>
38// CONTACT <id> <display> <tier> <birthday> (birthday MM-DD or "-")
39// relate_log:
40// TOUCH <date> <contact> <note>
41// OBLIGATION <date> <dir> <contact> <ref> <amount_cents> <desc>
42// (dir = OWE | DUE)
43// SETTLE <date> <ref> <note> (closes that ref)
44// THANKS <date> <contact> <occasion>
45// SENT <date> <contact> <occasion> (closes that thanks)
46// (date YYYY-MM-DD; note/desc/occasion = single hyphenated tokens this rung;
47// rich-text drafts are V4b / the thankyounotes.com product.)
48//
49// Determinism: stdout has no clocks (today via argv; epoch headers go to logs
50// only). Loud-fail law: missing files, CONTACT on an undeclared TIER, malformed
51// dates, OBLIGATION dir not OWE/DUE = exit 1.
52// EXCEED (honest, feature-class -- not a throughput claim): no consumer PRM
53// (Clay/Dex/Folk/Monica) models a BIDIRECTIONAL obligation ledger or thank-you-
54// as-managed-obligation, and none keep additive tamper-evident relationship
55// history with as-of time-travel. Outreach DRAFT prose = V4b.
56//
57// module: nishi-core.vizsla.relate
58// depends: nishi-core.lib.syscalls, nishi-core.lib.canon_cid, nishi-core.lib.seg_store
59// capability: VIZSLA_RELATE_PRM
60// spec: knowledge/specs/2026-06-10-nishi-vizsla-ladder.md license_tier: ORIGINAL
61import "nx_syscalls.nx"
62import "nx_canon_cid.nx"
63import "nx_seg_store.nx"
64const K_MAGIC_1970: i64 = 1970
65const K_MAGIC_146097: i64 = 146097
66const K_MAGIC_719468: i64 = 719468
67const K_MAGIC_4096: i64 = 4096
68const K_MAGIC_2048: i64 = 2048
69const K_MAGIC_65536: i64 = 65536
70const K_MAGIC_131072: i64 = 131072
71
72func rl_slen(s: *u8) -> i64 {
73 var n: i64 = 0
74 while s[n] != (0 as u8) { n = n + 1 }
75 return n
76}
77
78func rl_p(s: *u8) -> i64 {
79 sys_write(1, s, rl_slen(s))
80 return 0
81}
82
83func rl_eq(a: *u8, b: *u8) -> i64 {
84 var i: i64 = 0
85 var go: i64 = 1
86 while go == 1 {
87 if a[i] != b[i] { return 0 }
88 if a[i] == (0 as u8) { return 1 }
89 i = i + 1
90 }
91 return 0
92}
93
94func rl_dup(s: *u8) -> *u8 {
95 let n: i64 = rl_slen(s)
96 let d: *u8 = sys_mmap(n + 2)
97 var i: i64 = 0
98 while i <= n { d[i] = s[i]; i = i + 1 }
99 return d
100}
101
102func rl_atoi(s: *u8) -> i64 {
103 var v: i64 = 0
104 var i: i64 = 0
105 while s[i] != (0 as u8) {
106 let d: i64 = (s[i] as i64) - 48
107 if d >= 0 { if d <= 9 { v = v * 10 + d } }
108 i = i + 1
109 }
110 return v
111}
112
113func rl_isws(c: i64) -> i64 {
114 if c == 32 { return 1 }
115 if c == 13 { return 1 }
116 if c == 9 { return 1 }
117 return 0
118}
119
120func rl_tok(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 {
121 var p: i64 = off
122 var go: i64 = 1
123 while go == 1 {
124 if p >= lend { go = 0 } else {
125 if rl_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 }
126 }
127 }
128 var t: i64 = 0
129 go = 1
130 while go == 1 {
131 if p >= lend { go = 0 } else {
132 if rl_isws(b[p] as i64) == 1 { go = 0 } else {
133 if t < cap - 1 { dst[t] = b[p]; t = t + 1 }
134 p = p + 1
135 }
136 }
137 }
138 dst[t] = 0 as u8
139 return p
140}
141
142func rl_cat(dst: *u8, off: i64, s: *u8) -> i64 {
143 var i: i64 = 0
144 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
145 return off + i
146}
147
148func rl_catn(dst: *u8, off: i64, v: i64) -> i64 {
149 var o: i64 = off
150 var m: i64 = v
151 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
152 let t: *u8 = sys_mmap(28)
153 var k: i64 = 0
154 if m == 0 { t[0] = 48 as u8; k = 1 }
155 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
156 var i: i64 = 0
157 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
158 return o + k
159}
160
161func rl_find(list: *i64, n: i64, s: *u8) -> i64 {
162 var i: i64 = 0
163 while i < n {
164 if rl_eq(list[i] as *u8, s) == 1 { return i }
165 i = i + 1
166 }
167 return 0 - 1
168}
169
170func rl_kmemeq(b: *u8, off: i64, n: i64, s: *u8) -> i64 {
171 if rl_slen(s) != n { return 0 }
172 var i: i64 = 0
173 while i < n {
174 if b[off + i] != s[i] { return 0 }
175 i = i + 1
176 }
177 return 1
178}
179
180// decode a named field out of a canonical NXR1 value record (mirror of V2c)
181func rl_field(rec: *u8, rl: i64, want: *u8, out: *u8, cap: i64) -> i64 {
182 if rl < 8 { return 0 }
183 let nf: i64 = ss_r32(rec, 4)
184 var off: i64 = 8
185 var fi: i64 = 0
186 while fi < nf {
187 if off + 8 > rl { return 0 }
188 let kl: i64 = ss_r32(rec, off)
189 let koff: i64 = off + 4
190 let vl: i64 = ss_r32(rec, koff + kl)
191 let voff: i64 = koff + kl + 4
192 if rl_kmemeq(rec, koff, kl, want) == 1 {
193 var t: i64 = 0
194 while t < vl {
195 if t < cap - 1 { out[t] = rec[voff + t] }
196 t = t + 1
197 }
198 if t > cap - 1 { t = cap - 1 }
199 out[t] = 0 as u8
200 return 1
201 }
202 off = voff + vl
203 fi = fi + 1
204 }
205 return 0
206}
207
208func rl_isrel(b: *u8, koff: i64, kl: i64) -> i64 {
209 if kl <= 4 { return 0 }
210 return rl_kmemeq(b, koff, 4, "rel:" as *u8)
211}
212
213// civil date "YYYY-MM-DD" -> days since 1970-01-01 (Hinnant); -1 if malformed
214func rl_datedays(s: *u8) -> i64 {
215 let nums: *i64 = sys_mmap(8 * 4) as *i64
216 var nn: i64 = 0
217 var cur: i64 = 0
218 var indig: i64 = 0
219 var i: i64 = 0
220 var go: i64 = 1
221 while go == 1 {
222 let c: i64 = s[i] as i64
223 var isd: i64 = 0
224 if c >= 48 { if c <= 57 { isd = 1 } }
225 if isd == 1 { cur = cur * 10 + (c - 48); indig = 1 }
226 if isd == 0 {
227 if indig == 1 {
228 if nn < 4 { nums[nn] = cur; nn = nn + 1 }
229 cur = 0
230 indig = 0
231 }
232 }
233 if c == 0 { go = 0 }
234 i = i + 1
235 }
236 if nn != 3 { return 0 - 1 }
237 var y: i64 = nums[0]
238 let m: i64 = nums[1]
239 let d: i64 = nums[2]
240 if y < K_MAGIC_1970 { return 0 - 1 }
241 if m < 1 { return 0 - 1 }
242 if m > 12 { return 0 - 1 }
243 if d < 1 { return 0 - 1 }
244 if d > 31 { return 0 - 1 }
245 if m <= 2 { y = y - 1 }
246 let era: i64 = y / 400
247 let yoe: i64 = y - era * 400
248 var mp: i64 = m - 3
249 if m <= 2 { mp = m + 9 }
250 let doy: i64 = (153 * mp + 2) / 5 + d - 1
251 let doe: i64 = yoe * 365 + yoe / 4 - yoe / 100 + doy
252 return era * K_MAGIC_146097 + doe - K_MAGIC_719468
253}
254
255// leading integer (the year) out of a "YYYY-..." token
256func rl_yearof(s: *u8) -> i64 {
257 var v: i64 = 0
258 var i: i64 = 0
259 var go: i64 = 1
260 while go == 1 {
261 let c: i64 = s[i] as i64
262 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); i = i + 1; go = 2 } }
263 if go == 1 { go = 0 }
264 if go == 2 { go = 1 }
265 }
266 return v
267}
268
269// days until the next occurrence of a "MM-DD" birthday from today; -1 if "-"
270func rl_bday_until(today: *u8, today_days: i64, mmdd: *u8) -> i64 {
271 if mmdd[0] == (45 as u8) { if mmdd[1] == (0 as u8) { return 0 - 1 } }
272 let yr: i64 = rl_yearof(today)
273 if yr < K_MAGIC_1970 { return 0 - 1 }
274 let cand: *u8 = sys_mmap(32)
275 var co: i64 = 0
276 co = rl_catn(cand, co, yr)
277 cand[co] = 45 as u8; co = co + 1
278 co = rl_cat(cand, co, mmdd)
279 cand[co] = 0 as u8
280 var bdd: i64 = rl_datedays(cand)
281 if bdd < 0 { return 0 - 1 }
282 if bdd < today_days {
283 var c2: i64 = 0
284 c2 = rl_catn(cand, c2, yr + 1)
285 cand[c2] = 45 as u8; c2 = c2 + 1
286 c2 = rl_cat(cand, c2, mmdd)
287 cand[c2] = 0 as u8
288 bdd = rl_datedays(cand)
289 if bdd < 0 { return 0 - 1 }
290 }
291 return bdd - today_days
292}
293
294func rl_log(lpath: *u8, tag: *u8, rep: *u8, o: i64) -> i64 {
295 let fd: i64 = sys_openat_append(lpath, 0x1a4)
296 if fd <= 0 { return 0 }
297 let hdr: *u8 = sys_mmap(256)
298 var ho: i64 = 0
299 ho = rl_cat(hdr, ho, "VIZSLA-RELATE epoch=" as *u8)
300 ho = rl_catn(hdr, ho, sys_now_realtime_sec())
301 ho = rl_cat(hdr, ho, " cmd=" as *u8)
302 ho = rl_cat(hdr, ho, tag)
303 ho = rl_cat(hdr, ho, "\n" as *u8)
304 sys_write(fd, hdr, ho)
305 sys_write(fd, rep, o)
306 sys_close(fd)
307 return 0
308}
309
310func rl_prefixlog(prefix: *u8, out: *u8) -> i64 {
311 var o: i64 = 0
312 o = rl_cat(out, o, prefix)
313 o = rl_cat(out, o, "relate.log" as *u8)
314 out[o] = 0 as u8
315 return o
316}
317
318// contacts table packed in one heap-mmap state block:
319// cf[0]=ct_id cf[1]=ct_disp cf[2]=ct_tier(idx) cf[3]=ct_bday cf[4]=nct
320// cf[5]=tr_name cf[6]=tr_days cf[7]=ntr cf[8]=(reserved) cf[9]=soon_days
321func rl_load_contacts(cpath: *u8) -> *i64 {
322 let szp: *i64 = sys_mmap(16) as *i64
323 let cb: *u8 = ss_readall(cpath, szp)
324 let csz: i64 = szp[0]
325 if csz <= 0 { return 0 as *i64 }
326 let cf: *i64 = sys_mmap(8 * 16) as *i64
327 cf[0] = sys_mmap(8 * 256)
328 cf[1] = sys_mmap(8 * 256)
329 cf[2] = sys_mmap(8 * 256)
330 cf[3] = sys_mmap(8 * 256)
331 cf[4] = 0
332 cf[5] = sys_mmap(8 * 16)
333 cf[6] = sys_mmap(8 * 16)
334 cf[7] = 0
335 cf[8] = 0
336 cf[9] = 14
337 let ct_id: *i64 = cf[0] as *i64
338 let ct_disp: *i64 = cf[1] as *i64
339 let ct_tier: *i64 = cf[2] as *i64
340 let ct_bday: *i64 = cf[3] as *i64
341 let tr_name: *i64 = cf[5] as *i64
342 let tr_days: *i64 = cf[6] as *i64
343 let t0: *u8 = sys_mmap(128)
344 let t1: *u8 = sys_mmap(128)
345 let t2: *u8 = sys_mmap(128)
346 let t3: *u8 = sys_mmap(128)
347 var i: i64 = 0
348 while i < csz {
349 var e: i64 = i
350 var go: i64 = 1
351 while go == 1 {
352 if e >= csz { go = 0 } else {
353 if cb[e] == (10 as u8) { go = 0 } else { e = e + 1 }
354 }
355 }
356 var p: i64 = rl_tok(cb, i, e, t0, 128)
357 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
358 if rl_eq(t0, "CONF" as *u8) == 1 {
359 p = rl_tok(cb, p, e, t1, 128)
360 p = rl_tok(cb, p, e, t2, 128)
361 if rl_eq(t1, "soon_days" as *u8) == 1 { cf[9] = rl_atoi(t2) }
362 }
363 if rl_eq(t0, "TIER" as *u8) == 1 {
364 p = rl_tok(cb, p, e, t1, 128)
365 p = rl_tok(cb, p, e, t2, 128)
366 if cf[7] < 16 { tr_name[cf[7]] = rl_dup(t1) as i64; tr_days[cf[7]] = rl_atoi(t2); cf[7] = cf[7] + 1 }
367 }
368 if rl_eq(t0, "CONTACT" as *u8) == 1 {
369 p = rl_tok(cb, p, e, t1, 128)
370 p = rl_tok(cb, p, e, t2, 128)
371 p = rl_tok(cb, p, e, t3, 128)
372 let ti: i64 = rl_find(tr_name, cf[7], t3)
373 if ti < 0 { return 0 as *i64 }
374 let bd: *u8 = sys_mmap(16)
375 rl_tok(cb, p, e, bd, 16)
376 if bd[0] == (0 as u8) { bd[0] = 45 as u8; bd[1] = 0 as u8 }
377 if cf[4] < 256 {
378 ct_id[cf[4]] = rl_dup(t1) as i64
379 ct_disp[cf[4]] = rl_dup(t2) as i64
380 ct_tier[cf[4]] = ti
381 ct_bday[cf[4]] = rl_dup(bd) as i64
382 cf[4] = cf[4] + 1
383 }
384 }
385 i = e + 1
386 }
387 if cf[7] < 1 { return 0 as *i64 }
388 if cf[4] < 1 { return 0 as *i64 }
389 return cf
390}
391
392// ---------------- LOAD (additive event journal -> CID records) ----------------
393func rl_cmd_load(argc: i64, argv: *i64) -> i64 {
394 if argc < 4 { rl_p("VIZSLA-RELATE load needs <relate_log> <prefix> -- fail loud\n" as *u8); return 1 }
395 let lpath: *u8 = argv[2] as *u8
396 let prefix: *u8 = argv[3] as *u8
397 var segid: i64 = 0
398 if argc > 4 { segid = rl_atoi(argv[4] as *u8) }
399 if segid == 0 { segid = sys_now_us() }
400
401 let szp: *i64 = sys_mmap(16) as *i64
402 let b: *u8 = ss_readall(lpath, szp)
403 let sz: i64 = szp[0]
404 if sz <= 0 { rl_p("VIZSLA-RELATE relate_log MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
405
406 let keys: *i64 = sys_mmap(8 * 8) as *i64
407 let vals: *i64 = sys_mmap(8 * 8) as *i64
408 keys[0] = "k" as *u8 as i64
409 keys[1] = "date" as *u8 as i64
410 keys[2] = "a" as *u8 as i64
411 keys[3] = "b" as *u8 as i64
412 keys[4] = "c" as *u8 as i64
413 keys[5] = "d" as *u8 as i64
414 keys[6] = "e" as *u8 as i64
415
416 let t0: *u8 = sys_mmap(128)
417 let f1: *u8 = sys_mmap(128)
418 let f2: *u8 = sys_mmap(128)
419 let f3: *u8 = sys_mmap(128)
420 let f4: *u8 = sys_mmap(128)
421 let f5: *u8 = sys_mmap(128)
422 let f6: *u8 = sys_mmap(256)
423 let pp: *i64 = sys_mmap(16) as *i64
424 let ll: *i64 = sys_mmap(16) as *i64
425
426 let w: *i64 = ss_begin()
427 let seen: *i64 = sys_mmap(8 * K_MAGIC_4096) as *i64
428 var nseen: i64 = 0
429 var scanned: i64 = 0
430 var added: i64 = 0
431 var dup_infile: i64 = 0
432 var dup_instore: i64 = 0
433
434 var i: i64 = 0
435 while i < sz {
436 var e: i64 = i
437 var go: i64 = 1
438 while go == 1 {
439 if e >= sz { go = 0 } else {
440 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 }
441 }
442 }
443 var p: i64 = rl_tok(b, i, e, t0, 128)
444 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
445 var nf: i64 = 0
446 if rl_eq(t0, "TOUCH" as *u8) == 1 {
447 p = rl_tok(b, p, e, f1, 128); p = rl_tok(b, p, e, f2, 128); p = rl_tok(b, p, e, f3, 256)
448 vals[0] = "touch" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64; vals[3] = f3 as i64
449 nf = 4
450 }
451 if rl_eq(t0, "OBLIGATION" as *u8) == 1 {
452 p = rl_tok(b, p, e, f1, 128); p = rl_tok(b, p, e, f2, 128); p = rl_tok(b, p, e, f3, 128)
453 p = rl_tok(b, p, e, f4, 128); p = rl_tok(b, p, e, f5, 128); p = rl_tok(b, p, e, f6, 256)
454 if rl_eq(f2, "OWE" as *u8) == 0 { if rl_eq(f2, "DUE" as *u8) == 0 { rl_p("VIZSLA-RELATE OBLIGATION dir not OWE/DUE -- fail loud\n" as *u8); return 1 } }
455 vals[0] = "oblig" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64; vals[3] = f3 as i64
456 vals[4] = f4 as i64; vals[5] = f5 as i64; vals[6] = f6 as i64
457 nf = 7
458 }
459 if rl_eq(t0, "SETTLE" as *u8) == 1 {
460 p = rl_tok(b, p, e, f1, 128); p = rl_tok(b, p, e, f2, 128); p = rl_tok(b, p, e, f3, 256)
461 vals[0] = "settle" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64; vals[3] = f3 as i64
462 nf = 4
463 }
464 if rl_eq(t0, "THANKS" as *u8) == 1 {
465 p = rl_tok(b, p, e, f1, 128); p = rl_tok(b, p, e, f2, 128); p = rl_tok(b, p, e, f3, 128)
466 vals[0] = "thanks" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64; vals[3] = f3 as i64
467 nf = 4
468 }
469 if rl_eq(t0, "SENT" as *u8) == 1 {
470 p = rl_tok(b, p, e, f1, 128); p = rl_tok(b, p, e, f2, 128); p = rl_tok(b, p, e, f3, 128)
471 vals[0] = "sent" as *u8 as i64; vals[1] = f1 as i64; vals[2] = f2 as i64; vals[3] = f3 as i64
472 nf = 4
473 }
474 if nf > 0 {
475 scanned = scanned + 1
476 if rl_datedays(f1) < 0 { rl_p("VIZSLA-RELATE bad date (YYYY-MM-DD) -- fail loud\n" as *u8); return 1 }
477 let enc: *u8 = sys_mmap(K_MAGIC_2048)
478 let el: i64 = canon_encode(keys, vals, nf, enc)
479 let cid: *u8 = sys_mmap(96)
480 cid_of(enc, el, cid)
481 let kbuf: *u8 = sys_mmap(128)
482 var ko: i64 = 0
483 ko = rl_cat(kbuf, ko, "rel:" as *u8)
484 ko = rl_cat(kbuf, ko, cid)
485 kbuf[ko] = 0 as u8
486 var fresh: i64 = 1
487 if rl_find(seen, nseen, kbuf) >= 0 { fresh = 0; dup_infile = dup_infile + 1 }
488 if fresh == 1 {
489 if nseen < K_MAGIC_4096 { seen[nseen] = rl_dup(kbuf) as i64; nseen = nseen + 1 }
490 }
491 if fresh == 1 {
492 if ss_get_idx(prefix, kbuf, pp, ll) == 1 { fresh = 0; dup_instore = dup_instore + 1 }
493 }
494 if fresh == 1 {
495 if ss_add(w, 1, kbuf, enc, el) != 0 { rl_p("VIZSLA-RELATE writer full -- fail loud\n" as *u8); return 1 }
496 added = added + 1
497 }
498 }
499 i = e + 1
500 }
501
502 var committed: i64 = 0
503 if added > 0 {
504 if ss_commit(prefix, w, segid) != 0 { rl_p("VIZSLA-RELATE commit FAILED -- fail loud\n" as *u8); return 1 }
505 committed = 1
506 }
507
508 let rep: *u8 = sys_mmap(K_MAGIC_4096)
509 var o: i64 = 0
510 o = rl_cat(rep, o, "VIZSLA-RELATE-LOAD scanned=" as *u8)
511 o = rl_catn(rep, o, scanned)
512 o = rl_cat(rep, o, " new=" as *u8)
513 o = rl_catn(rep, o, added)
514 o = rl_cat(rep, o, " dup_infile=" as *u8)
515 o = rl_catn(rep, o, dup_infile)
516 o = rl_cat(rep, o, " dup_instore=" as *u8)
517 o = rl_catn(rep, o, dup_instore)
518 o = rl_cat(rep, o, " segment=" as *u8)
519 if committed == 1 {
520 o = rl_cat(rep, o, "seg-" as *u8)
521 o = rl_catn(rep, o, segid)
522 } else {
523 o = rl_cat(rep, o, "none" as *u8)
524 }
525 o = rl_cat(rep, o, "\n" as *u8)
526 sys_write(1, rep, o)
527 let lp: *u8 = sys_mmap(512)
528 rl_prefixlog(prefix, lp)
529 rl_log(lp, "load" as *u8, rep, o)
530 return 0
531}
532
533// fill latest-touch (days,date) per contact idx by walking the store
534func rl_latest_touch(prefix: *u8, cf: *i64, a_days: *i64, a_date: *i64) -> i64 {
535 let ct_id: *i64 = cf[0] as *i64
536 var i0: i64 = 0
537 while i0 < cf[4] { a_days[i0] = 0 - 1; a_date[i0] = "never" as *u8 as i64; i0 = i0 + 1 }
538 let segs: *i64 = sys_mmap(8 * 260) as *i64
539 let ns: i64 = ss_manifest(prefix, segs)
540 let fk: *u8 = sys_mmap(64)
541 let fa: *u8 = sys_mmap(128)
542 let fd: *u8 = sys_mmap(128)
543 var touches: i64 = 0
544 var s: i64 = 0
545 while s < ns {
546 let path: *u8 = sys_mmap(512)
547 var po: i64 = 0
548 po = rl_cat(path, po, prefix); po = rl_cat(path, po, segs[s] as *u8); po = rl_cat(path, po, ".docs" as *u8)
549 path[po] = 0 as u8
550 let szp: *i64 = sys_mmap(16) as *i64
551 let b: *u8 = ss_readall(path, szp)
552 let sz: i64 = szp[0]
553 var j: i64 = 0
554 while j + 9 <= sz {
555 let kind: i64 = b[j]
556 let kl: i64 = ss_r32(b, j + 1)
557 let koff: i64 = j + 5
558 let vl: i64 = ss_r32(b, koff + kl)
559 let voff: i64 = koff + kl + 4
560 var take: i64 = 0
561 if kind == 1 { take = rl_isrel(b, koff, kl) }
562 if take == 1 {
563 let rec: *u8 = (b as i64 + voff) as *u8
564 if rl_field(rec, vl, "k" as *u8, fk, 64) == 1 {
565 if rl_eq(fk, "touch" as *u8) == 1 {
566 if rl_field(rec, vl, "a" as *u8, fa, 128) == 1 {
567 if rl_field(rec, vl, "date" as *u8, fd, 128) == 1 {
568 touches = touches + 1
569 let aix: i64 = rl_find(ct_id, cf[4], fa)
570 if aix >= 0 {
571 let dd: i64 = rl_datedays(fd)
572 if dd > a_days[aix] { a_days[aix] = dd; a_date[aix] = rl_dup(fd) as i64 }
573 }
574 }
575 }
576 }
577 }
578 }
579 j = voff + vl
580 }
581 s = s + 1
582 }
583 return touches
584}
585
586// ---------------- CADENCE (tier-driven follow-up) ----------------
587func rl_cmd_cadence(argc: i64, argv: *i64) -> i64 {
588 if argc < 5 { rl_p("VIZSLA-RELATE cadence needs <prefix> <contacts> <today> -- fail loud\n" as *u8); return 1 }
589 let prefix: *u8 = argv[2] as *u8
590 let cf: *i64 = rl_load_contacts(argv[3] as *u8)
591 if (cf as i64) == 0 { rl_p("VIZSLA-RELATE contacts MISSING/MALFORMED -- fail loud\n" as *u8); return 1 }
592 let today: i64 = rl_datedays(argv[4] as *u8)
593 if today < 0 { rl_p("VIZSLA-RELATE bad today date (YYYY-MM-DD) -- fail loud\n" as *u8); return 1 }
594
595 let ct_id: *i64 = cf[0] as *i64
596 let ct_tier: *i64 = cf[2] as *i64
597 let tr_name: *i64 = cf[5] as *i64
598 let tr_days: *i64 = cf[6] as *i64
599 let a_days: *i64 = sys_mmap(8 * 256) as *i64
600 let a_date: *i64 = sys_mmap(8 * 256) as *i64
601 rl_latest_touch(prefix, cf, a_days, a_date)
602
603 let rep: *u8 = sys_mmap(K_MAGIC_65536)
604 var o: i64 = 0
605 var nfresh: i64 = 0
606 var ndue: i64 = 0
607 var nnew: i64 = 0
608 var i: i64 = 0
609 while i < cf[4] {
610 let cad: i64 = tr_days[ct_tier[i]]
611 o = rl_cat(rep, o, "VIZSLA-RELATE-CADENCE contact=" as *u8)
612 o = rl_cat(rep, o, ct_id[i] as *u8)
613 o = rl_cat(rep, o, " tier=" as *u8)
614 o = rl_cat(rep, o, tr_name[ct_tier[i]] as *u8)
615 o = rl_cat(rep, o, " last=" as *u8)
616 o = rl_cat(rep, o, a_date[i] as *u8)
617 o = rl_cat(rep, o, " days=" as *u8)
618 if a_days[i] < 0 { o = rl_catn(rep, o, 0 - 1) } else { o = rl_catn(rep, o, today - a_days[i]) }
619 o = rl_cat(rep, o, " cadence=" as *u8)
620 o = rl_catn(rep, o, cad)
621 if a_days[i] < 0 {
622 nnew = nnew + 1
623 o = rl_cat(rep, o, " state=NEW action=introduce\n" as *u8)
624 } else {
625 if today - a_days[i] <= cad {
626 nfresh = nfresh + 1
627 o = rl_cat(rep, o, " state=FRESH action=none\n" as *u8)
628 } else {
629 ndue = ndue + 1
630 o = rl_cat(rep, o, " state=DUE action=reconnect\n" as *u8)
631 }
632 }
633 i = i + 1
634 }
635 o = rl_cat(rep, o, "VIZSLA-RELATE-CADENCE-VERDICT contacts=" as *u8)
636 o = rl_catn(rep, o, cf[4])
637 o = rl_cat(rep, o, " fresh=" as *u8)
638 o = rl_catn(rep, o, nfresh)
639 o = rl_cat(rep, o, " due=" as *u8)
640 o = rl_catn(rep, o, ndue)
641 o = rl_cat(rep, o, " new=" as *u8)
642 o = rl_catn(rep, o, nnew)
643 o = rl_cat(rep, o, "\n" as *u8)
644 sys_write(1, rep, o)
645 let lp: *u8 = sys_mmap(512)
646 rl_prefixlog(prefix, lp)
647 rl_log(lp, "cadence" as *u8, rep, o)
648 return 0
649}
650
651// collect OPEN obligations (after SETTLE) into an ob block:
652// ob[0]=ct ob[1]=dir ob[2]=ref ob[3]=amt(str) ob[4]=desc ob[5]=nopen ob[6]=settled
653func rl_collect_obligs(prefix: *u8) -> *i64 {
654 let ob: *i64 = sys_mmap(8 * 8) as *i64
655 ob[0] = sys_mmap(8 * 256); ob[1] = sys_mmap(8 * 256); ob[2] = sys_mmap(8 * 256)
656 ob[3] = sys_mmap(8 * 256); ob[4] = sys_mmap(8 * 256); ob[5] = 0; ob[6] = 0
657 let o_ct: *i64 = ob[0] as *i64
658 let o_dir: *i64 = ob[1] as *i64
659 let o_ref: *i64 = ob[2] as *i64
660 let o_amt: *i64 = ob[3] as *i64
661 let o_desc: *i64 = ob[4] as *i64
662 // temp store of all obligations + the settled-ref set
663 let t_ct: *i64 = sys_mmap(8 * 256) as *i64
664 let t_dir: *i64 = sys_mmap(8 * 256) as *i64
665 let t_ref: *i64 = sys_mmap(8 * 256) as *i64
666 let t_amt: *i64 = sys_mmap(8 * 256) as *i64
667 let t_desc: *i64 = sys_mmap(8 * 256) as *i64
668 var nt: i64 = 0
669 let settled: *i64 = sys_mmap(8 * 256) as *i64
670 var nset: i64 = 0
671
672 let segs: *i64 = sys_mmap(8 * 260) as *i64
673 let ns: i64 = ss_manifest(prefix, segs)
674 let fk: *u8 = sys_mmap(64)
675 let fa: *u8 = sys_mmap(128)
676 let fb: *u8 = sys_mmap(128)
677 let fc: *u8 = sys_mmap(128)
678 let fd: *u8 = sys_mmap(128)
679 let fe: *u8 = sys_mmap(256)
680 var s: i64 = 0
681 while s < ns {
682 let path: *u8 = sys_mmap(512)
683 var po: i64 = 0
684 po = rl_cat(path, po, prefix); po = rl_cat(path, po, segs[s] as *u8); po = rl_cat(path, po, ".docs" as *u8)
685 path[po] = 0 as u8
686 let szp: *i64 = sys_mmap(16) as *i64
687 let b: *u8 = ss_readall(path, szp)
688 let sz: i64 = szp[0]
689 var j: i64 = 0
690 while j + 9 <= sz {
691 let kind: i64 = b[j]
692 let kl: i64 = ss_r32(b, j + 1)
693 let koff: i64 = j + 5
694 let vl: i64 = ss_r32(b, koff + kl)
695 let voff: i64 = koff + kl + 4
696 var take: i64 = 0
697 if kind == 1 { take = rl_isrel(b, koff, kl) }
698 if take == 1 {
699 let rec: *u8 = (b as i64 + voff) as *u8
700 if rl_field(rec, vl, "k" as *u8, fk, 64) == 1 {
701 if rl_eq(fk, "oblig" as *u8) == 1 {
702 rl_field(rec, vl, "a" as *u8, fb, 128)
703 rl_field(rec, vl, "b" as *u8, fa, 128)
704 rl_field(rec, vl, "c" as *u8, fc, 128)
705 rl_field(rec, vl, "d" as *u8, fd, 128)
706 rl_field(rec, vl, "e" as *u8, fe, 256)
707 if nt < 256 {
708 t_dir[nt] = rl_dup(fb) as i64
709 t_ct[nt] = rl_dup(fa) as i64
710 t_ref[nt] = rl_dup(fc) as i64
711 t_amt[nt] = rl_dup(fd) as i64
712 t_desc[nt] = rl_dup(fe) as i64
713 nt = nt + 1
714 }
715 }
716 if rl_eq(fk, "settle" as *u8) == 1 {
717 rl_field(rec, vl, "a" as *u8, fc, 128)
718 if nset < 256 { settled[nset] = rl_dup(fc) as i64; nset = nset + 1 }
719 }
720 }
721 }
722 j = voff + vl
723 }
724 s = s + 1
725 }
726 ob[6] = nset
727 var k: i64 = 0
728 while k < nt {
729 if rl_find(settled, nset, t_ref[k] as *u8) < 0 {
730 let n: i64 = ob[5]
731 o_ct[n] = t_ct[k]; o_dir[n] = t_dir[k]; o_ref[n] = t_ref[k]; o_amt[n] = t_amt[k]; o_desc[n] = t_desc[k]
732 ob[5] = n + 1
733 }
734 k = k + 1
735 }
736 return ob
737}
738
739// ---------------- LEDGER (bidirectional obligation ledger) ----------------
740func rl_cmd_ledger(argc: i64, argv: *i64) -> i64 {
741 if argc < 4 { rl_p("VIZSLA-RELATE ledger needs <prefix> <contacts> -- fail loud\n" as *u8); return 1 }
742 let prefix: *u8 = argv[2] as *u8
743 let cf: *i64 = rl_load_contacts(argv[3] as *u8)
744 if (cf as i64) == 0 { rl_p("VIZSLA-RELATE contacts MISSING/MALFORMED -- fail loud\n" as *u8); return 1 }
745
746 let ob: *i64 = rl_collect_obligs(prefix)
747 let o_ct: *i64 = ob[0] as *i64
748 let o_dir: *i64 = ob[1] as *i64
749 let o_ref: *i64 = ob[2] as *i64
750 let o_amt: *i64 = ob[3] as *i64
751 let o_desc: *i64 = ob[4] as *i64
752
753 let rep: *u8 = sys_mmap(K_MAGIC_65536)
754 var o: i64 = 0
755 var nowe: i64 = 0
756 var ndue: i64 = 0
757 var owe_amt: i64 = 0
758 var due_amt: i64 = 0
759 var i: i64 = 0
760 while i < ob[5] {
761 let amt: i64 = rl_atoi(o_amt[i] as *u8)
762 o = rl_cat(rep, o, "VIZSLA-RELATE-OBLIG contact=" as *u8)
763 o = rl_cat(rep, o, o_ct[i] as *u8)
764 o = rl_cat(rep, o, " dir=" as *u8)
765 o = rl_cat(rep, o, o_dir[i] as *u8)
766 o = rl_cat(rep, o, " ref=" as *u8)
767 o = rl_cat(rep, o, o_ref[i] as *u8)
768 o = rl_cat(rep, o, " amount=" as *u8)
769 o = rl_catn(rep, o, amt)
770 o = rl_cat(rep, o, " desc=" as *u8)
771 o = rl_cat(rep, o, o_desc[i] as *u8)
772 o = rl_cat(rep, o, " state=OPEN\n" as *u8)
773 if rl_eq(o_dir[i] as *u8, "OWE" as *u8) == 1 { nowe = nowe + 1; owe_amt = owe_amt + amt } else { ndue = ndue + 1; due_amt = due_amt + amt }
774 i = i + 1
775 }
776 o = rl_cat(rep, o, "VIZSLA-RELATE-OBLIG-VERDICT open=" as *u8)
777 o = rl_catn(rep, o, ob[5])
778 o = rl_cat(rep, o, " owe=" as *u8)
779 o = rl_catn(rep, o, nowe)
780 o = rl_cat(rep, o, " due=" as *u8)
781 o = rl_catn(rep, o, ndue)
782 o = rl_cat(rep, o, " owe_amount=" as *u8)
783 o = rl_catn(rep, o, owe_amt)
784 o = rl_cat(rep, o, " due_amount=" as *u8)
785 o = rl_catn(rep, o, due_amt)
786 o = rl_cat(rep, o, " settled=" as *u8)
787 o = rl_catn(rep, o, ob[6])
788 o = rl_cat(rep, o, "\n" as *u8)
789 sys_write(1, rep, o)
790 let lp: *u8 = sys_mmap(512)
791 rl_prefixlog(prefix, lp)
792 rl_log(lp, "ledger" as *u8, rep, o)
793 return 0
794}
795
796// collect OPEN thank-yous (THANKS with no matching SENT contact|occasion):
797// th[0]=ct th[1]=occ th[2]=dt th[3]=nopen th[4]=sent
798func rl_collect_thanks(prefix: *u8) -> *i64 {
799 let th: *i64 = sys_mmap(8 * 8) as *i64
800 th[0] = sys_mmap(8 * 256); th[1] = sys_mmap(8 * 256); th[2] = sys_mmap(8 * 256); th[3] = 0; th[4] = 0
801 let h_ct: *i64 = th[0] as *i64
802 let h_occ: *i64 = th[1] as *i64
803 let h_dt: *i64 = th[2] as *i64
804 let t_ct: *i64 = sys_mmap(8 * 256) as *i64
805 let t_occ: *i64 = sys_mmap(8 * 256) as *i64
806 let t_dt: *i64 = sys_mmap(8 * 256) as *i64
807 var nt: i64 = 0
808 let sent: *i64 = sys_mmap(8 * 256) as *i64
809 var nsent: i64 = 0
810
811 let segs: *i64 = sys_mmap(8 * 260) as *i64
812 let ns: i64 = ss_manifest(prefix, segs)
813 let fk: *u8 = sys_mmap(64)
814 let fa: *u8 = sys_mmap(128)
815 let fb: *u8 = sys_mmap(128)
816 let fd: *u8 = sys_mmap(128)
817 var s: i64 = 0
818 while s < ns {
819 let path: *u8 = sys_mmap(512)
820 var po: i64 = 0
821 po = rl_cat(path, po, prefix); po = rl_cat(path, po, segs[s] as *u8); po = rl_cat(path, po, ".docs" as *u8)
822 path[po] = 0 as u8
823 let szp: *i64 = sys_mmap(16) as *i64
824 let b: *u8 = ss_readall(path, szp)
825 let sz: i64 = szp[0]
826 var j: i64 = 0
827 while j + 9 <= sz {
828 let kind: i64 = b[j]
829 let kl: i64 = ss_r32(b, j + 1)
830 let koff: i64 = j + 5
831 let vl: i64 = ss_r32(b, koff + kl)
832 let voff: i64 = koff + kl + 4
833 var take: i64 = 0
834 if kind == 1 { take = rl_isrel(b, koff, kl) }
835 if take == 1 {
836 let rec: *u8 = (b as i64 + voff) as *u8
837 if rl_field(rec, vl, "k" as *u8, fk, 64) == 1 {
838 if rl_eq(fk, "thanks" as *u8) == 1 {
839 rl_field(rec, vl, "a" as *u8, fa, 128)
840 rl_field(rec, vl, "b" as *u8, fb, 128)
841 rl_field(rec, vl, "date" as *u8, fd, 128)
842 if nt < 256 { t_ct[nt] = rl_dup(fa) as i64; t_occ[nt] = rl_dup(fb) as i64; t_dt[nt] = rl_dup(fd) as i64; nt = nt + 1 }
843 }
844 if rl_eq(fk, "sent" as *u8) == 1 {
845 rl_field(rec, vl, "a" as *u8, fa, 128)
846 rl_field(rec, vl, "b" as *u8, fb, 128)
847 let key: *u8 = sys_mmap(256)
848 var ko: i64 = 0
849 ko = rl_cat(key, ko, fa); key[ko] = 124 as u8; ko = ko + 1; ko = rl_cat(key, ko, fb); key[ko] = 0 as u8
850 if nsent < 256 { sent[nsent] = rl_dup(key) as i64; nsent = nsent + 1 }
851 }
852 }
853 }
854 j = voff + vl
855 }
856 s = s + 1
857 }
858 th[4] = nsent
859 var k: i64 = 0
860 while k < nt {
861 let key: *u8 = sys_mmap(256)
862 var ko: i64 = 0
863 ko = rl_cat(key, ko, t_ct[k] as *u8); key[ko] = 124 as u8; ko = ko + 1; ko = rl_cat(key, ko, t_occ[k] as *u8); key[ko] = 0 as u8
864 if rl_find(sent, nsent, key) < 0 {
865 let n: i64 = th[3]
866 h_ct[n] = t_ct[k]; h_occ[n] = t_occ[k]; h_dt[n] = t_dt[k]
867 th[3] = n + 1
868 }
869 k = k + 1
870 }
871 return th
872}
873
874// ---------------- THANKS (gratitude as a tracked debt) ----------------
875func rl_cmd_thanks(argc: i64, argv: *i64) -> i64 {
876 if argc < 5 { rl_p("VIZSLA-RELATE thanks needs <prefix> <contacts> <today> -- fail loud\n" as *u8); return 1 }
877 let prefix: *u8 = argv[2] as *u8
878 let cf: *i64 = rl_load_contacts(argv[3] as *u8)
879 if (cf as i64) == 0 { rl_p("VIZSLA-RELATE contacts MISSING/MALFORMED -- fail loud\n" as *u8); return 1 }
880 let today: i64 = rl_datedays(argv[4] as *u8)
881 if today < 0 { rl_p("VIZSLA-RELATE bad today date (YYYY-MM-DD) -- fail loud\n" as *u8); return 1 }
882 let soon: i64 = cf[9]
883
884 let th: *i64 = rl_collect_thanks(prefix)
885 let h_ct: *i64 = th[0] as *i64
886 let h_occ: *i64 = th[1] as *i64
887 let h_dt: *i64 = th[2] as *i64
888
889 let rep: *u8 = sys_mmap(K_MAGIC_65536)
890 var o: i64 = 0
891 var npend: i64 = 0
892 var nover: i64 = 0
893 var i: i64 = 0
894 while i < th[3] {
895 let dd: i64 = rl_datedays(h_dt[i] as *u8)
896 let days: i64 = today - dd
897 o = rl_cat(rep, o, "VIZSLA-RELATE-THANKS contact=" as *u8)
898 o = rl_cat(rep, o, h_ct[i] as *u8)
899 o = rl_cat(rep, o, " occasion=" as *u8)
900 o = rl_cat(rep, o, h_occ[i] as *u8)
901 o = rl_cat(rep, o, " owed=" as *u8)
902 o = rl_cat(rep, o, h_dt[i] as *u8)
903 o = rl_cat(rep, o, " days=" as *u8)
904 o = rl_catn(rep, o, days)
905 if days > soon {
906 nover = nover + 1
907 o = rl_cat(rep, o, " state=OVERDUE\n" as *u8)
908 } else {
909 npend = npend + 1
910 o = rl_cat(rep, o, " state=PENDING\n" as *u8)
911 }
912 i = i + 1
913 }
914 o = rl_cat(rep, o, "VIZSLA-RELATE-THANKS-VERDICT open=" as *u8)
915 o = rl_catn(rep, o, th[3])
916 o = rl_cat(rep, o, " pending=" as *u8)
917 o = rl_catn(rep, o, npend)
918 o = rl_cat(rep, o, " overdue=" as *u8)
919 o = rl_catn(rep, o, nover)
920 o = rl_cat(rep, o, " sent=" as *u8)
921 o = rl_catn(rep, o, th[4])
922 o = rl_cat(rep, o, "\n" as *u8)
923 sys_write(1, rep, o)
924 let lp: *u8 = sys_mmap(512)
925 rl_prefixlog(prefix, lp)
926 rl_log(lp, "thanks" as *u8, rep, o)
927 return 0
928}
929
930// ---------------- BRIEF (the consigliere daily counsel) ----------------
931func rl_cmd_brief(argc: i64, argv: *i64) -> i64 {
932 if argc < 5 { rl_p("VIZSLA-RELATE brief needs <prefix> <contacts> <today> -- fail loud\n" as *u8); return 1 }
933 let prefix: *u8 = argv[2] as *u8
934 let cf: *i64 = rl_load_contacts(argv[3] as *u8)
935 if (cf as i64) == 0 { rl_p("VIZSLA-RELATE contacts MISSING/MALFORMED -- fail loud\n" as *u8); return 1 }
936 let tstr: *u8 = argv[4] as *u8
937 let today: i64 = rl_datedays(tstr)
938 if today < 0 { rl_p("VIZSLA-RELATE bad today date (YYYY-MM-DD) -- fail loud\n" as *u8); return 1 }
939 let soon: i64 = cf[9]
940
941 let ct_id: *i64 = cf[0] as *i64
942 let ct_tier: *i64 = cf[2] as *i64
943 let ct_bday: *i64 = cf[3] as *i64
944 let tr_days: *i64 = cf[6] as *i64
945 let a_days: *i64 = sys_mmap(8 * 256) as *i64
946 let a_date: *i64 = sys_mmap(8 * 256) as *i64
947 rl_latest_touch(prefix, cf, a_days, a_date)
948
949 let rep: *u8 = sys_mmap(K_MAGIC_131072)
950 var o: i64 = 0
951 o = rl_cat(rep, o, "VIZSLA-RELATE-BRIEF date=" as *u8)
952 o = rl_cat(rep, o, tstr)
953 o = rl_cat(rep, o, "\n" as *u8)
954
955 // 1) follow-ups due (tier cadence exceeded) or NEW
956 var nfollow: i64 = 0
957 var i: i64 = 0
958 while i < cf[4] {
959 let cad: i64 = tr_days[ct_tier[i]]
960 var flag: i64 = 0
961 if a_days[i] < 0 { flag = 2 } else { if today - a_days[i] > cad { flag = 1 } }
962 if flag > 0 {
963 nfollow = nfollow + 1
964 o = rl_cat(rep, o, " FOLLOWUP contact=" as *u8)
965 o = rl_cat(rep, o, ct_id[i] as *u8)
966 if flag == 2 {
967 o = rl_cat(rep, o, " (NEW)\n" as *u8)
968 } else {
969 o = rl_cat(rep, o, " days=" as *u8)
970 o = rl_catn(rep, o, today - a_days[i])
971 o = rl_cat(rep, o, " (DUE)\n" as *u8)
972 }
973 }
974 i = i + 1
975 }
976
977 // 2) upcoming birthdays within soon_days
978 var nbday: i64 = 0
979 var bi: i64 = 0
980 while bi < cf[4] {
981 let until: i64 = rl_bday_until(tstr, today, ct_bday[bi] as *u8)
982 if until >= 0 {
983 if until <= soon {
984 nbday = nbday + 1
985 o = rl_cat(rep, o, " BIRTHDAY contact=" as *u8)
986 o = rl_cat(rep, o, ct_id[bi] as *u8)
987 o = rl_cat(rep, o, " on=" as *u8)
988 o = rl_cat(rep, o, ct_bday[bi] as *u8)
989 o = rl_cat(rep, o, " in=" as *u8)
990 o = rl_catn(rep, o, until)
991 o = rl_cat(rep, o, "d\n" as *u8)
992 }
993 }
994 bi = bi + 1
995 }
996
997 // 3) open thank-yous
998 let th: *i64 = rl_collect_thanks(prefix)
999 let h_ct: *i64 = th[0] as *i64
1000 let h_occ: *i64 = th[1] as *i64
1001 let h_dt: *i64 = th[2] as *i64
1002 var ti: i64 = 0
1003 while ti < th[3] {
1004 let dd: i64 = rl_datedays(h_dt[ti] as *u8)
1005 o = rl_cat(rep, o, " THANKYOU contact=" as *u8)
1006 o = rl_cat(rep, o, h_ct[ti] as *u8)
1007 o = rl_cat(rep, o, " occasion=" as *u8)
1008 o = rl_cat(rep, o, h_occ[ti] as *u8)
1009 if today - dd > soon { o = rl_cat(rep, o, " (OVERDUE)\n" as *u8) } else { o = rl_cat(rep, o, " (PENDING)\n" as *u8) }
1010 ti = ti + 1
1011 }
1012
1013 // 4) open obligations
1014 let ob: *i64 = rl_collect_obligs(prefix)
1015 let o_ct: *i64 = ob[0] as *i64
1016 let o_dir: *i64 = ob[1] as *i64
1017 let o_ref: *i64 = ob[2] as *i64
1018 var oi: i64 = 0
1019 while oi < ob[5] {
1020 o = rl_cat(rep, o, " OBLIGATION contact=" as *u8)
1021 o = rl_cat(rep, o, o_ct[oi] as *u8)
1022 o = rl_cat(rep, o, " dir=" as *u8)
1023 o = rl_cat(rep, o, o_dir[oi] as *u8)
1024 o = rl_cat(rep, o, " ref=" as *u8)
1025 o = rl_cat(rep, o, o_ref[oi] as *u8)
1026 o = rl_cat(rep, o, "\n" as *u8)
1027 oi = oi + 1
1028 }
1029
1030 o = rl_cat(rep, o, "VIZSLA-RELATE-BRIEF-VERDICT followups=" as *u8)
1031 o = rl_catn(rep, o, nfollow)
1032 o = rl_cat(rep, o, " birthdays=" as *u8)
1033 o = rl_catn(rep, o, nbday)
1034 o = rl_cat(rep, o, " thankyous=" as *u8)
1035 o = rl_catn(rep, o, th[3])
1036 o = rl_cat(rep, o, " obligations=" as *u8)
1037 o = rl_catn(rep, o, ob[5])
1038 o = rl_cat(rep, o, "\n" as *u8)
1039 sys_write(1, rep, o)
1040 let lp: *u8 = sys_mmap(512)
1041 rl_prefixlog(prefix, lp)
1042 rl_log(lp, "brief" as *u8, rep, o)
1043 return 0
1044}
1045
1046func main(argc: i64, argv: *i64) -> i64 {
1047 if argc < 2 { rl_p("VIZSLA-RELATE usage: load|cadence|ledger|thanks|brief -- fail loud\n" as *u8); return 1 }
1048 let cmd: *u8 = argv[1] as *u8
1049 if rl_eq(cmd, "load" as *u8) == 1 { return rl_cmd_load(argc, argv) }
1050 if rl_eq(cmd, "cadence" as *u8) == 1 { return rl_cmd_cadence(argc, argv) }
1051 if rl_eq(cmd, "ledger" as *u8) == 1 { return rl_cmd_ledger(argc, argv) }
1052 if rl_eq(cmd, "thanks" as *u8) == 1 { return rl_cmd_thanks(argc, argv) }
1053 if rl_eq(cmd, "brief" as *u8) == 1 { return rl_cmd_brief(argc, argv) }
1054 rl_p("VIZSLA-RELATE unknown command (load|cadence|ledger|thanks|brief) -- fail loud\n" as *u8)
1055 return 1
1056}