code wiki / _hdl_build / nx_vizsla_plan.nx

nx_vizsla_plan.nx source

↩ module page · 1309 lines · 47553 B

1// nx_vizsla_plan.nx -- NISHI VIZSLA PLANNER: PMP-grade mechanics, friend-group simple. 2// Operator 2026-07-07: "planning with pmp level but simple for a friend group". Closes the last 3// census-ABSENT planning axes: PA7 PRIORITY (Eisenhower urgency x importance), PA8 DEPENDENCIES 4// (DAG + cycle detection + critical path), PA13 TIME-BLOCKING (place ready tasks into real 5// calendar free windows). All integer, NO-FLOAT, deterministic (today/date are argv, never a clock). 6// 7// Records (load <file> <prefix> [segid], additive + CID-idempotent law 10): 8// CONF <plan> <u4_days> <u3_days> <u2_days> -> "cnf:" urgency bands (data, not magic numbers; 9// absent -> bootstrap defaults 2/7/30) 10// TASK <plan> <tid> <owner> <due> <effort_min> <imp 1-5> <dep[,dep..]|-> <title> -> "tsk:" 11// MILE <plan> <mid> <date> <title> -> "mil:" milestone 12// DONE <plan> <tid> <date> -> "don:" completion marker (additive history) 13// Commands: 14// board <prefix> <plan> <today> 15// per task: state READY (deps done) / BLOCKED / DONE; urgency from days-to-due vs the CONF 16// bands (overdue=5); score = urg*10 + imp*2; late flag. Dependency DAG is Kahn-checked -- 17// a CYCLE or unknown dep names the offenders and fails loud (a plan you cannot execute is a 18// data error, not a rendering choice). CRITICAL PATH = longest-effort dependency chain 19// (topo-order DP), named task-by-task. Milestones report done/due_tasks/pct. Verdict rolls up. 20// timeblock <prefix> <plan> <calprefix> <date> [earliest latest] 21// READY tasks in priority order first-fit into the calendar's real free windows that day 22// (composes the R-CAL busy walk: cal: events + rec: occurrences). Too-big tasks are named 23// UNPLACED -- honesty over silent drop. 24// Loud-fail: bad dates/imp/effort, unknown dep, cycle, unknown plan, bad bounds. 25// spec: knowledge/research/2026-06-22-vizsla-sclass-roadmap.md license_tier: ORIGINAL 26import "nx_syscalls.nx" 27import "nx_canon_cid.nx" 28import "nx_seg_store.nx" 29const K_MAGIC_1970: i64 = 1970 30const K_MAGIC_146097: i64 = 146097 31const K_MAGIC_719468: i64 = 719468 32const K_MAGIC_146096: i64 = 146096 33const K_MAGIC_1460: i64 = 1460 34const K_MAGIC_36524: i64 = 36524 35const K_MAGIC_2048: i64 = 2048 36const K_MAGIC_4096: i64 = 4096 37const K_MAGIC_262144: i64 = 262144 38const K_MAGIC_1440: i64 = 1440 39const K_MAGIC_65536: i64 = 65536 40 41func vp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 42func vp_p(s: *u8) -> i64 { sys_write(1, s, vp_slen(s)); return 0 } 43 44func vp_eq(a: *u8, b: *u8) -> i64 { 45 var i: i64 = 0 46 var go: i64 = 1 47 while go == 1 { 48 if a[i] != b[i] { return 0 } 49 if a[i] == (0 as u8) { return 1 } 50 i = i + 1 51 } 52 return 0 53} 54 55func vp_cmp(a: *u8, b: *u8) -> i64 { 56 var i: i64 = 0 57 var go: i64 = 1 58 while go == 1 { 59 let ca: i64 = a[i] as i64 60 let cb: i64 = b[i] as i64 61 if ca < cb { return 0 - 1 } 62 if ca > cb { return 1 } 63 if ca == 0 { return 0 } 64 i = i + 1 65 } 66 return 0 67} 68 69func vp_dup(s: *u8) -> *u8 { 70 let n: i64 = vp_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 vp_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 88func vp_digits(s: *u8) -> i64 { 89 if s[0] == (0 as u8) { return 0 } 90 var i: i64 = 0 91 while s[i] != (0 as u8) { 92 let d: i64 = (s[i] as i64) - 48 93 if d < 0 { return 0 } 94 if d > 9 { return 0 } 95 i = i + 1 96 } 97 return 1 98} 99 100func vp_isws(c: i64) -> i64 { 101 if c == 32 { return 1 } 102 if c == 13 { return 1 } 103 if c == 9 { return 1 } 104 return 0 105} 106 107func vp_tok(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 { 108 var p: i64 = off 109 var go: i64 = 1 110 while go == 1 { 111 if p >= lend { go = 0 } else { 112 if vp_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 } 113 } 114 } 115 var t: i64 = 0 116 go = 1 117 while go == 1 { 118 if p >= lend { go = 0 } else { 119 if vp_isws(b[p] as i64) == 1 { go = 0 } else { 120 if t < cap - 1 { dst[t] = b[p]; t = t + 1 } 121 p = p + 1 122 } 123 } 124 } 125 dst[t] = 0 as u8 126 return p 127} 128 129func vp_cat(dst: *u8, off: i64, s: *u8) -> i64 { 130 var i: i64 = 0 131 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 132 return off + i 133} 134 135func vp_catn(dst: *u8, off: i64, v: i64) -> i64 { 136 var o: i64 = off 137 var m: i64 = v 138 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m } 139 let t: *u8 = sys_mmap(28) 140 var k: i64 = 0 141 if m == 0 { t[0] = 48 as u8; k = 1 } 142 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 143 var i: i64 = 0 144 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 145 return o + k 146} 147 148func vp_hhmm(dst: *u8, off: i64, m: i64) -> i64 { 149 var o: i64 = off 150 let h: i64 = m / 60 151 let mm: i64 = m - h * 60 152 if h < 10 { dst[o] = 48 as u8; o = o + 1 } 153 o = vp_catn(dst, o, h) 154 dst[o] = 58 as u8; o = o + 1 155 if mm < 10 { dst[o] = 48 as u8; o = o + 1 } 156 o = vp_catn(dst, o, mm) 157 return o 158} 159 160func vp_kmemeq(b: *u8, off: i64, n: i64, s: *u8) -> i64 { 161 if vp_slen(s) != n { return 0 } 162 var i: i64 = 0 163 while i < n { 164 if b[off + i] != s[i] { return 0 } 165 i = i + 1 166 } 167 return 1 168} 169 170func vp_field(rec: *u8, rl: i64, want: *u8, out: *u8, cap: i64) -> i64 { 171 if rl < 8 { return 0 } 172 let nf: i64 = ss_r32(rec, 4) 173 var off: i64 = 8 174 var fi: i64 = 0 175 while fi < nf { 176 if off + 8 > rl { return 0 } 177 let kl: i64 = ss_r32(rec, off) 178 let koff: i64 = off + 4 179 let vl: i64 = ss_r32(rec, koff + kl) 180 let voff: i64 = koff + kl + 4 181 if vp_kmemeq(rec, koff, kl, want) == 1 { 182 var t: i64 = 0 183 while t < vl { 184 if t < cap - 1 { out[t] = rec[voff + t] } 185 t = t + 1 186 } 187 if t > cap - 1 { t = cap - 1 } 188 out[t] = 0 as u8 189 return 1 190 } 191 off = voff + vl 192 fi = fi + 1 193 } 194 return 0 195} 196 197func vp_iskind(b: *u8, koff: i64, kl: i64, pfx: *u8) -> i64 { 198 if kl <= 4 { return 0 } 199 return vp_kmemeq(b, koff, 4, pfx) 200} 201 202func vp_datedays(s: *u8) -> i64 { 203 let nums: *i64 = sys_mmap(8 * 4) as *i64 204 var nn: i64 = 0 205 var cur: i64 = 0 206 var indig: i64 = 0 207 var i: i64 = 0 208 var go: i64 = 1 209 while go == 1 { 210 let c: i64 = s[i] as i64 211 var isd: i64 = 0 212 if c >= 48 { if c <= 57 { isd = 1 } } 213 if isd == 1 { cur = cur * 10 + (c - 48); indig = 1 } 214 if isd == 0 { 215 if indig == 1 { 216 if nn < 4 { nums[nn] = cur; nn = nn + 1 } 217 cur = 0 218 indig = 0 219 } 220 } 221 if c == 0 { go = 0 } 222 i = i + 1 223 } 224 if nn != 3 { return 0 - 1 } 225 var y: i64 = nums[0] 226 let m: i64 = nums[1] 227 let d: i64 = nums[2] 228 if y < K_MAGIC_1970 { return 0 - 1 } 229 if m < 1 { return 0 - 1 } 230 if m > 12 { return 0 - 1 } 231 if d < 1 { return 0 - 1 } 232 if d > 31 { return 0 - 1 } 233 if m <= 2 { y = y - 1 } 234 let era: i64 = y / 400 235 let yoe: i64 = y - era * 400 236 var mp: i64 = m - 3 237 if m <= 2 { mp = m + 9 } 238 let doy: i64 = (153 * mp + 2) / 5 + d - 1 239 let doe: i64 = yoe * 365 + yoe / 4 - yoe / 100 + doy 240 return era * K_MAGIC_146097 + doe - K_MAGIC_719468 241} 242 243func vp_ymd2days(y0: i64, m: i64, d: i64) -> i64 { 244 var y: i64 = y0 245 if m <= 2 { y = y0 - 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 255func vp_civil(z0: i64, out: *i64) -> i64 { 256 var z: i64 = z0 + K_MAGIC_719468 257 var era: i64 = z / K_MAGIC_146097 258 if z < 0 { era = (z - K_MAGIC_146096) / K_MAGIC_146097 } 259 let doe: i64 = z - era * K_MAGIC_146097 260 let yoe: i64 = (doe - doe / K_MAGIC_1460 + doe / K_MAGIC_36524 - doe / K_MAGIC_146096) / 365 261 let y0: i64 = yoe + era * 400 262 let doy: i64 = doe - (365 * yoe + yoe / 4 - yoe / 100) 263 let mp: i64 = (5 * doy + 2) / 153 264 let d: i64 = doy - (153 * mp + 2) / 5 + 1 265 var m: i64 = mp + 3 266 if mp >= 10 { m = mp - 9 } 267 var y: i64 = y0 268 if m <= 2 { y = y0 + 1 } 269 out[0] = y; out[1] = m; out[2] = d 270 return 0 271} 272 273func vp_dim(y: i64, m: i64) -> i64 { 274 if m == 2 { 275 var leap: i64 = 0 276 if y % 4 == 0 { leap = 1 } 277 if y % 100 == 0 { leap = 0 } 278 if y % 400 == 0 { leap = 1 } 279 if leap == 1 { return 29 } 280 return 28 281 } 282 if m == 4 { return 30 } 283 if m == 6 { return 30 } 284 if m == 9 { return 30 } 285 if m == 11 { return 30 } 286 return 31 287} 288 289func vp_occ(freq: *u8, sd0: i64, sy: i64, sm: i64, sd: i64, interval: i64, k: i64) -> i64 { 290 if vp_eq(freq, "DAILY" as *u8) == 1 { return sd0 + k * interval } 291 if vp_eq(freq, "WEEKLY" as *u8) == 1 { return sd0 + k * interval * 7 } 292 var mstep: i64 = interval 293 if vp_eq(freq, "YEARLY" as *u8) == 1 { mstep = interval * 12 } 294 let tot: i64 = (sm - 1) + k * mstep 295 let y2: i64 = sy + tot / 12 296 let m2: i64 = tot - (tot / 12) * 12 + 1 297 var d2: i64 = sd 298 let dim: i64 = vp_dim(y2, m2) 299 if d2 > dim { d2 = dim } 300 return vp_ymd2days(y2, m2, d2) 301} 302 303func vp_find(list: *i64, n: i64, s: *u8) -> i64 { 304 var i: i64 = 0 305 while i < n { 306 if vp_eq(list[i] as *u8, s) == 1 { return i } 307 i = i + 1 308 } 309 return 0 - 1 310} 311 312func vp_put(keys: *i64, vals: *i64, nf: i64, kp: *u8, st: *i64) -> i64 { 313 let enc: *u8 = sys_mmap(K_MAGIC_2048) 314 let el: i64 = canon_encode(keys, vals, nf, enc) 315 let cid: *u8 = sys_mmap(96) 316 cid_of(enc, el, cid) 317 let kbuf: *u8 = sys_mmap(128) 318 var ko: i64 = 0 319 ko = vp_cat(kbuf, ko, kp) 320 ko = vp_cat(kbuf, ko, cid) 321 kbuf[ko] = 0 as u8 322 let seen: *i64 = st[1] as *i64 323 var fresh: i64 = 1 324 if vp_find(seen, st[2], kbuf) >= 0 { fresh = 0; st[4] = st[4] + 1 } 325 if fresh == 1 { 326 if st[2] < K_MAGIC_2048 { 327 let dk: *u8 = vp_dup(kbuf) 328 seen[st[2]] = dk as i64 329 st[2] = st[2] + 1 330 } 331 } 332 if fresh == 1 { 333 let pp: *i64 = sys_mmap(16) as *i64 334 let ll: *i64 = sys_mmap(16) as *i64 335 if ss_get_idx(st[6] as *u8, kbuf, pp, ll) == 1 { fresh = 0; st[5] = st[5] + 1 } 336 } 337 if fresh == 1 { 338 if ss_add(st[0] as *i64, 1, kbuf, enc, el) != 0 { return 0 - 1 } 339 st[3] = st[3] + 1 340 } 341 return 0 342} 343 344// ---------------- LOAD ---------------- 345func vp_cmd_load(argc: i64, argv: *i64) -> i64 { 346 if argc < 4 { vp_p("VIZSLA-PLAN load needs <file> <prefix> -- fail loud\n" as *u8); return 1 } 347 let efile: *u8 = argv[2] as *u8 348 let prefix: *u8 = argv[3] as *u8 349 var segid: i64 = 0 350 if argc > 4 { segid = vp_atoi(argv[4] as *u8) } 351 if segid == 0 { segid = sys_now_us() } 352 353 let szp: *i64 = sys_mmap(16) as *i64 354 let b: *u8 = ss_readall(efile, szp) 355 let sz: i64 = szp[0] 356 if sz <= 0 { vp_p("VIZSLA-PLAN file MISSING/EMPTY -- fail loud\n" as *u8); return 1 } 357 358 let ckeys: *i64 = sys_mmap(8 * 8) as *i64 359 let cvals: *i64 = sys_mmap(8 * 8) as *i64 360 ckeys[0] = "kind" as *u8 as i64 361 ckeys[1] = "plan" as *u8 as i64 362 ckeys[2] = "u4" as *u8 as i64 363 ckeys[3] = "u3" as *u8 as i64 364 ckeys[4] = "u2" as *u8 as i64 365 cvals[0] = "cnf" as *u8 as i64 366 let tkeys: *i64 = sys_mmap(8 * 12) as *i64 367 let tvals: *i64 = sys_mmap(8 * 12) as *i64 368 tkeys[0] = "kind" as *u8 as i64 369 tkeys[1] = "plan" as *u8 as i64 370 tkeys[2] = "tid" as *u8 as i64 371 tkeys[3] = "owner" as *u8 as i64 372 tkeys[4] = "due" as *u8 as i64 373 tkeys[5] = "effort" as *u8 as i64 374 tkeys[6] = "imp" as *u8 as i64 375 tkeys[7] = "dep" as *u8 as i64 376 tkeys[8] = "title" as *u8 as i64 377 tvals[0] = "tsk" as *u8 as i64 378 let mkeys: *i64 = sys_mmap(8 * 8) as *i64 379 let mvals: *i64 = sys_mmap(8 * 8) as *i64 380 mkeys[0] = "kind" as *u8 as i64 381 mkeys[1] = "plan" as *u8 as i64 382 mkeys[2] = "mid" as *u8 as i64 383 mkeys[3] = "date" as *u8 as i64 384 mkeys[4] = "title" as *u8 as i64 385 mvals[0] = "mil" as *u8 as i64 386 let dkeys: *i64 = sys_mmap(8 * 8) as *i64 387 let dvals: *i64 = sys_mmap(8 * 8) as *i64 388 dkeys[0] = "kind" as *u8 as i64 389 dkeys[1] = "plan" as *u8 as i64 390 dkeys[2] = "tid" as *u8 as i64 391 dkeys[3] = "date" as *u8 as i64 392 dvals[0] = "don" as *u8 as i64 393 394 let st: *i64 = sys_mmap(8 * 8) as *i64 395 st[0] = ss_begin() as i64 396 st[1] = sys_mmap(8 * K_MAGIC_2048) 397 st[2] = 0 398 st[3] = 0 399 st[4] = 0 400 st[5] = 0 401 st[6] = prefix as i64 402 403 let t0: *u8 = sys_mmap(128) 404 let f1: *u8 = sys_mmap(128) 405 let f2: *u8 = sys_mmap(128) 406 let f3: *u8 = sys_mmap(128) 407 let f4: *u8 = sys_mmap(128) 408 let f5: *u8 = sys_mmap(128) 409 let f6: *u8 = sys_mmap(256) 410 let f7: *u8 = sys_mmap(256) 411 let fdep: *u8 = sys_mmap(256) 412 let ftl: *u8 = sys_mmap(256) 413 var scanned: i64 = 0 414 415 var i: i64 = 0 416 while i < sz { 417 var e: i64 = i 418 var go: i64 = 1 419 while go == 1 { 420 if e >= sz { go = 0 } else { 421 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 } 422 } 423 } 424 var p: i64 = vp_tok(b, i, e, t0, 128) 425 if t0[0] == (35 as u8) { t0[0] = 0 as u8 } 426 if vp_eq(t0, "CONF" as *u8) == 1 { 427 scanned = scanned + 1 428 p = vp_tok(b, p, e, f1, 128) 429 p = vp_tok(b, p, e, f2, 128) 430 p = vp_tok(b, p, e, f3, 128) 431 p = vp_tok(b, p, e, f4, 128) 432 if f1[0] == (0 as u8) { vp_p("VIZSLA-PLAN CONF missing plan -- fail loud\n" as *u8); return 1 } 433 if vp_digits(f2) == 0 { vp_p("VIZSLA-PLAN CONF u4 not digits -- fail loud\n" as *u8); return 1 } 434 if vp_digits(f3) == 0 { vp_p("VIZSLA-PLAN CONF u3 not digits -- fail loud\n" as *u8); return 1 } 435 if vp_digits(f4) == 0 { vp_p("VIZSLA-PLAN CONF u2 not digits -- fail loud\n" as *u8); return 1 } 436 cvals[1] = f1 as i64 437 cvals[2] = f2 as i64 438 cvals[3] = f3 as i64 439 cvals[4] = f4 as i64 440 if vp_put(ckeys, cvals, 5, "cnf:" as *u8, st) != 0 { vp_p("VIZSLA-PLAN writer full -- fail loud\n" as *u8); return 1 } 441 } 442 if vp_eq(t0, "TASK" as *u8) == 1 { 443 scanned = scanned + 1 444 p = vp_tok(b, p, e, f1, 128) 445 p = vp_tok(b, p, e, f2, 128) 446 p = vp_tok(b, p, e, f3, 128) 447 p = vp_tok(b, p, e, f4, 128) 448 p = vp_tok(b, p, e, f5, 128) 449 p = vp_tok(b, p, e, f6, 256) 450 let fimp: *u8 = f6 451 // TASK <plan> <tid> <owner> <due> <effort> <imp> <dep> <title> 452 p = vp_tok(b, p, e, fdep, 256) 453 p = vp_tok(b, p, e, ftl, 256) 454 if f1[0] == (0 as u8) { vp_p("VIZSLA-PLAN TASK missing plan -- fail loud\n" as *u8); return 1 } 455 if f2[0] == (0 as u8) { vp_p("VIZSLA-PLAN TASK missing tid -- fail loud\n" as *u8); return 1 } 456 if f3[0] == (0 as u8) { vp_p("VIZSLA-PLAN TASK missing owner -- fail loud\n" as *u8); return 1 } 457 if vp_datedays(f4) < 0 { vp_p("VIZSLA-PLAN TASK bad due date -- fail loud\n" as *u8); return 1 } 458 if vp_digits(f5) == 0 { vp_p("VIZSLA-PLAN TASK effort not digits -- fail loud\n" as *u8); return 1 } 459 if vp_atoi(f5) < 1 { vp_p("VIZSLA-PLAN TASK effort < 1 -- fail loud\n" as *u8); return 1 } 460 if vp_digits(fimp) == 0 { vp_p("VIZSLA-PLAN TASK imp not digits -- fail loud\n" as *u8); return 1 } 461 let iv: i64 = vp_atoi(fimp) 462 if iv < 1 { vp_p("VIZSLA-PLAN TASK imp out of 1..5 -- fail loud\n" as *u8); return 1 } 463 if iv > 5 { vp_p("VIZSLA-PLAN TASK imp out of 1..5 -- fail loud\n" as *u8); return 1 } 464 if fdep[0] == (0 as u8) { vp_p("VIZSLA-PLAN TASK missing dep -- fail loud\n" as *u8); return 1 } 465 if ftl[0] == (0 as u8) { vp_p("VIZSLA-PLAN TASK missing title -- fail loud\n" as *u8); return 1 } 466 tvals[1] = f1 as i64 467 tvals[2] = f2 as i64 468 tvals[3] = f3 as i64 469 tvals[4] = f4 as i64 470 tvals[5] = f5 as i64 471 tvals[6] = fimp as i64 472 tvals[7] = fdep as i64 473 tvals[8] = ftl as i64 474 if vp_put(tkeys, tvals, 9, "tsk:" as *u8, st) != 0 { vp_p("VIZSLA-PLAN writer full -- fail loud\n" as *u8); return 1 } 475 } 476 if vp_eq(t0, "MILE" as *u8) == 1 { 477 scanned = scanned + 1 478 p = vp_tok(b, p, e, f1, 128) 479 p = vp_tok(b, p, e, f2, 128) 480 p = vp_tok(b, p, e, f3, 128) 481 p = vp_tok(b, p, e, f7, 256) 482 if f1[0] == (0 as u8) { vp_p("VIZSLA-PLAN MILE missing plan -- fail loud\n" as *u8); return 1 } 483 if f2[0] == (0 as u8) { vp_p("VIZSLA-PLAN MILE missing mid -- fail loud\n" as *u8); return 1 } 484 if vp_datedays(f3) < 0 { vp_p("VIZSLA-PLAN MILE bad date -- fail loud\n" as *u8); return 1 } 485 if f7[0] == (0 as u8) { vp_p("VIZSLA-PLAN MILE missing title -- fail loud\n" as *u8); return 1 } 486 mvals[1] = f1 as i64 487 mvals[2] = f2 as i64 488 mvals[3] = f3 as i64 489 mvals[4] = f7 as i64 490 if vp_put(mkeys, mvals, 5, "mil:" as *u8, st) != 0 { vp_p("VIZSLA-PLAN writer full -- fail loud\n" as *u8); return 1 } 491 } 492 if vp_eq(t0, "DONE" as *u8) == 1 { 493 scanned = scanned + 1 494 p = vp_tok(b, p, e, f1, 128) 495 p = vp_tok(b, p, e, f2, 128) 496 p = vp_tok(b, p, e, f3, 128) 497 if f1[0] == (0 as u8) { vp_p("VIZSLA-PLAN DONE missing plan -- fail loud\n" as *u8); return 1 } 498 if f2[0] == (0 as u8) { vp_p("VIZSLA-PLAN DONE missing tid -- fail loud\n" as *u8); return 1 } 499 if vp_datedays(f3) < 0 { vp_p("VIZSLA-PLAN DONE bad date -- fail loud\n" as *u8); return 1 } 500 dvals[1] = f1 as i64 501 dvals[2] = f2 as i64 502 dvals[3] = f3 as i64 503 if vp_put(dkeys, dvals, 4, "don:" as *u8, st) != 0 { vp_p("VIZSLA-PLAN writer full -- fail loud\n" as *u8); return 1 } 504 } 505 i = e + 1 506 } 507 508 var committed: i64 = 0 509 if st[3] > 0 { 510 if ss_commit(prefix, st[0] as *i64, segid) != 0 { vp_p("VIZSLA-PLAN commit FAILED -- fail loud\n" as *u8); return 1 } 511 committed = 1 512 } 513 514 let rep: *u8 = sys_mmap(K_MAGIC_4096) 515 var o: i64 = 0 516 o = vp_cat(rep, o, "VIZSLA-PLAN-LOAD scanned=" as *u8) 517 o = vp_catn(rep, o, scanned) 518 o = vp_cat(rep, o, " new=" as *u8) 519 o = vp_catn(rep, o, st[3]) 520 o = vp_cat(rep, o, " dup_infile=" as *u8) 521 o = vp_catn(rep, o, st[4]) 522 o = vp_cat(rep, o, " dup_instore=" as *u8) 523 o = vp_catn(rep, o, st[5]) 524 o = vp_cat(rep, o, " segment=" as *u8) 525 if committed == 1 { 526 o = vp_cat(rep, o, "seg-" as *u8) 527 o = vp_catn(rep, o, segid) 528 } else { 529 o = vp_cat(rep, o, "none" as *u8) 530 } 531 o = vp_cat(rep, o, "\n" as *u8) 532 sys_write(1, rep, o) 533 return 0 534} 535 536// gather plan state from the store. st slots: 537// 0=n 1=tid[] 2=owner[] 3=due[] 4=duedays[] 5=effort[] 6=imp[] 7=dep[] 8=title[] 9=doneflag[] 538// 10=nmile 11=mid[] 12=mdate[] 13=mdays[] 14=u4 15=u3 16=u2 539func vp_gather(prefix: *u8, plan: *u8, st: *i64) -> i64 { 540 let segs: *i64 = sys_mmap(8 * 260) as *i64 541 let ns: i64 = ss_manifest(prefix, segs) 542 let fp: *u8 = sys_mmap(128) 543 let fa: *u8 = sys_mmap(256) 544 let fb: *u8 = sys_mmap(256) 545 let fc: *u8 = sys_mmap(256) 546 let fd2: *u8 = sys_mmap(256) 547 let fe: *u8 = sys_mmap(256) 548 let ff: *u8 = sys_mmap(256) 549 let dtids: *i64 = sys_mmap(8 * 256) as *i64 550 var ndt: i64 = 0 551 var s: i64 = 0 552 while s < ns { 553 let path: *u8 = sys_mmap(512) 554 var po: i64 = 0 555 po = vp_cat(path, po, prefix) 556 po = vp_cat(path, po, segs[s] as *u8) 557 po = vp_cat(path, po, ".docs" as *u8) 558 path[po] = 0 as u8 559 let szp: *i64 = sys_mmap(16) as *i64 560 let b: *u8 = ss_readall(path, szp) 561 let sz: i64 = szp[0] 562 var j: i64 = 0 563 while j + 9 <= sz { 564 let kind: i64 = b[j] 565 let kl: i64 = ss_r32(b, j + 1) 566 let koff: i64 = j + 5 567 let vl: i64 = ss_r32(b, koff + kl) 568 let voff: i64 = koff + kl + 4 569 let rec: *u8 = (b as i64 + voff) as *u8 570 var istsk: i64 = 0 571 var ismil: i64 = 0 572 var isdon: i64 = 0 573 var iscnf: i64 = 0 574 if kind == 1 { istsk = vp_iskind(b, koff, kl, "tsk:" as *u8) } 575 if kind == 1 { ismil = vp_iskind(b, koff, kl, "mil:" as *u8) } 576 if kind == 1 { isdon = vp_iskind(b, koff, kl, "don:" as *u8) } 577 if kind == 1 { iscnf = vp_iskind(b, koff, kl, "cnf:" as *u8) } 578 if istsk == 1 { 579 if vp_field(rec, vl, "plan" as *u8, fp, 128) == 1 { if vp_eq(fp, plan) == 1 { 580 let n: i64 = st[0] 581 if n < 128 { 582 vp_field(rec, vl, "tid" as *u8, fa, 256) 583 vp_field(rec, vl, "owner" as *u8, fb, 256) 584 vp_field(rec, vl, "due" as *u8, fc, 256) 585 vp_field(rec, vl, "effort" as *u8, fd2, 256) 586 vp_field(rec, vl, "imp" as *u8, fe, 256) 587 vp_field(rec, vl, "dep" as *u8, ff, 256) 588 let tids: *i64 = st[1] as *i64 589 let owners: *i64 = st[2] as *i64 590 let dues: *i64 = st[3] as *i64 591 let duedays: *i64 = st[4] as *i64 592 let efforts: *i64 = st[5] as *i64 593 let imps: *i64 = st[6] as *i64 594 let deps: *i64 = st[7] as *i64 595 let titles: *i64 = st[8] as *i64 596 // dedup by tid (CID makes exact dups impossible, but a re-defined task 597 // with different fields would double -- first wins, honest count) 598 if vp_find(tids, n, fa) < 0 { 599 let d1: *u8 = vp_dup(fa) 600 let d2: *u8 = vp_dup(fb) 601 let d3: *u8 = vp_dup(fc) 602 let d4: *u8 = vp_dup(ff) 603 tids[n] = d1 as i64 604 owners[n] = d2 as i64 605 dues[n] = d3 as i64 606 duedays[n] = vp_datedays(fc) 607 efforts[n] = vp_atoi(fd2) 608 imps[n] = vp_atoi(fe) 609 deps[n] = d4 as i64 610 vp_field(rec, vl, "title" as *u8, fe, 256) 611 let d5: *u8 = vp_dup(fe) 612 titles[n] = d5 as i64 613 st[0] = n + 1 614 } 615 } 616 } } 617 } 618 if ismil == 1 { 619 if vp_field(rec, vl, "plan" as *u8, fp, 128) == 1 { if vp_eq(fp, plan) == 1 { 620 let nm: i64 = st[10] 621 if nm < 32 { 622 vp_field(rec, vl, "mid" as *u8, fa, 256) 623 vp_field(rec, vl, "date" as *u8, fb, 256) 624 let mids: *i64 = st[11] as *i64 625 let mdates: *i64 = st[12] as *i64 626 let mdays: *i64 = st[13] as *i64 627 let d1: *u8 = vp_dup(fa) 628 let d2: *u8 = vp_dup(fb) 629 mids[nm] = d1 as i64 630 mdates[nm] = d2 as i64 631 mdays[nm] = vp_datedays(fb) 632 st[10] = nm + 1 633 } 634 } } 635 } 636 if isdon == 1 { 637 if vp_field(rec, vl, "plan" as *u8, fp, 128) == 1 { if vp_eq(fp, plan) == 1 { 638 if ndt < 256 { 639 vp_field(rec, vl, "tid" as *u8, fa, 256) 640 if vp_find(dtids, ndt, fa) < 0 { 641 let d1: *u8 = vp_dup(fa) 642 dtids[ndt] = d1 as i64 643 ndt = ndt + 1 644 } 645 } 646 } } 647 } 648 if iscnf == 1 { 649 if vp_field(rec, vl, "plan" as *u8, fp, 128) == 1 { if vp_eq(fp, plan) == 1 { 650 vp_field(rec, vl, "u4" as *u8, fa, 256) 651 vp_field(rec, vl, "u3" as *u8, fb, 256) 652 vp_field(rec, vl, "u2" as *u8, fc, 256) 653 st[14] = vp_atoi(fa) 654 st[15] = vp_atoi(fb) 655 st[16] = vp_atoi(fc) 656 } } 657 } 658 j = voff + vl 659 } 660 s = s + 1 661 } 662 // apply done markers to flags 663 let tids: *i64 = st[1] as *i64 664 let flags: *i64 = st[9] as *i64 665 var t: i64 = 0 666 while t < st[0] { 667 flags[t] = 0 668 if vp_find(dtids, ndt, tids[t] as *u8) >= 0 { flags[t] = 1 } 669 t = t + 1 670 } 671 return 0 672} 673 674func vp_urgency(duedays: i64, today: i64, u4: i64, u3: i64, u2: i64) -> i64 { 675 if duedays < today { return 5 } 676 let d: i64 = duedays - today 677 if d <= u4 { return 4 } 678 if d <= u3 { return 3 } 679 if d <= u2 { return 2 } 680 return 1 681} 682 683// parse comma-separated dep list of task ids into indices; -1 terminated; returns count or -1 on unknown 684func vp_depidx(depstr: *u8, tids: *i64, n: i64, out: *i64) -> i64 { 685 if vp_eq(depstr, "-" as *u8) == 1 { out[0] = 0 - 1; return 0 } 686 let tokb: *u8 = sys_mmap(256) 687 var cnt: i64 = 0 688 var i: i64 = 0 689 var t: i64 = 0 690 var go: i64 = 1 691 while go == 1 { 692 let c: i64 = depstr[i] as i64 693 var flush: i64 = 0 694 if c == 44 { flush = 1 } 695 if c == 0 { flush = 1 } 696 if flush == 1 { 697 tokb[t] = 0 as u8 698 if t > 0 { 699 let idx: i64 = vp_find(tids, n, tokb) 700 if idx < 0 { return 0 - 1 } 701 if cnt < 8 { out[cnt] = idx; cnt = cnt + 1 } 702 } 703 t = 0 704 } else { 705 if t < 255 { tokb[t] = c as u8; t = t + 1 } 706 } 707 if c == 0 { go = 0 } 708 i = i + 1 709 } 710 out[cnt] = 0 - 1 711 return cnt 712} 713 714func vp_alloc_state() -> *i64 { 715 let st: *i64 = sys_mmap(8 * 24) as *i64 716 st[0] = 0 717 st[1] = sys_mmap(8 * 128) 718 st[2] = sys_mmap(8 * 128) 719 st[3] = sys_mmap(8 * 128) 720 st[4] = sys_mmap(8 * 128) 721 st[5] = sys_mmap(8 * 128) 722 st[6] = sys_mmap(8 * 128) 723 st[7] = sys_mmap(8 * 128) 724 st[8] = sys_mmap(8 * 128) 725 st[9] = sys_mmap(8 * 128) 726 st[10] = 0 727 st[11] = sys_mmap(8 * 32) 728 st[12] = sys_mmap(8 * 32) 729 st[13] = sys_mmap(8 * 32) 730 st[14] = 2 731 st[15] = 7 732 st[16] = 30 733 return st 734} 735 736// ---------------- BOARD ---------------- 737func vp_cmd_board(argc: i64, argv: *i64) -> i64 { 738 if argc < 5 { vp_p("VIZSLA-PLAN board needs <prefix> <plan> <today> -- fail loud\n" as *u8); return 1 } 739 let prefix: *u8 = argv[2] as *u8 740 let plan: *u8 = argv[3] as *u8 741 let today: i64 = vp_datedays(argv[4] as *u8) 742 if today < 0 { vp_p("VIZSLA-PLAN board bad today date -- fail loud\n" as *u8); return 1 } 743 744 let st: *i64 = vp_alloc_state() 745 vp_gather(prefix, plan, st) 746 let n: i64 = st[0] 747 if n == 0 { vp_p("VIZSLA-PLAN board plan has NO tasks -- fail loud\n" as *u8); return 1 } 748 let tids: *i64 = st[1] as *i64 749 let owners: *i64 = st[2] as *i64 750 let dues: *i64 = st[3] as *i64 751 let duedays: *i64 = st[4] as *i64 752 let efforts: *i64 = st[5] as *i64 753 let imps: *i64 = st[6] as *i64 754 let depstrs: *i64 = st[7] as *i64 755 let titles: *i64 = st[8] as *i64 756 let done: *i64 = st[9] as *i64 757 758 // dep indices (flat n x 8) + unknown-dep loud-fail 759 let dmat: *i64 = sys_mmap(8 * 128 * 9) as *i64 760 let dcnt: *i64 = sys_mmap(8 * 128) as *i64 761 var i: i64 = 0 762 while i < n { 763 let row: *i64 = (dmat as i64 + i * 9 * 8) as *i64 764 let c: i64 = vp_depidx(depstrs[i] as *u8, tids, n, row) 765 if c < 0 { 766 vp_p("VIZSLA-PLAN-BAD-DEP task=" as *u8) 767 vp_p(tids[i] as *u8) 768 vp_p(" names an unknown dependency -- fail loud\n" as *u8) 769 return 1 770 } 771 dcnt[i] = c 772 i = i + 1 773 } 774 775 // Kahn topological order over ALL tasks (cycle = data error, loud) 776 let processed: *i64 = sys_mmap(8 * 128) as *i64 777 let order: *i64 = sys_mmap(8 * 128) as *i64 778 var norder: i64 = 0 779 i = 0 780 while i < n { processed[i] = 0; i = i + 1 } 781 var progress: i64 = 1 782 while progress == 1 { 783 progress = 0 784 var t: i64 = 0 785 while t < n { 786 if processed[t] == 0 { 787 var ok: i64 = 1 788 let row: *i64 = (dmat as i64 + t * 9 * 8) as *i64 789 var k: i64 = 0 790 while k < dcnt[t] { 791 if processed[row[k]] == 0 { ok = 0 } 792 k = k + 1 793 } 794 if ok == 1 { 795 processed[t] = 1 796 order[norder] = t 797 norder = norder + 1 798 progress = 1 799 } 800 } 801 t = t + 1 802 } 803 } 804 if norder < n { 805 vp_p("VIZSLA-PLAN-CYCLE plan=" as *u8) 806 vp_p(plan) 807 vp_p(" stuck=" as *u8) 808 var t: i64 = 0 809 var first: i64 = 1 810 while t < n { 811 if processed[t] == 0 { 812 if first == 0 { vp_p("," as *u8) } 813 vp_p(tids[t] as *u8) 814 first = 0 815 } 816 t = t + 1 817 } 818 vp_p(" -- dependency cycle, fail loud\n" as *u8) 819 return 1 820 } 821 822 // critical path: longest effort chain (DP in topo order) 823 let longest: *i64 = sys_mmap(8 * 128) as *i64 824 let from: *i64 = sys_mmap(8 * 128) as *i64 825 var oi: i64 = 0 826 while oi < n { 827 let t: i64 = order[oi] 828 var best: i64 = 0 829 var bfrom: i64 = 0 - 1 830 let row: *i64 = (dmat as i64 + t * 9 * 8) as *i64 831 var k: i64 = 0 832 while k < dcnt[t] { 833 if longest[row[k]] > best { best = longest[row[k]]; bfrom = row[k] } 834 k = k + 1 835 } 836 longest[t] = best + efforts[t] 837 from[t] = bfrom 838 oi = oi + 1 839 } 840 var cpend: i64 = 0 841 i = 1 842 while i < n { 843 if longest[i] > longest[cpend] { cpend = i } 844 i = i + 1 845 } 846 // reconstruct chain indices (reversed walk) 847 let chain: *i64 = sys_mmap(8 * 128) as *i64 848 var clen: i64 = 0 849 var cur: i64 = cpend 850 var go: i64 = 1 851 while go == 1 { 852 chain[clen] = cur 853 clen = clen + 1 854 if from[cur] < 0 { go = 0 } else { cur = from[cur] } 855 } 856 857 let rep: *u8 = sys_mmap(K_MAGIC_262144) 858 var o: i64 = 0 859 860 // task lines: not-done sorted by score desc / due asc / tid lex, then done by tid 861 let idxs: *i64 = sys_mmap(8 * 128) as *i64 862 var nn: i64 = 0 863 i = 0 864 while i < n { if done[i] == 0 { idxs[nn] = i; nn = nn + 1 } i = i + 1 } 865 let scores: *i64 = sys_mmap(8 * 128) as *i64 866 i = 0 867 while i < n { 868 let u: i64 = vp_urgency(duedays[i], today, st[14], st[15], st[16]) 869 scores[i] = u * 10 + imps[i] * 2 870 i = i + 1 871 } 872 var a: i64 = 0 873 while a < nn { 874 var mn: i64 = a 875 var c: i64 = a + 1 876 while c < nn { 877 var better: i64 = 0 878 let ic: i64 = idxs[c] 879 let im: i64 = idxs[mn] 880 if scores[ic] > scores[im] { better = 1 } 881 if scores[ic] == scores[im] { 882 if duedays[ic] < duedays[im] { better = 1 } 883 if duedays[ic] == duedays[im] { 884 if vp_cmp(tids[ic] as *u8, tids[im] as *u8) < 0 { better = 1 } 885 } 886 } 887 if better == 1 { mn = c } 888 c = c + 1 889 } 890 if mn != a { let t1: i64 = idxs[a]; idxs[a] = idxs[mn]; idxs[mn] = t1 } 891 a = a + 1 892 } 893 894 var ready: i64 = 0 895 var blocked: i64 = 0 896 var late: i64 = 0 897 var ndone: i64 = 0 898 var ti: i64 = 0 899 while ti < nn { 900 let t: i64 = idxs[ti] 901 var isready: i64 = 1 902 let row: *i64 = (dmat as i64 + t * 9 * 8) as *i64 903 var k: i64 = 0 904 while k < dcnt[t] { 905 if done[row[k]] == 0 { isready = 0 } 906 k = k + 1 907 } 908 var islate: i64 = 0 909 if duedays[t] < today { islate = 1; late = late + 1 } 910 if isready == 1 { ready = ready + 1 } else { blocked = blocked + 1 } 911 let u: i64 = vp_urgency(duedays[t], today, st[14], st[15], st[16]) 912 o = vp_cat(rep, o, "VIZSLA-PLAN-TASK plan=" as *u8) 913 o = vp_cat(rep, o, plan) 914 o = vp_cat(rep, o, " id=" as *u8) 915 o = vp_cat(rep, o, tids[t] as *u8) 916 o = vp_cat(rep, o, " owner=" as *u8) 917 o = vp_cat(rep, o, owners[t] as *u8) 918 o = vp_cat(rep, o, " due=" as *u8) 919 o = vp_cat(rep, o, dues[t] as *u8) 920 o = vp_cat(rep, o, " state=" as *u8) 921 if isready == 1 { o = vp_cat(rep, o, "READY" as *u8) } else { o = vp_cat(rep, o, "BLOCKED" as *u8) } 922 o = vp_cat(rep, o, " urg=" as *u8) 923 o = vp_catn(rep, o, u) 924 o = vp_cat(rep, o, " imp=" as *u8) 925 o = vp_catn(rep, o, imps[t]) 926 o = vp_cat(rep, o, " score=" as *u8) 927 o = vp_catn(rep, o, scores[t]) 928 o = vp_cat(rep, o, " late=" as *u8) 929 o = vp_catn(rep, o, islate) 930 o = vp_cat(rep, o, " title=" as *u8) 931 o = vp_cat(rep, o, titles[t] as *u8) 932 o = vp_cat(rep, o, "\n" as *u8) 933 ti = ti + 1 934 } 935 i = 0 936 while i < n { 937 if done[i] == 1 { 938 ndone = ndone + 1 939 o = vp_cat(rep, o, "VIZSLA-PLAN-TASK plan=" as *u8) 940 o = vp_cat(rep, o, plan) 941 o = vp_cat(rep, o, " id=" as *u8) 942 o = vp_cat(rep, o, tids[i] as *u8) 943 o = vp_cat(rep, o, " owner=" as *u8) 944 o = vp_cat(rep, o, owners[i] as *u8) 945 o = vp_cat(rep, o, " due=" as *u8) 946 o = vp_cat(rep, o, dues[i] as *u8) 947 o = vp_cat(rep, o, " state=DONE title=" as *u8) 948 o = vp_cat(rep, o, titles[i] as *u8) 949 o = vp_cat(rep, o, "\n" as *u8) 950 } 951 i = i + 1 952 } 953 954 // milestones 955 var mi: i64 = 0 956 let mids: *i64 = st[11] as *i64 957 let mdates: *i64 = st[12] as *i64 958 let mdays: *i64 = st[13] as *i64 959 while mi < st[10] { 960 var duet: i64 = 0 961 var dunet: i64 = 0 962 i = 0 963 while i < n { 964 if duedays[i] <= mdays[mi] { 965 duet = duet + 1 966 if done[i] == 1 { dunet = dunet + 1 } 967 } 968 i = i + 1 969 } 970 var mpct: i64 = 0 971 if duet > 0 { mpct = (dunet * 100) / duet } 972 o = vp_cat(rep, o, "VIZSLA-PLAN-MILE plan=" as *u8) 973 o = vp_cat(rep, o, plan) 974 o = vp_cat(rep, o, " id=" as *u8) 975 o = vp_cat(rep, o, mids[mi] as *u8) 976 o = vp_cat(rep, o, " date=" as *u8) 977 o = vp_cat(rep, o, mdates[mi] as *u8) 978 o = vp_cat(rep, o, " due_tasks=" as *u8) 979 o = vp_catn(rep, o, duet) 980 o = vp_cat(rep, o, " done=" as *u8) 981 o = vp_catn(rep, o, dunet) 982 o = vp_cat(rep, o, " pct=" as *u8) 983 o = vp_catn(rep, o, mpct) 984 o = vp_cat(rep, o, "\n" as *u8) 985 mi = mi + 1 986 } 987 988 // critical path line (chain reversed: chain[clen-1] is the head) 989 o = vp_cat(rep, o, "VIZSLA-PLAN-CRITICAL plan=" as *u8) 990 o = vp_cat(rep, o, plan) 991 o = vp_cat(rep, o, " effort_min=" as *u8) 992 o = vp_catn(rep, o, longest[cpend]) 993 o = vp_cat(rep, o, " path=" as *u8) 994 var ci: i64 = clen - 1 995 var firstc: i64 = 1 996 while ci >= 0 { 997 if firstc == 0 { o = vp_cat(rep, o, ">" as *u8) } 998 o = vp_cat(rep, o, tids[chain[ci]] as *u8) 999 firstc = 0 1000 ci = ci - 1 1001 } 1002 o = vp_cat(rep, o, "\n" as *u8) 1003 1004 let pct: i64 = (ndone * 100) / n 1005 o = vp_cat(rep, o, "VIZSLA-PLAN-VERDICT plan=" as *u8) 1006 o = vp_cat(rep, o, plan) 1007 o = vp_cat(rep, o, " tasks=" as *u8) 1008 o = vp_catn(rep, o, n) 1009 o = vp_cat(rep, o, " done=" as *u8) 1010 o = vp_catn(rep, o, ndone) 1011 o = vp_cat(rep, o, " ready=" as *u8) 1012 o = vp_catn(rep, o, ready) 1013 o = vp_cat(rep, o, " blocked=" as *u8) 1014 o = vp_catn(rep, o, blocked) 1015 o = vp_cat(rep, o, " late=" as *u8) 1016 o = vp_catn(rep, o, late) 1017 o = vp_cat(rep, o, " pct=" as *u8) 1018 o = vp_catn(rep, o, pct) 1019 o = vp_cat(rep, o, " critical_min=" as *u8) 1020 o = vp_catn(rep, o, longest[cpend]) 1021 o = vp_cat(rep, o, "\n" as *u8) 1022 sys_write(1, rep, o) 1023 return 0 1024} 1025 1026// collect busy intervals for calprefix on day qd (cal: + rec:) 1027func vp_busy_day(calpfx: *u8, qd: i64, bs: *i64, be: *i64, np: *i64) -> i64 { 1028 let segs: *i64 = sys_mmap(8 * 260) as *i64 1029 let ns: i64 = ss_manifest(calpfx, segs) 1030 let fdt: *u8 = sys_mmap(128) 1031 let fst: *u8 = sys_mmap(128) 1032 let fdu: *u8 = sys_mmap(128) 1033 let ffr: *u8 = sys_mmap(64) 1034 let fiv: *u8 = sys_mmap(64) 1035 let fct: *u8 = sys_mmap(64) 1036 let ymd: *i64 = sys_mmap(8 * 4) as *i64 1037 var s: i64 = 0 1038 while s < ns { 1039 let path: *u8 = sys_mmap(512) 1040 var po: i64 = 0 1041 po = vp_cat(path, po, calpfx) 1042 po = vp_cat(path, po, segs[s] as *u8) 1043 po = vp_cat(path, po, ".docs" as *u8) 1044 path[po] = 0 as u8 1045 let szp: *i64 = sys_mmap(16) as *i64 1046 let b: *u8 = ss_readall(path, szp) 1047 let sz: i64 = szp[0] 1048 var j: i64 = 0 1049 while j + 9 <= sz { 1050 let kind: i64 = b[j] 1051 let kl: i64 = ss_r32(b, j + 1) 1052 let koff: i64 = j + 5 1053 let vl: i64 = ss_r32(b, koff + kl) 1054 let voff: i64 = koff + kl + 4 1055 let rec: *u8 = (b as i64 + voff) as *u8 1056 var iscal: i64 = 0 1057 var isrec: i64 = 0 1058 if kind == 1 { iscal = vp_iskind(b, koff, kl, "cal:" as *u8) } 1059 if kind == 1 { isrec = vp_iskind(b, koff, kl, "rec:" as *u8) } 1060 if iscal == 1 { 1061 var dd: i64 = 0 - 1 1062 if vp_field(rec, vl, "date" as *u8, fdt, 128) == 1 { dd = vp_datedays(fdt) } 1063 if dd == qd { 1064 vp_field(rec, vl, "start" as *u8, fst, 128) 1065 vp_field(rec, vl, "dur" as *u8, fdu, 128) 1066 if np[0] < 512 { 1067 let sv: i64 = vp_atoi(fst) 1068 let ev: i64 = sv + vp_atoi(fdu) 1069 bs[np[0]] = sv 1070 be[np[0]] = ev 1071 np[0] = np[0] + 1 1072 } 1073 } 1074 } 1075 if isrec == 1 { 1076 vp_field(rec, vl, "sdate" as *u8, fdt, 128) 1077 vp_field(rec, vl, "start" as *u8, fst, 128) 1078 vp_field(rec, vl, "dur" as *u8, fdu, 128) 1079 vp_field(rec, vl, "freq" as *u8, ffr, 64) 1080 vp_field(rec, vl, "interval" as *u8, fiv, 64) 1081 vp_field(rec, vl, "count" as *u8, fct, 64) 1082 let sd0: i64 = vp_datedays(fdt) 1083 vp_civil(sd0, ymd) 1084 let iv: i64 = vp_atoi(fiv) 1085 let ct: i64 = vp_atoi(fct) 1086 var k: i64 = 0 1087 while k < ct { 1088 let occ: i64 = vp_occ(ffr, sd0, ymd[0], ymd[1], ymd[2], iv, k) 1089 if occ == qd { if np[0] < 512 { 1090 let sv: i64 = vp_atoi(fst) 1091 let ev: i64 = sv + vp_atoi(fdu) 1092 bs[np[0]] = sv 1093 be[np[0]] = ev 1094 np[0] = np[0] + 1 1095 } } 1096 if occ > qd { k = ct } 1097 k = k + 1 1098 } 1099 } 1100 j = voff + vl 1101 } 1102 s = s + 1 1103 } 1104 return 0 1105} 1106 1107// ---------------- TIMEBLOCK ---------------- 1108func vp_cmd_timeblock(argc: i64, argv: *i64) -> i64 { 1109 if argc < 6 { vp_p("VIZSLA-PLAN timeblock needs <prefix> <plan> <calprefix> <date> [earliest latest] -- fail loud\n" as *u8); return 1 } 1110 let prefix: *u8 = argv[2] as *u8 1111 let plan: *u8 = argv[3] as *u8 1112 let calpfx: *u8 = argv[4] as *u8 1113 let qd: i64 = vp_datedays(argv[5] as *u8) 1114 if qd < 0 { vp_p("VIZSLA-PLAN timeblock bad date -- fail loud\n" as *u8); return 1 } 1115 var emin: i64 = 0 1116 var emax: i64 = K_MAGIC_1440 1117 if argc > 7 { 1118 if vp_digits(argv[6] as *u8) == 0 { vp_p("VIZSLA-PLAN timeblock earliest not digits -- fail loud\n" as *u8); return 1 } 1119 if vp_digits(argv[7] as *u8) == 0 { vp_p("VIZSLA-PLAN timeblock latest not digits -- fail loud\n" as *u8); return 1 } 1120 emin = vp_atoi(argv[6] as *u8) 1121 emax = vp_atoi(argv[7] as *u8) 1122 } 1123 if emin >= emax { vp_p("VIZSLA-PLAN timeblock earliest >= latest -- fail loud\n" as *u8); return 1 } 1124 if emax > K_MAGIC_1440 { vp_p("VIZSLA-PLAN timeblock latest > 1440 -- fail loud\n" as *u8); return 1 } 1125 1126 let st: *i64 = vp_alloc_state() 1127 vp_gather(prefix, plan, st) 1128 let n: i64 = st[0] 1129 if n == 0 { vp_p("VIZSLA-PLAN timeblock plan has NO tasks -- fail loud\n" as *u8); return 1 } 1130 let tids: *i64 = st[1] as *i64 1131 let duedays: *i64 = st[4] as *i64 1132 let efforts: *i64 = st[5] as *i64 1133 let imps: *i64 = st[6] as *i64 1134 let depstrs: *i64 = st[7] as *i64 1135 let done: *i64 = st[9] as *i64 1136 1137 // READY not-done tasks (all deps done); unknown dep -> loud 1138 let dmat: *i64 = sys_mmap(8 * 128 * 9) as *i64 1139 let dcnt: *i64 = sys_mmap(8 * 128) as *i64 1140 var i: i64 = 0 1141 while i < n { 1142 let row: *i64 = (dmat as i64 + i * 9 * 8) as *i64 1143 let c: i64 = vp_depidx(depstrs[i] as *u8, tids, n, row) 1144 if c < 0 { vp_p("VIZSLA-PLAN-BAD-DEP -- fail loud\n" as *u8); return 1 } 1145 dcnt[i] = c 1146 i = i + 1 1147 } 1148 let cand: *i64 = sys_mmap(8 * 128) as *i64 1149 var nc: i64 = 0 1150 i = 0 1151 while i < n { 1152 if done[i] == 0 { 1153 var isready: i64 = 1 1154 let row: *i64 = (dmat as i64 + i * 9 * 8) as *i64 1155 var k: i64 = 0 1156 while k < dcnt[i] { 1157 if done[row[k]] == 0 { isready = 0 } 1158 k = k + 1 1159 } 1160 if isready == 1 { cand[nc] = i; nc = nc + 1 } 1161 } 1162 i = i + 1 1163 } 1164 // priority order (score desc / due asc / tid lex), today := the block date 1165 let scores: *i64 = sys_mmap(8 * 128) as *i64 1166 i = 0 1167 while i < n { 1168 let u: i64 = vp_urgency(duedays[i], qd, st[14], st[15], st[16]) 1169 scores[i] = u * 10 + imps[i] * 2 1170 i = i + 1 1171 } 1172 var a: i64 = 0 1173 while a < nc { 1174 var mn: i64 = a 1175 var c: i64 = a + 1 1176 while c < nc { 1177 var better: i64 = 0 1178 let ic: i64 = cand[c] 1179 let im: i64 = cand[mn] 1180 if scores[ic] > scores[im] { better = 1 } 1181 if scores[ic] == scores[im] { 1182 if duedays[ic] < duedays[im] { better = 1 } 1183 if duedays[ic] == duedays[im] { 1184 if vp_cmp(tids[ic] as *u8, tids[im] as *u8) < 0 { better = 1 } 1185 } 1186 } 1187 if better == 1 { mn = c } 1188 c = c + 1 1189 } 1190 if mn != a { let t1: i64 = cand[a]; cand[a] = cand[mn]; cand[mn] = t1 } 1191 a = a + 1 1192 } 1193 1194 // free windows of the calendar day within [emin,emax) 1195 let bs: *i64 = sys_mmap(8 * 512) as *i64 1196 let be: *i64 = sys_mmap(8 * 512) as *i64 1197 let np: *i64 = sys_mmap(16) as *i64 1198 np[0] = 0 1199 vp_busy_day(calpfx, qd, bs, be, np) 1200 let nb: i64 = np[0] 1201 a = 0 1202 while a < nb { 1203 var mn: i64 = a 1204 var c: i64 = a + 1 1205 while c < nb { 1206 if bs[c] < bs[mn] { mn = c } 1207 c = c + 1 1208 } 1209 if mn != a { 1210 let t1: i64 = bs[a]; bs[a] = bs[mn]; bs[mn] = t1 1211 let t2: i64 = be[a]; be[a] = be[mn]; be[mn] = t2 1212 } 1213 a = a + 1 1214 } 1215 let ws: *i64 = sys_mmap(8 * 64) as *i64 1216 let we: *i64 = sys_mmap(8 * 64) as *i64 1217 var nw: i64 = 0 1218 var prev: i64 = emin 1219 var bi: i64 = 0 1220 while bi < nb { 1221 var s0: i64 = prev 1222 var e0: i64 = bs[bi] 1223 if e0 > emax { e0 = emax } 1224 if e0 > s0 { if nw < 64 { ws[nw] = s0; we[nw] = e0; nw = nw + 1 } } 1225 if be[bi] > prev { prev = be[bi] } 1226 if prev >= emax { bi = nb } 1227 bi = bi + 1 1228 } 1229 if prev < emax { if nw < 64 { ws[nw] = prev; we[nw] = emax; nw = nw + 1 } } 1230 var free_min: i64 = 0 1231 i = 0 1232 while i < nw { free_min = free_min + (we[i] - ws[i]); i = i + 1 } 1233 1234 // first-fit placement in priority order 1235 let cursor: *i64 = sys_mmap(8 * 64) as *i64 1236 i = 0 1237 while i < nw { cursor[i] = ws[i]; i = i + 1 } 1238 let rep: *u8 = sys_mmap(K_MAGIC_65536) 1239 var o: i64 = 0 1240 var placed: i64 = 0 1241 var unplaced: i64 = 0 1242 var used: i64 = 0 1243 var ci: i64 = 0 1244 while ci < nc { 1245 let t: i64 = cand[ci] 1246 var put: i64 = 0 1247 var wi: i64 = 0 1248 while wi < nw { 1249 if put == 0 { 1250 let rem: i64 = we[wi] - cursor[wi] 1251 if efforts[t] <= rem { 1252 o = vp_cat(rep, o, "VIZSLA-PLAN-BLOCK plan=" as *u8) 1253 o = vp_cat(rep, o, plan) 1254 o = vp_cat(rep, o, " date=" as *u8) 1255 o = vp_cat(rep, o, argv[5] as *u8) 1256 o = vp_cat(rep, o, " start=" as *u8) 1257 o = vp_hhmm(rep, o, cursor[wi]) 1258 o = vp_cat(rep, o, " end=" as *u8) 1259 o = vp_hhmm(rep, o, cursor[wi] + efforts[t]) 1260 o = vp_cat(rep, o, " task=" as *u8) 1261 o = vp_cat(rep, o, tids[t] as *u8) 1262 o = vp_cat(rep, o, "\n" as *u8) 1263 cursor[wi] = cursor[wi] + efforts[t] 1264 used = used + efforts[t] 1265 placed = placed + 1 1266 put = 1 1267 } 1268 } 1269 wi = wi + 1 1270 } 1271 if put == 0 { 1272 o = vp_cat(rep, o, "VIZSLA-PLAN-UNPLACED plan=" as *u8) 1273 o = vp_cat(rep, o, plan) 1274 o = vp_cat(rep, o, " task=" as *u8) 1275 o = vp_cat(rep, o, tids[t] as *u8) 1276 o = vp_cat(rep, o, " effort_min=" as *u8) 1277 o = vp_catn(rep, o, efforts[t]) 1278 o = vp_cat(rep, o, " reason=no-free-window-fits\n" as *u8) 1279 unplaced = unplaced + 1 1280 } 1281 ci = ci + 1 1282 } 1283 1284 o = vp_cat(rep, o, "VIZSLA-PLAN-TIMEBLOCK plan=" as *u8) 1285 o = vp_cat(rep, o, plan) 1286 o = vp_cat(rep, o, " date=" as *u8) 1287 o = vp_cat(rep, o, argv[5] as *u8) 1288 o = vp_cat(rep, o, " placed=" as *u8) 1289 o = vp_catn(rep, o, placed) 1290 o = vp_cat(rep, o, " unplaced=" as *u8) 1291 o = vp_catn(rep, o, unplaced) 1292 o = vp_cat(rep, o, " used_min=" as *u8) 1293 o = vp_catn(rep, o, used) 1294 o = vp_cat(rep, o, " free_min=" as *u8) 1295 o = vp_catn(rep, o, free_min) 1296 o = vp_cat(rep, o, "\n" as *u8) 1297 sys_write(1, rep, o) 1298 return 0 1299} 1300 1301func main(argc: i64, argv: *i64) -> i64 { 1302 if argc < 2 { vp_p("VIZSLA-PLAN usage: load|board|timeblock -- fail loud\n" as *u8); return 1 } 1303 let cmd: *u8 = argv[1] as *u8 1304 if vp_eq(cmd, "load" as *u8) == 1 { return vp_cmd_load(argc, argv) } 1305 if vp_eq(cmd, "board" as *u8) == 1 { return vp_cmd_board(argc, argv) } 1306 if vp_eq(cmd, "timeblock" as *u8) == 1 { return vp_cmd_timeblock(argc, argv) } 1307 vp_p("VIZSLA-PLAN unknown command (load|board|timeblock) -- fail loud\n" as *u8) 1308 return 1 1309}