@plot-pm/board 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/board-server.mjs +144 -142
- package/package.json +1 -1
- package/plot-budget.sh +166 -1
- package/plot-deliver.sh +14 -8
- package/plot-dispatch.sh +54 -46
- package/plot-fleet-scan.sh +134 -7
- package/plot-host.sh +728 -38
- package/plot-plan-meta.sh +23 -7
- package/plot-reap.sh +37 -10
package/plot-plan-meta.sh
CHANGED
|
@@ -440,19 +440,35 @@ function reset_state() {
|
|
|
440
440
|
delete changelog; n_changelog = 0; changelog_seen = 0; cl_open = 0
|
|
441
441
|
}
|
|
442
442
|
function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assignee, review, impl, design, approved, delivered, issue, issue2, i, j, v, is_dup, out, sorted_b, sorted_p, sorted_i, nb, np, ni, num_issues, str_issues, n_num_i, n_str_i, issue_is_str) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
443
|
+
# THE PHASE IS READ FROM THE FIELD PLOT WRITES. A canonical `State:`/`Phase:`
|
|
444
|
+
# outranks front matter, because every lifecycle script writes the canonical
|
|
445
|
+
# body and none writes front matter: `plot-approve.sh` holds zero front-matter
|
|
446
|
+
# references, and the five in `plot-deliver.sh` all REFUSE the case rather
|
|
447
|
+
# than write it. A plan carrying both therefore reported a value no transition
|
|
448
|
+
# had ever touched - measured 2026-09-17, an approval wrote `State: Approved`,
|
|
449
|
+
# the parser answered `draft`, and the scan dispatched nothing.
|
|
450
|
+
#
|
|
451
|
+
# `phase_alt` carries the loser, so the disagreement stays readable - that is
|
|
452
|
+
# the field the delivery gate in `plot-deliver.sh` reads.
|
|
453
|
+
#
|
|
454
|
+
# THE PRECEDENCE MOVES FOR THE FIELDS PLOT OWNS, NOT FOR EVERY FIELD. The
|
|
455
|
+
# board-facing fields below, `design_raw` among them, keep front-matter-wins:
|
|
456
|
+
# no lifecycle script writes any of them, so neither record is more current.
|
|
457
|
+
if (canon_state != "" || canon_phase != "") {
|
|
449
458
|
# `State:` is primary and `Phase:` the alternate, exactly as front matter
|
|
450
459
|
# reads `status:` over `phase:`. A file carrying both reports the
|
|
451
|
-
# disagreement rather than hiding it
|
|
460
|
+
# disagreement rather than hiding it, and front matter joins that alternate
|
|
461
|
+
# when the canonical body answered first.
|
|
452
462
|
fmt = "canonical"
|
|
453
463
|
praw = (canon_state != "") ? canon_state : canon_phase
|
|
454
464
|
palt_raw = (canon_state != "" && canon_phase != "") ? canon_phase : ""
|
|
465
|
+
if (palt_raw == "") palt_raw = (fm_status != "") ? fm_status : fm_phase
|
|
455
466
|
traw = canon_type
|
|
467
|
+
} else if (fm_status != "" || fm_phase != "") {
|
|
468
|
+
fmt = "frontmatter"
|
|
469
|
+
praw = (fm_status != "") ? fm_status : fm_phase
|
|
470
|
+
palt_raw = (fm_status != "" && fm_phase != "") ? fm_phase : ""
|
|
471
|
+
traw = fm_type
|
|
456
472
|
} else {
|
|
457
473
|
fmt = "none"; praw = ""; palt_raw = ""; traw = ""
|
|
458
474
|
}
|
package/plot-reap.sh
CHANGED
|
@@ -385,11 +385,19 @@ manifest_for() {
|
|
|
385
385
|
reap=0; kept=0; removed=0; cleared=0; vanished=0; unplaced=0
|
|
386
386
|
printf '%-8s %-52s %s\n' "verdict" "branch" "why"
|
|
387
387
|
|
|
388
|
-
while IFS=$'\
|
|
388
|
+
while IFS=$'\037' read -r wt br prunable; do
|
|
389
389
|
[ -n "$wt" ] || continue
|
|
390
390
|
short=${br#refs/heads/}
|
|
391
391
|
[ "$wt" = "$ROOT" ] && continue
|
|
392
392
|
|
|
393
|
+
# WHAT THE REPORT CALLS THIS TREE. `short` is the branch and stays empty for
|
|
394
|
+
# a detached desk, which is the correct reading and a useless label — an
|
|
395
|
+
# operator reading a blank column cannot tell which of thirteen it names.
|
|
396
|
+
# The directory is what identifies such a tree, so the report says so and
|
|
397
|
+
# every refusal still measures `short`.
|
|
398
|
+
label=$short
|
|
399
|
+
[ -z "$label" ] && label="(detached) $(basename "$wt")"
|
|
400
|
+
|
|
393
401
|
# 4a. GIT'S OWN ANSWER THAT THE DIRECTORY IS GONE, and it is a REPORT rather
|
|
394
402
|
# than a sixth refusal. The five below each say *do not remove this* and
|
|
395
403
|
# send an operator to look; this says *there is nothing to remove and the
|
|
@@ -414,7 +422,7 @@ while IFS=$'\t' read -r wt br prunable; do
|
|
|
414
422
|
# prune stays the operator's decision — the same discipline that makes
|
|
415
423
|
# every refusal a measurement rather than an act.
|
|
416
424
|
if [ "$prunable" = "yes" ]; then
|
|
417
|
-
printf '%-8s %-52s %s\n' "vanished" "$
|
|
425
|
+
printf '%-8s %-52s %s\n' "vanished" "$label" "directory gone — 'git worktree prune' clears the entry"
|
|
418
426
|
vanished=$((vanished+1)); continue
|
|
419
427
|
fi
|
|
420
428
|
|
|
@@ -482,7 +490,7 @@ while IFS=$'\t' read -r wt br prunable; do
|
|
|
482
490
|
# population the rule excludes. It counts, because a refusal that counts is
|
|
483
491
|
# the difference between *nothing to clean* and *nothing was looked at*.
|
|
484
492
|
if [ "$unclassified" = true ]; then
|
|
485
|
-
printf '%-8s %-52s %s\n' "unknown" "$
|
|
493
|
+
printf '%-8s %-52s %s\n' "unknown" "$label" \
|
|
486
494
|
"under $(basename "$WT_ROOT")/, no worker pid and no recognised name — needs a person"
|
|
487
495
|
unplaced=$((unplaced+1)); continue
|
|
488
496
|
fi
|
|
@@ -531,6 +539,25 @@ while IFS=$'\t' read -r wt br prunable; do
|
|
|
531
539
|
merge=merged; why="merged into $DEFAULT"
|
|
532
540
|
elif [ -n "$short" ] && pr_merged "$short"; then
|
|
533
541
|
merge=merged; why="PR merged (squash)"
|
|
542
|
+
elif [ -z "$short" ] \
|
|
543
|
+
&& [ "$(git -C "$wt" rev-list --count "origin/$DEFAULT..HEAD" 2>/dev/null || echo 1)" = "0" ]; then
|
|
544
|
+
# A DETACHED DESK HAS NOTHING TO LAND, and that is a measurement rather
|
|
545
|
+
# than a weakened refusal. `plot-dispatch.sh --start` cuts a free agent's
|
|
546
|
+
# desk detached at `origin/<main>` because a free agent holds no slice, so
|
|
547
|
+
# such a tree never had a branch and can never have a PR. Reading it
|
|
548
|
+
# through `no-merged-pr` would refuse every one of them forever — the same
|
|
549
|
+
# silence this slice removes, in the opposite direction.
|
|
550
|
+
#
|
|
551
|
+
# THE READING IS `HEAD` AGAINST THE DEFAULT BRANCH, never the desk's name.
|
|
552
|
+
# Measured 2026-09-22: three `free-*` desks hold a branch and two carry
|
|
553
|
+
# live workers (pids 27820, 6542), so a prefix-keyed test reaps a running
|
|
554
|
+
# agent. Detachment is the condition; the name is not.
|
|
555
|
+
#
|
|
556
|
+
# A detached desk carrying commits falls through to `not-merged` and is
|
|
557
|
+
# kept, which is correct: somebody committed there and nothing says the
|
|
558
|
+
# work landed. The live-pid, marker and dirty refusals are all asked
|
|
559
|
+
# before this and are unaffected.
|
|
560
|
+
merge=merged; why="detached, nothing to land"
|
|
534
561
|
fi
|
|
535
562
|
|
|
536
563
|
# THE DECISION. One call, and the script holds no `if` about whether a
|
|
@@ -599,11 +626,11 @@ NODE_EOF
|
|
|
599
626
|
no-merged-pr) reason="unlanded work — no merged PR" ;;
|
|
600
627
|
*) reason="rule could not be asked — keeping" ;;
|
|
601
628
|
esac
|
|
602
|
-
printf '%-8s %-52s %s\n' "keep" "$
|
|
629
|
+
printf '%-8s %-52s %s\n' "keep" "$label" "$reason"; kept=$((kept+1)); continue
|
|
603
630
|
fi
|
|
604
631
|
|
|
605
632
|
if [ "$MAX" -gt 0 ] && [ "$reap" -ge "$MAX" ]; then
|
|
606
|
-
printf '%-8s %-52s %s\n' "keep" "$
|
|
633
|
+
printf '%-8s %-52s %s\n' "keep" "$label" "--max $MAX reached"; kept=$((kept+1)); continue
|
|
607
634
|
fi
|
|
608
635
|
|
|
609
636
|
# Resolved BEFORE the removal, because `canonical` needs the directory to
|
|
@@ -619,7 +646,7 @@ NODE_EOF
|
|
|
619
646
|
|
|
620
647
|
reap=$((reap+1))
|
|
621
648
|
if [ "$DRY" -eq 1 ]; then
|
|
622
|
-
printf '%-8s %-52s %s\n' "would" "$
|
|
649
|
+
printf '%-8s %-52s %s\n' "would" "$label" "$why${logs:+, log $logs}"
|
|
623
650
|
else
|
|
624
651
|
if git worktree remove --force "$wt" 2>/dev/null; then
|
|
625
652
|
# The worktree is gone; NOW the manifest may go. Inside the success arm
|
|
@@ -647,16 +674,16 @@ NODE_EOF
|
|
|
647
674
|
while IFS= read -r f; do rm -f "$f" 2>/dev/null; done < <(branch_log_files "$short")
|
|
648
675
|
why="$why, log removed"
|
|
649
676
|
fi
|
|
650
|
-
printf '%-8s %-52s %s\n' "reaped" "$
|
|
677
|
+
printf '%-8s %-52s %s\n' "reaped" "$label" "$why"; removed=$((removed+1))
|
|
651
678
|
else
|
|
652
|
-
printf '%-8s %-52s %s\n' "FAILED" "$
|
|
679
|
+
printf '%-8s %-52s %s\n' "FAILED" "$label" "git worktree remove refused"; kept=$((kept+1))
|
|
653
680
|
fi
|
|
654
681
|
fi
|
|
655
682
|
done < <(git worktree list --porcelain \
|
|
656
|
-
| awk '/^worktree /{ if (
|
|
683
|
+
| awk -v OFS="\037" '/^worktree /{ if (p != "") print p, br, pr; p=$2; br=""; pr="no"; next }
|
|
657
684
|
/^branch / { br=$2; next }
|
|
658
685
|
/^prunable/ { pr="yes"; next }
|
|
659
|
-
END { if (
|
|
686
|
+
END { if (p != "") print p, br, pr }')
|
|
660
687
|
|
|
661
688
|
[ "$DRY" -eq 0 ] && git worktree prune 2>/dev/null
|
|
662
689
|
|