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}