code wiki / _hdl_build / nx_vizsla_budget.nx
nx_vizsla_budget.nx source
↩ module page · 604 lines · 21540 B
1// nx_vizsla_budget.nx -- NISHI VIZSLA V1: the family budget ledger.
2// Fills finance/EDITOR (reconcile) and feeds finance/ARCHITECT (plan) from the
3// V0 gap profile. Storage = the INFOMGMT seg_store substrate (append-only
4// immutable segments, rename(2) commit point, fsync power-loss closure) with
5// nx_canon_cid content addressing -- NO SQL, additive law: money history is
6// never destroyed.
7//
8// Commands (argv[1]):
9// load <txnfile> <prefix> [segid] parse TXN rows -> canonical records ->
10// CID keys "txn:nxc1-..."; a record whose
11// CID already exists in the store is a
12// DUP not a rewrite = IDEMPOTENT RE-LOAD
13// (law 10) by construction. segid
14// defaults to sys_now_us(); the gate
15// passes explicit ids for determinism.
16// report <prefix> <envfile> [asof] per-category sums vs ENV envelopes.
17// asof = max segment id to read (0 or
18// absent = ALL) = TIME-TRAVEL: the
19// ledger exactly as it stood at that
20// commit, reconstructed from history.
21// verify <prefix> recompute every record's CID and
22// compare to its key = TAMPER-EVIDENCE.
23// Any flipped byte names its segment.
24// (no args) report on the durable family store
25// knowledge/vizsla/ledger- with
26// knowledge/vizsla/envelopes.txt.
27//
28// File formats (line records, # comments):
29// TXN <date> <amount_cents> <category> <desc> (amount: digits only --
30// V1 scope is the expense ledger; credits/income = V1b, flagged not silent)
31// ENV <category> <budget_cents>
32//
33// Determinism: stdout has no clocks; epoch header goes to "<prefix>budget.log"
34// only (log path derived from the store prefix, so gate fixtures in /tmp can
35// never pollute the family log). Loud-fail law: missing files, malformed TXN
36// rows, non-digit amounts = exit 1 with a named reason.
37// EXCEED vs consumer budget apps: additive history + as-of-commit time travel
38// + content-address tamper evidence; none of Mint/YNAB-class tools prove any.
39// spec: knowledge/specs/2026-06-10-nishi-vizsla-ladder.md license_tier: ORIGINAL
40import "nx_syscalls.nx"
41import "nx_canon_cid.nx"
42import "nx_seg_store.nx"
43const K_MAGIC_2048: i64 = 2048
44const K_MAGIC_4096: i64 = 4096
45const K_MAGIC_65536: i64 = 65536
46
47func vb_slen(s: *u8) -> i64 {
48 var n: i64 = 0
49 while s[n] != (0 as u8) { n = n + 1 }
50 return n
51}
52
53func vb_p(s: *u8) -> i64 {
54 sys_write(1, s, vb_slen(s))
55 return 0
56}
57
58func vb_eq(a: *u8, b: *u8) -> i64 {
59 var i: i64 = 0
60 var go: i64 = 1
61 while go == 1 {
62 if a[i] != b[i] { return 0 }
63 if a[i] == (0 as u8) { return 1 }
64 i = i + 1
65 }
66 return 0
67}
68
69func vb_dup(s: *u8) -> *u8 {
70 let n: i64 = vb_slen(s)
71 let d: *u8 = sys_mmap(n + 2)
72 var i: i64 = 0
73 while i <= n { d[i] = s[i]; i = i + 1 }
74 return d
75}
76
77func vb_atoi(s: *u8) -> i64 {
78 var v: i64 = 0
79 var i: i64 = 0
80 while s[i] != (0 as u8) {
81 let d: i64 = (s[i] as i64) - 48
82 if d >= 0 { if d <= 9 { v = v * 10 + d } }
83 i = i + 1
84 }
85 return v
86}
87
88// strict: 1 iff non-empty and every char is a digit (boundary validation, law 12)
89func vb_digits(s: *u8) -> i64 {
90 if s[0] == (0 as u8) { return 0 }
91 var i: i64 = 0
92 while s[i] != (0 as u8) {
93 let d: i64 = (s[i] as i64) - 48
94 if d < 0 { return 0 }
95 if d > 9 { return 0 }
96 i = i + 1
97 }
98 return 1
99}
100
101func vb_isws(c: i64) -> i64 {
102 if c == 32 { return 1 }
103 if c == 13 { return 1 }
104 if c == 9 { return 1 }
105 return 0
106}
107
108// next whitespace-delimited token from b[off..lend) -> dst; returns new offset
109func vb_tok(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 {
110 var p: i64 = off
111 var go: i64 = 1
112 while go == 1 {
113 if p >= lend { go = 0 } else {
114 if vb_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 }
115 }
116 }
117 var t: i64 = 0
118 go = 1
119 while go == 1 {
120 if p >= lend { go = 0 } else {
121 if vb_isws(b[p] as i64) == 1 { go = 0 } else {
122 if t < cap - 1 { dst[t] = b[p]; t = t + 1 }
123 p = p + 1
124 }
125 }
126 }
127 dst[t] = 0 as u8
128 return p
129}
130
131func vb_cat(dst: *u8, off: i64, s: *u8) -> i64 {
132 var i: i64 = 0
133 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 }
134 return off + i
135}
136
137func vb_catn(dst: *u8, off: i64, v: i64) -> i64 {
138 var o: i64 = off
139 var m: i64 = v
140 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m }
141 let t: *u8 = sys_mmap(28)
142 var k: i64 = 0
143 if m == 0 { t[0] = 48 as u8; k = 1 }
144 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
145 var i: i64 = 0
146 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
147 return o + k
148}
149
150// find s in a ptr-list; -1 if absent
151func vb_find(list: *i64, n: i64, s: *u8) -> i64 {
152 var i: i64 = 0
153 while i < n {
154 if vb_eq(list[i] as *u8, s) == 1 { return i }
155 i = i + 1
156 }
157 return 0 - 1
158}
159
160// compare blob bytes b[off..off+n) against null-terminated s of length n
161func vb_kmemeq(b: *u8, off: i64, n: i64, s: *u8) -> i64 {
162 if vb_slen(s) != n { return 0 }
163 var i: i64 = 0
164 while i < n {
165 if b[off + i] != s[i] { return 0 }
166 i = i + 1
167 }
168 return 1
169}
170
171// extract field `want` from canonical NXR1 bytes into out; 1 found / 0 absent
172func vb_field(rec: *u8, rl: i64, want: *u8, out: *u8, cap: i64) -> i64 {
173 if rl < 8 { return 0 }
174 let nf: i64 = ss_r32(rec, 4)
175 var off: i64 = 8
176 var fi: i64 = 0
177 while fi < nf {
178 if off + 8 > rl { return 0 }
179 let kl: i64 = ss_r32(rec, off)
180 let koff: i64 = off + 4
181 let vl: i64 = ss_r32(rec, koff + kl)
182 let voff: i64 = koff + kl + 4
183 if vb_kmemeq(rec, koff, kl, want) == 1 {
184 var t: i64 = 0
185 while t < vl {
186 if t < cap - 1 { out[t] = rec[voff + t] }
187 t = t + 1
188 }
189 if t > cap - 1 { t = cap - 1 }
190 out[t] = 0 as u8
191 return 1
192 }
193 off = voff + vl
194 fi = fi + 1
195 }
196 return 0
197}
198
199// is entry key b[koff..koff+kl) a "txn:" record key? (flag-style, shallow ifs)
200func vb_istxn(b: *u8, koff: i64, kl: i64) -> i64 {
201 if kl <= 4 { return 0 }
202 return vb_kmemeq(b, koff, 4, "txn:" as *u8)
203}
204
205// "<prefix>budget.log" -- log home derived from the store prefix (fixture isolation)
206func vb_logpath(prefix: *u8, out: *u8) -> i64 {
207 var o: i64 = 0
208 o = vb_cat(out, o, prefix)
209 o = vb_cat(out, o, "budget.log" as *u8)
210 out[o] = 0 as u8
211 return o
212}
213
214// append `rep` (o bytes) to the prefix log with an epoch header line
215func vb_log(prefix: *u8, tag: *u8, rep: *u8, o: i64) -> i64 {
216 let lp: *u8 = sys_mmap(512)
217 vb_logpath(prefix, lp)
218 let fd: i64 = sys_openat_append(lp, 0x1a4)
219 if fd <= 0 { return 0 }
220 let hdr: *u8 = sys_mmap(256)
221 var ho: i64 = 0
222 ho = vb_cat(hdr, ho, "VIZSLA-BUDGET epoch=" as *u8)
223 ho = vb_catn(hdr, ho, sys_now_realtime_sec())
224 ho = vb_cat(hdr, ho, " cmd=" as *u8)
225 ho = vb_cat(hdr, ho, tag)
226 ho = vb_cat(hdr, ho, "\n" as *u8)
227 sys_write(fd, hdr, ho)
228 sys_write(fd, rep, o)
229 sys_close(fd)
230 return 0
231}
232
233// accumulate one canonical txn record into the category table.
234// st: [0]=cats ptr-list [1]=sums [2]=ncat [3]=txns [4]=total_cents
235func vb_acc(rec: *u8, rl: i64, st: *i64) -> i64 {
236 let fcat: *u8 = sys_mmap(128)
237 let famt: *u8 = sys_mmap(128)
238 if vb_field(rec, rl, "category" as *u8, fcat, 128) == 0 { return 0 }
239 if vb_field(rec, rl, "amount" as *u8, famt, 128) == 0 { return 0 }
240 let cats: *i64 = st[0] as *i64
241 let sums: *i64 = st[1] as *i64
242 var ci: i64 = vb_find(cats, st[2], fcat)
243 if ci < 0 {
244 if st[2] >= 256 { return 0 }
245 cats[st[2]] = vb_dup(fcat) as i64
246 sums[st[2]] = 0
247 ci = st[2]
248 st[2] = st[2] + 1
249 }
250 let amt: i64 = vb_atoi(famt)
251 sums[ci] = sums[ci] + amt
252 st[3] = st[3] + 1
253 st[4] = st[4] + amt
254 return 1
255}
256
257// ---------------- LOAD ----------------
258func vb_cmd_load(argc: i64, argv: *i64) -> i64 {
259 if argc < 4 { vb_p("VIZSLA-BUDGET load needs <txnfile> <prefix> -- fail loud\n" as *u8); return 1 }
260 let tpath: *u8 = argv[2] as *u8
261 let prefix: *u8 = argv[3] as *u8
262 var segid: i64 = 0
263 if argc > 4 { segid = vb_atoi(argv[4] as *u8) }
264 if segid == 0 { segid = sys_now_us() }
265
266 let szp: *i64 = sys_mmap(16) as *i64
267 let b: *u8 = ss_readall(tpath, szp)
268 let sz: i64 = szp[0]
269 if sz <= 0 { vb_p("VIZSLA-BUDGET txnfile MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
270
271 let keys: *i64 = sys_mmap(8 * 8) as *i64
272 let vals: *i64 = sys_mmap(8 * 8) as *i64
273 keys[0] = "kind" as *u8 as i64
274 keys[1] = "date" as *u8 as i64
275 keys[2] = "amount" as *u8 as i64
276 keys[3] = "category" as *u8 as i64
277 keys[4] = "desc" as *u8 as i64
278 vals[0] = "txn" as *u8 as i64
279
280 let t0: *u8 = sys_mmap(128)
281 let dt: *u8 = sys_mmap(128)
282 let am: *u8 = sys_mmap(128)
283 let ct: *u8 = sys_mmap(128)
284 let ds: *u8 = sys_mmap(256)
285 let pp: *i64 = sys_mmap(16) as *i64
286 let ll: *i64 = sys_mmap(16) as *i64
287
288 let w: *i64 = ss_begin()
289 let seen: *i64 = sys_mmap(8 * K_MAGIC_2048) as *i64
290 var nseen: i64 = 0
291 var scanned: i64 = 0
292 var added: i64 = 0
293 var dup_infile: i64 = 0
294 var dup_instore: i64 = 0
295
296 var i: i64 = 0
297 while i < sz {
298 var e: i64 = i
299 var go: i64 = 1
300 while go == 1 {
301 if e >= sz { go = 0 } else {
302 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 }
303 }
304 }
305 var p: i64 = vb_tok(b, i, e, t0, 128)
306 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
307 if vb_eq(t0, "TXN" as *u8) == 1 {
308 scanned = scanned + 1
309 p = vb_tok(b, p, e, dt, 128)
310 p = vb_tok(b, p, e, am, 128)
311 p = vb_tok(b, p, e, ct, 128)
312 p = vb_tok(b, p, e, ds, 256)
313 if dt[0] == (0 as u8) { vb_p("VIZSLA-BUDGET TXN missing date -- fail loud\n" as *u8); return 1 }
314 if vb_digits(am) == 0 { vb_p("VIZSLA-BUDGET TXN amount not pure digits (credits=V1b) -- fail loud\n" as *u8); return 1 }
315 if ct[0] == (0 as u8) { vb_p("VIZSLA-BUDGET TXN missing category -- fail loud\n" as *u8); return 1 }
316 if ds[0] == (0 as u8) { vb_p("VIZSLA-BUDGET TXN missing desc -- fail loud\n" as *u8); return 1 }
317 vals[1] = dt as i64
318 vals[2] = am as i64
319 vals[3] = ct as i64
320 vals[4] = ds as i64
321 let enc: *u8 = sys_mmap(K_MAGIC_2048)
322 let el: i64 = canon_encode(keys, vals, 5, enc)
323 let cid: *u8 = sys_mmap(96)
324 cid_of(enc, el, cid)
325 let kbuf: *u8 = sys_mmap(128)
326 var ko: i64 = 0
327 ko = vb_cat(kbuf, ko, "txn:" as *u8)
328 ko = vb_cat(kbuf, ko, cid)
329 kbuf[ko] = 0 as u8
330 // flattened dedup ladder (in-file CID repeat, then in-store CID hit)
331 var fresh: i64 = 1
332 if vb_find(seen, nseen, kbuf) >= 0 { fresh = 0; dup_infile = dup_infile + 1 }
333 if fresh == 1 {
334 if nseen < K_MAGIC_2048 { seen[nseen] = vb_dup(kbuf) as i64; nseen = nseen + 1 }
335 }
336 if fresh == 1 {
337 if ss_get_idx(prefix, kbuf, pp, ll) == 1 { fresh = 0; dup_instore = dup_instore + 1 }
338 }
339 if fresh == 1 {
340 if ss_add(w, 1, kbuf, enc, el) != 0 { vb_p("VIZSLA-BUDGET writer full -- fail loud\n" as *u8); return 1 }
341 added = added + 1
342 }
343 }
344 i = e + 1
345 }
346
347 var committed: i64 = 0
348 if added > 0 {
349 if ss_commit(prefix, w, segid) != 0 { vb_p("VIZSLA-BUDGET commit FAILED -- fail loud\n" as *u8); return 1 }
350 committed = 1
351 }
352
353 let rep: *u8 = sys_mmap(K_MAGIC_4096)
354 var o: i64 = 0
355 o = vb_cat(rep, o, "VIZSLA-LEDGER-LOAD scanned=" as *u8)
356 o = vb_catn(rep, o, scanned)
357 o = vb_cat(rep, o, " new=" as *u8)
358 o = vb_catn(rep, o, added)
359 o = vb_cat(rep, o, " dup_infile=" as *u8)
360 o = vb_catn(rep, o, dup_infile)
361 o = vb_cat(rep, o, " dup_instore=" as *u8)
362 o = vb_catn(rep, o, dup_instore)
363 o = vb_cat(rep, o, " segment=" as *u8)
364 if committed == 1 {
365 o = vb_cat(rep, o, "seg-" as *u8)
366 o = vb_catn(rep, o, segid)
367 } else {
368 o = vb_cat(rep, o, "none" as *u8)
369 }
370 o = vb_cat(rep, o, "\n" as *u8)
371 sys_write(1, rep, o)
372 vb_log(prefix, "load" as *u8, rep, o)
373 return 0
374}
375
376// ---------------- VERIFY (tamper-evidence) ----------------
377func vb_cmd_verify(argc: i64, argv: *i64) -> i64 {
378 if argc < 3 { vb_p("VIZSLA-BUDGET verify needs <prefix> -- fail loud\n" as *u8); return 1 }
379 let prefix: *u8 = argv[2] as *u8
380 let segs: *i64 = sys_mmap(8 * 260) as *i64
381 let ns: i64 = ss_manifest(prefix, segs)
382 if ns == 0 { vb_p("VIZSLA-BUDGET verify: no manifest/segments -- fail loud\n" as *u8); return 1 }
383 let rep: *u8 = sys_mmap(K_MAGIC_65536)
384 var o: i64 = 0
385 var entries: i64 = 0
386 var bad: i64 = 0
387 var s: i64 = 0
388 while s < ns {
389 let path: *u8 = sys_mmap(512)
390 var po: i64 = 0
391 po = vb_cat(path, po, prefix)
392 po = vb_cat(path, po, segs[s] as *u8)
393 po = vb_cat(path, po, ".docs" as *u8)
394 path[po] = 0 as u8
395 let szp: *i64 = sys_mmap(16) as *i64
396 let b: *u8 = ss_readall(path, szp)
397 let sz: i64 = szp[0]
398 var i: i64 = 0
399 while i + 9 <= sz {
400 let kl: i64 = ss_r32(b, i + 1)
401 let koff: i64 = i + 5
402 let vl: i64 = ss_r32(b, koff + kl)
403 let voff: i64 = koff + kl + 4
404 if vb_istxn(b, koff, kl) == 1 {
405 entries = entries + 1
406 let cid: *u8 = sys_mmap(96)
407 let cl: i64 = cid_of((b as i64 + voff) as *u8, vl, cid)
408 var ok: i64 = 1
409 if kl - 4 != cl { ok = 0 }
410 var t: i64 = 0
411 while t < cl {
412 if ok == 1 { if b[koff + 4 + t] != cid[t] { ok = 0 } }
413 t = t + 1
414 }
415 if ok == 0 {
416 bad = bad + 1
417 o = vb_cat(rep, o, "VIZSLA-LEDGER-TAMPER segment=" as *u8)
418 o = vb_cat(rep, o, segs[s] as *u8)
419 o = vb_cat(rep, o, " entry=" as *u8)
420 o = vb_catn(rep, o, entries)
421 o = vb_cat(rep, o, " cid-mismatch\n" as *u8)
422 }
423 }
424 i = voff + vl
425 }
426 s = s + 1
427 }
428 o = vb_cat(rep, o, "VIZSLA-LEDGER-VERIFY entries=" as *u8)
429 o = vb_catn(rep, o, entries)
430 o = vb_cat(rep, o, " bad=" as *u8)
431 o = vb_catn(rep, o, bad)
432 if bad == 0 {
433 o = vb_cat(rep, o, " verdict=CLEAN\n" as *u8)
434 } else {
435 o = vb_cat(rep, o, " verdict=TAMPERED\n" as *u8)
436 }
437 sys_write(1, rep, o)
438 vb_log(prefix, "verify" as *u8, rep, o)
439 if bad == 0 { return 0 }
440 return 1
441}
442
443// ---------------- REPORT (default; time-travel via asof) ----------------
444func vb_cmd_report(prefix: *u8, epath: *u8, asof: i64) -> i64 {
445 let szp: *i64 = sys_mmap(16) as *i64
446 let eb: *u8 = ss_readall(epath, szp)
447 let esz: i64 = szp[0]
448 if esz <= 0 { vb_p("VIZSLA-BUDGET envelopes MISSING/EMPTY -- fail loud\n" as *u8); return 1 }
449
450 // parse envelopes (file order = report order)
451 let e_cat: *i64 = sys_mmap(8 * 64) as *i64
452 let e_bud: *i64 = sys_mmap(8 * 64) as *i64
453 var nenv: i64 = 0
454 let t0: *u8 = sys_mmap(128)
455 let t1: *u8 = sys_mmap(128)
456 let t2: *u8 = sys_mmap(128)
457 var i: i64 = 0
458 while i < esz {
459 var e: i64 = i
460 var go: i64 = 1
461 while go == 1 {
462 if e >= esz { go = 0 } else {
463 if eb[e] == (10 as u8) { go = 0 } else { e = e + 1 }
464 }
465 }
466 var p: i64 = vb_tok(eb, i, e, t0, 128)
467 if t0[0] == (35 as u8) { t0[0] = 0 as u8 }
468 if vb_eq(t0, "ENV" as *u8) == 1 {
469 p = vb_tok(eb, p, e, t1, 128)
470 p = vb_tok(eb, p, e, t2, 128)
471 if vb_digits(t2) == 0 { vb_p("VIZSLA-BUDGET ENV budget not pure digits -- fail loud\n" as *u8); return 1 }
472 if nenv < 64 { e_cat[nenv] = vb_dup(t1) as i64; e_bud[nenv] = vb_atoi(t2); nenv = nenv + 1 }
473 }
474 i = e + 1
475 }
476
477 // walk committed segments (chronological; asof bounds the read = time travel)
478 let segs: *i64 = sys_mmap(8 * 260) as *i64
479 let ns: i64 = ss_manifest(prefix, segs)
480 let st: *i64 = sys_mmap(8 * 8) as *i64
481 st[0] = sys_mmap(8 * 256)
482 st[1] = sys_mmap(8 * 256)
483 st[2] = 0
484 st[3] = 0
485 st[4] = 0
486 var segread: i64 = 0
487 var s: i64 = 0
488 while s < ns {
489 var use: i64 = 1
490 if asof > 0 { if vb_atoi(segs[s] as *u8) > asof { use = 0 } }
491 if use == 1 {
492 segread = segread + 1
493 let path: *u8 = sys_mmap(512)
494 var po: i64 = 0
495 po = vb_cat(path, po, prefix)
496 po = vb_cat(path, po, segs[s] as *u8)
497 po = vb_cat(path, po, ".docs" as *u8)
498 path[po] = 0 as u8
499 let szp2: *i64 = sys_mmap(16) as *i64
500 let b: *u8 = ss_readall(path, szp2)
501 let sz: i64 = szp2[0]
502 var j: i64 = 0
503 while j + 9 <= sz {
504 let kind: i64 = b[j]
505 let kl: i64 = ss_r32(b, j + 1)
506 let koff: i64 = j + 5
507 let vl: i64 = ss_r32(b, koff + kl)
508 let voff: i64 = koff + kl + 4
509 var take: i64 = 0
510 if kind == 1 { take = vb_istxn(b, koff, kl) }
511 if take == 1 { vb_acc((b as i64 + voff) as *u8, vl, st) }
512 j = voff + vl
513 }
514 }
515 s = s + 1
516 }
517 let cats: *i64 = st[0] as *i64
518 let sums: *i64 = st[1] as *i64
519 let ncat: i64 = st[2]
520
521 // compose the report: envelope-order rows, then unbudgeted (honesty: named, not dropped)
522 let rep: *u8 = sys_mmap(K_MAGIC_65536)
523 var o: i64 = 0
524 o = vb_cat(rep, o, "VIZSLA-LEDGER-REPORT asof=" as *u8)
525 if asof > 0 { o = vb_catn(rep, o, asof) } else { o = vb_cat(rep, o, "ALL" as *u8) }
526 o = vb_cat(rep, o, " segments=" as *u8)
527 o = vb_catn(rep, o, segread)
528 o = vb_cat(rep, o, "\n" as *u8)
529 var over: i64 = 0
530 var unb: i64 = 0
531 var ei: i64 = 0
532 while ei < nenv {
533 var spent: i64 = 0
534 let ci2: i64 = vb_find(cats, ncat, e_cat[ei] as *u8)
535 if ci2 >= 0 { spent = sums[ci2] }
536 let delta: i64 = e_bud[ei] - spent
537 o = vb_cat(rep, o, "VIZSLA-LEDGER category=" as *u8)
538 o = vb_cat(rep, o, e_cat[ei] as *u8)
539 o = vb_cat(rep, o, " spent=" as *u8)
540 o = vb_catn(rep, o, spent)
541 o = vb_cat(rep, o, " budget=" as *u8)
542 o = vb_catn(rep, o, e_bud[ei])
543 o = vb_cat(rep, o, " delta=" as *u8)
544 o = vb_catn(rep, o, delta)
545 if delta >= 0 {
546 o = vb_cat(rep, o, " state=OK\n" as *u8)
547 } else {
548 o = vb_cat(rep, o, " state=OVER\n" as *u8)
549 over = over + 1
550 }
551 ei = ei + 1
552 }
553 var ci3: i64 = 0
554 while ci3 < ncat {
555 if vb_find(e_cat, nenv, cats[ci3] as *u8) < 0 {
556 unb = unb + 1
557 o = vb_cat(rep, o, "VIZSLA-LEDGER category=" as *u8)
558 o = vb_cat(rep, o, cats[ci3] as *u8)
559 o = vb_cat(rep, o, " spent=" as *u8)
560 o = vb_catn(rep, o, sums[ci3])
561 o = vb_cat(rep, o, " budget=0 delta=" as *u8)
562 o = vb_catn(rep, o, 0 - sums[ci3])
563 o = vb_cat(rep, o, " state=UNBUDGETED\n" as *u8)
564 }
565 ci3 = ci3 + 1
566 }
567 o = vb_cat(rep, o, "VIZSLA-LEDGER-TOTAL txns=" as *u8)
568 o = vb_catn(rep, o, st[3])
569 o = vb_cat(rep, o, " spent=" as *u8)
570 o = vb_catn(rep, o, st[4])
571 o = vb_cat(rep, o, "\n" as *u8)
572 o = vb_cat(rep, o, "VIZSLA-LEDGER-VERDICT envelopes=" as *u8)
573 o = vb_catn(rep, o, nenv)
574 o = vb_cat(rep, o, " over=" as *u8)
575 o = vb_catn(rep, o, over)
576 o = vb_cat(rep, o, " unbudgeted=" as *u8)
577 o = vb_catn(rep, o, unb)
578 if over == 0 {
579 o = vb_cat(rep, o, " state=WITHIN\n" as *u8)
580 } else {
581 o = vb_cat(rep, o, " state=ATTENTION\n" as *u8)
582 }
583 sys_write(1, rep, o)
584 vb_log(prefix, "report" as *u8, rep, o)
585 return 0
586}
587
588func main(argc: i64, argv: *i64) -> i64 {
589 var cmd: *u8 = "report" as *u8
590 if argc > 1 { cmd = argv[1] as *u8 }
591 if vb_eq(cmd, "load" as *u8) == 1 { return vb_cmd_load(argc, argv) }
592 if vb_eq(cmd, "verify" as *u8) == 1 { return vb_cmd_verify(argc, argv) }
593 if vb_eq(cmd, "report" as *u8) == 0 {
594 vb_p("VIZSLA-BUDGET unknown command (load|report|verify) -- fail loud\n" as *u8)
595 return 1
596 }
597 var prefix: *u8 = "knowledge/vizsla/ledger-" as *u8
598 var epath: *u8 = "knowledge/vizsla/envelopes.txt" as *u8
599 var asof: i64 = 0
600 if argc > 2 { prefix = argv[2] as *u8 }
601 if argc > 3 { epath = argv[3] as *u8 }
602 if argc > 4 { asof = vb_atoi(argv[4] as *u8) }
603 return vb_cmd_report(prefix, epath, asof)
604}