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}