@plot-pm/board 0.11.0 → 0.13.0
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 +101 -101
- package/package.json +5 -1
- package/plot-agent-monitor.sh +37 -13
- package/plot-approve.sh +65 -30
- package/plot-build-monitor.sh +435 -0
- package/plot-config.sh +8 -1
- package/plot-default-branch.sh +109 -0
- package/plot-deliver.sh +47 -27
- package/plot-dispatch.sh +546 -10
- package/plot-fleet-scan.sh +498 -175
- package/plot-host.sh +255 -26
- package/plot-monitor-subject.sh +194 -0
- package/plot-plan-meta.sh +111 -7
- package/plot-pr-merged.sh +180 -0
- package/plot-reap.sh +45 -5
- package/plot-release-refs.sh +188 -46
- package/plot-resolve-artifact.sh +115 -22
- package/plot-worker-state.sh +94 -0
package/plot-deliver.sh
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
#
|
|
36
36
|
# Each step asks THE SOURCE IT WOULD HAVE WRITTEN whether it is already done:
|
|
37
37
|
# the plan file for the phase and the record, the index directories for the
|
|
38
|
-
# symlink, the sprint file for the
|
|
38
|
+
# symlink, the sprint file for the tick. Never a progress file of its own.
|
|
39
39
|
#
|
|
40
40
|
# WHAT IT REFUSES, and why refusing beats guessing:
|
|
41
41
|
# - phase is not `approved` — nothing to deliver. (Already-Delivered is NOT a
|
|
@@ -66,7 +66,7 @@ done
|
|
|
66
66
|
die() { echo "plot-deliver: $*" >&2; exit 1; }
|
|
67
67
|
|
|
68
68
|
[ -n "$slug" ] || die "need a plan slug (usage: plot-deliver.sh [--dry-run] <slug>)"
|
|
69
|
-
git rev-parse --git-dir >/dev/null 2>&1 || die "not a git repository"
|
|
69
|
+
git rev-parse --git-dir >/dev/null 2>&1 || die "not a git repository — run this from inside the checkout, or 'git init' one here"
|
|
70
70
|
|
|
71
71
|
cfg() { bash "$script_dir/plot-config.sh" get "$1" "$2"; }
|
|
72
72
|
|
|
@@ -86,10 +86,13 @@ plan_file=""
|
|
|
86
86
|
for cand in "$PLAN_DIR"*"$slug".md "$ACTIVE_DIR$slug.md" "$DELIVERED_DIR$slug.md"; do
|
|
87
87
|
[ -e "$cand" ] && { plan_file="$cand"; break; }
|
|
88
88
|
done
|
|
89
|
-
[ -n "$plan_file" ] || die "no plan found for '$slug' — looked in $PLAN_DIR, $ACTIVE_DIR, $DELIVERED_DIR
|
|
89
|
+
[ -n "$plan_file" ] || die "no plan found for '$slug' — looked in $PLAN_DIR, $ACTIVE_DIR, $DELIVERED_DIR.
|
|
90
|
+
Check the slug: ls $PLAN_DIR | grep -i '$slug'"
|
|
90
91
|
|
|
91
92
|
meta=$(bash "$script_dir/plot-plan-meta.sh" "$plan_file" 2>/dev/null) || meta=""
|
|
92
|
-
[ -n "$meta" ] || die "cannot parse '$plan_file' — refusing rather than guessing
|
|
93
|
+
[ -n "$meta" ] || die "cannot parse '$plan_file' — refusing rather than guessing.
|
|
94
|
+
See what the parser reads: $script_dir/plot-plan-meta.sh $plan_file
|
|
95
|
+
A plan needs a '## Status' section with a 'State:' field."
|
|
93
96
|
|
|
94
97
|
jfield() { printf '%s' "$meta" | jq -r "$1" 2>/dev/null; }
|
|
95
98
|
|
|
@@ -106,13 +109,16 @@ delivered_raw=$(jfield '.delivered_raw')
|
|
|
106
109
|
case "$phase" in
|
|
107
110
|
approved|delivered) ;;
|
|
108
111
|
released)
|
|
109
|
-
die "plan '$slug' is already released — nothing to deliver.
|
|
112
|
+
die "plan '$slug' is already released — nothing to deliver.
|
|
113
|
+
Nothing to do here; the work shipped." ;;
|
|
110
114
|
draft|design)
|
|
111
|
-
die "plan '$slug' is still '$phase' — approve it first
|
|
115
|
+
die "plan '$slug' is still '$phase' — approve it first: /plot-approve $slug" ;;
|
|
112
116
|
NONE|"")
|
|
113
|
-
die "cannot read the phase of '$slug' ($plan_file) — refusing rather than guessing.
|
|
117
|
+
die "cannot read the phase of '$slug' ($plan_file) — refusing rather than guessing.
|
|
118
|
+
Its '## Status' section needs a line reading '- **State:** Approved'." ;;
|
|
114
119
|
*)
|
|
115
|
-
die "plan '$slug' is in phase '$phase' — only an Approved plan can be delivered.
|
|
120
|
+
die "plan '$slug' is in phase '$phase' — only an Approved plan can be delivered.
|
|
121
|
+
If that phase is wrong, correct the 'State:' line in $plan_file and push it." ;;
|
|
116
122
|
esac
|
|
117
123
|
|
|
118
124
|
# ---------------------------------------------------------------------------
|
|
@@ -157,7 +163,8 @@ fi
|
|
|
157
163
|
|
|
158
164
|
verdict=$(PLOT_REPO_ROOT="$repo_root" PLOT_SCRIPTS_DIR="$script_dir" \
|
|
159
165
|
node "$ask_mjs" deliverable "$slug" "$plan_file" 2>/dev/null) || verdict=""
|
|
160
|
-
[ -n "$verdict" ] || die "cannot determine deliverability of '$slug' — refusing rather than guessing.
|
|
166
|
+
[ -n "$verdict" ] || die "cannot determine deliverability of '$slug' — refusing rather than guessing.
|
|
167
|
+
See what it said: PLOT_REPO_ROOT=$repo_root PLOT_SCRIPTS_DIR=$script_dir node $ask_mjs deliverable $slug $plan_file"
|
|
161
168
|
|
|
162
169
|
vfield() { printf '%s' "$verdict" | jq -r "$1" 2>/dev/null; }
|
|
163
170
|
|
|
@@ -187,7 +194,7 @@ today=$(date +%Y-%m-%d)
|
|
|
187
194
|
if [ "$dry_run" = 1 ]; then
|
|
188
195
|
echo "step: would flip Phase → Delivered and fill Delivered: $today"
|
|
189
196
|
echo "step: would move active/ → delivered/ symlink"
|
|
190
|
-
echo "step: would
|
|
197
|
+
echo "step: would tick the sprint item${sprint:+ (sprint: $sprint)}"
|
|
191
198
|
echo "summary: phase=would record=would index=would sprint=would push=would"
|
|
192
199
|
exit 0
|
|
193
200
|
fi
|
|
@@ -221,12 +228,15 @@ real_plan_path() { # $1 = plan file as found
|
|
|
221
228
|
}
|
|
222
229
|
|
|
223
230
|
rel=$(cd "$repo_root" && real_plan_path "$plan_file") || rel=""
|
|
224
|
-
[ -n "$rel" ] || die "$plan_file is outside the repository root
|
|
231
|
+
[ -n "$rel" ] || die "$plan_file is outside the repository root ($repo_root).
|
|
232
|
+
Move the plan under $PLAN_DIR inside this checkout and re-run."
|
|
225
233
|
|
|
226
234
|
# The filename, for symlink creation.
|
|
227
235
|
plan_basename=$(basename "$rel")
|
|
228
236
|
|
|
229
|
-
# Flip `**
|
|
237
|
+
# Flip `**State:** Approved` → `Delivered` in the `## Status` section only.
|
|
238
|
+
# Reads `State:` and `Phase:` alike: it changes the VALUE on whichever line
|
|
239
|
+
# carries it, so a plan written before the 2026-09-07 rename still delivers.
|
|
230
240
|
#
|
|
231
241
|
# READS ONE FILE AND WRITES ANOTHER, rather than editing in place. It edited in
|
|
232
242
|
# place until 2026-09-02, which is what let the phase land without its record —
|
|
@@ -235,7 +245,7 @@ flip_phase() { # $1=in $2=out → 0 if it changed the file, 1 if nothing to fli
|
|
|
235
245
|
awk '
|
|
236
246
|
BEGIN { section = ""; done = 0 }
|
|
237
247
|
/^## / { section = ($0 ~ /^## Status/) ? "status" : ""; print; next }
|
|
238
|
-
section == "status" && !done && tolower($0) ~ /^[ \t]*[-*]?[ \t]*\**phase[:*]/ {
|
|
248
|
+
section == "status" && !done && tolower($0) ~ /^[ \t]*[-*]?[ \t]*\**(state|phase)[:*]/ {
|
|
239
249
|
if (tolower($0) ~ /approved/) {
|
|
240
250
|
sub(/[Aa]pproved/, "Delivered")
|
|
241
251
|
done = 1
|
|
@@ -410,8 +420,20 @@ write_transition() { # $1=file $2=record $3=recorded(yes|no) → sets phase_repo
|
|
|
410
420
|
return 0
|
|
411
421
|
}
|
|
412
422
|
|
|
413
|
-
#
|
|
414
|
-
|
|
423
|
+
# Tick this plan's sprint item.
|
|
424
|
+
#
|
|
425
|
+
# THE TICK ONLY, since 2026-09-08. This also wrote a `<!-- status: delivered -->`
|
|
426
|
+
# annotation, and `a-withdrawn-item-is-not-open` measured that record dead in
|
|
427
|
+
# both directions: 67 lines carried one, none carried a value any reader acted
|
|
428
|
+
# on, and `plot-sprint-release.sh` read the field nowhere. The plan file carries
|
|
429
|
+
# `State:` and a dated `Delivered:` record, and the estate-outranks-the-checkbox
|
|
430
|
+
# rule points at those — a cache nobody refreshes and nobody reads is a second
|
|
431
|
+
# answer waiting to contradict the first.
|
|
432
|
+
#
|
|
433
|
+
# The box is NOT the same record. `/plot-sprint close` step 2a exists to tick
|
|
434
|
+
# boxes the estate says are done, and ticking here is what keeps that step's
|
|
435
|
+
# work to the items it genuinely cannot resolve.
|
|
436
|
+
update_sprint_item() { # $1=worktree root → prints none|updated|already|missing
|
|
415
437
|
local root="$1" f found=""
|
|
416
438
|
[ -n "$sprint" ] || { printf 'none'; return 0; }
|
|
417
439
|
local dir="$root/${SPRINT_DIR#/}"
|
|
@@ -429,15 +451,8 @@ update_sprint_annotation() { # $1=worktree root → prints none|updated|already|
|
|
|
429
451
|
index($0, "[" slug "]") == 0 { print; next }
|
|
430
452
|
{
|
|
431
453
|
line = $0
|
|
432
|
-
# Check the box
|
|
454
|
+
# Check the box. Nothing else on the line is touched.
|
|
433
455
|
sub(/\[ \]/, "[x]", line)
|
|
434
|
-
# Update or add status annotation
|
|
435
|
-
if (index(line, "<!--") == 0) {
|
|
436
|
-
line = line " <!-- status: delivered -->"
|
|
437
|
-
} else {
|
|
438
|
-
if (line ~ /status:[ \t]*[a-z-]+/) sub(/status:[ \t]*[a-z-]+/, "status: delivered", line)
|
|
439
|
-
else sub(/-->/, ", status: delivered -->", line)
|
|
440
|
-
}
|
|
441
456
|
print line
|
|
442
457
|
}
|
|
443
458
|
' "$found")
|
|
@@ -500,8 +515,8 @@ apply_local_writes() { # $1=root → sets phase_report record_report index_repo
|
|
|
500
515
|
# Step 5 — move the index symlink (best effort).
|
|
501
516
|
index_report=$(move_index_symlink "$root")
|
|
502
517
|
|
|
503
|
-
# Step 6 —
|
|
504
|
-
sprint_report=$(
|
|
518
|
+
# Step 6 — tick the sprint item.
|
|
519
|
+
sprint_report=$(update_sprint_item "$root")
|
|
505
520
|
return 0
|
|
506
521
|
}
|
|
507
522
|
|
|
@@ -515,7 +530,10 @@ bookbr="plot/deliver-$slug"
|
|
|
515
530
|
tmpwt="$wt_root/.plot-deliver-$slug.$$"
|
|
516
531
|
# -B: a leftover branch from an earlier failed run must not block this one.
|
|
517
532
|
git worktree add -q -B "$bookbr" "$tmpwt" "origin/$MAIN" 2>/dev/null \
|
|
518
|
-
|| die "could not prepare a booking worktree at $tmpwt
|
|
533
|
+
|| die "could not prepare a booking worktree at $tmpwt.
|
|
534
|
+
Most often origin/$MAIN is not fetched, or '$bookbr' is checked out in
|
|
535
|
+
another worktree. Check both: git fetch origin $MAIN && git worktree list
|
|
536
|
+
Nothing has been written locally; the plan is untouched."
|
|
519
537
|
|
|
520
538
|
cleanup() {
|
|
521
539
|
git worktree remove --force "$tmpwt" >/dev/null 2>&1 || true
|
|
@@ -542,7 +560,9 @@ if git -C "$tmpwt" diff --cached --quiet 2>/dev/null; then
|
|
|
542
560
|
else
|
|
543
561
|
if ! git -C "$tmpwt" -c "user.name=$who" commit -q -m "plot: deliver $slug"; then
|
|
544
562
|
cleanup
|
|
545
|
-
die "could not commit the delivery
|
|
563
|
+
die "could not commit the delivery.
|
|
564
|
+
See what git refused: git -C $tmpwt status
|
|
565
|
+
Nothing was pushed; re-run this — it is idempotent."
|
|
546
566
|
fi
|
|
547
567
|
|
|
548
568
|
push_out=$(bash "$script_dir/plot-push-main.sh" "$bookbr" "$MAIN" 2>&1)
|