@plot-pm/board 0.14.1 → 0.14.3
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 +128 -124
- package/package.json +1 -1
- package/plot-deliver.sh +72 -0
- package/plot-dispatch.sh +221 -46
- package/plot-fleet-scan.sh +134 -7
- package/plot-host.sh +687 -34
- package/plot-plan-meta.sh +98 -7
package/plot-plan-meta.sh
CHANGED
|
@@ -150,6 +150,16 @@
|
|
|
150
150
|
# BECAUSE annotations already work in both slice dialects from
|
|
151
151
|
# one block of code — a field line would need two spellings,
|
|
152
152
|
# and the template writes the list dialect.
|
|
153
|
+
# `<!-- agent: reviewer -->` names which KIND of agent this
|
|
154
|
+
# slice needs, reported as `waves[].branches[].agent` and read
|
|
155
|
+
# by plot-dispatch.sh where `--agent` is absent. OPTIONAL, and
|
|
156
|
+
# the key is ABSENT where none was written, never "" — dispatch
|
|
157
|
+
# reads presence, so a blank would send it looking for a charter
|
|
158
|
+
# called "". The value runs to the closing marker, as `builds:`
|
|
159
|
+
# does. It CANNOT validate its value the way `waits:` does: a
|
|
160
|
+
# charter name is a bare word with no structure to check, so a
|
|
161
|
+
# template documents the marker inside an outer comment block,
|
|
162
|
+
# where this pattern cannot see it.
|
|
153
163
|
# prs PR numbers, sorted and unique, read from EITHER spelling:
|
|
154
164
|
# `→ #NNN` / `→ owner/repo#NNN` links in the `## Branches`
|
|
155
165
|
# section, OR `PR: #NNN` in a `## Waves` `### ` heading. The
|
|
@@ -424,24 +434,41 @@ function reset_state() {
|
|
|
424
434
|
delete deferred_of; delete deferred_why; delete claimed_of; delete ordered_b; n_waves = 0
|
|
425
435
|
delete waits_of; delete waits_set
|
|
426
436
|
delete builds_of; delete builds_set
|
|
437
|
+
delete agent_of; delete agent_set
|
|
427
438
|
delete started; n_started = 0
|
|
428
439
|
fm_changelog = ""
|
|
429
440
|
delete changelog; n_changelog = 0; changelog_seen = 0; cl_open = 0
|
|
430
441
|
}
|
|
431
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) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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 != "") {
|
|
438
458
|
# `State:` is primary and `Phase:` the alternate, exactly as front matter
|
|
439
459
|
# reads `status:` over `phase:`. A file carrying both reports the
|
|
440
|
-
# disagreement rather than hiding it
|
|
460
|
+
# disagreement rather than hiding it, and front matter joins that alternate
|
|
461
|
+
# when the canonical body answered first.
|
|
441
462
|
fmt = "canonical"
|
|
442
463
|
praw = (canon_state != "") ? canon_state : canon_phase
|
|
443
464
|
palt_raw = (canon_state != "" && canon_phase != "") ? canon_phase : ""
|
|
465
|
+
if (palt_raw == "") palt_raw = (fm_status != "") ? fm_status : fm_phase
|
|
444
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
|
|
445
472
|
} else {
|
|
446
473
|
fmt = "none"; praw = ""; palt_raw = ""; traw = ""
|
|
447
474
|
}
|
|
@@ -584,6 +611,13 @@ function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assigne
|
|
|
584
611
|
# names no deliverable emits no key, so a consumer reads a name or
|
|
585
612
|
# nothing. An empty string would read as a deliverable called "".
|
|
586
613
|
if (builds_set[i] == 1) out = out ",\"builds\":\"" jesc(builds_of[i]) "\""
|
|
614
|
+
# ABSENT, NOT EMPTY, the rule the three annotations above already keep.
|
|
615
|
+
# Dispatch reads the PRESENCE of this key to decide whether the plan names
|
|
616
|
+
# a kind at all, so an `"agent":""` would read as a charter called "" and
|
|
617
|
+
# send it looking for `.plot/charters/.json`.
|
|
618
|
+
# NOTE: no apostrophe may appear in this awk region — the whole program is
|
|
619
|
+
# one single-quoted shell string, and one closes it mid-comment.
|
|
620
|
+
if (agent_set[i] == 1) out = out ",\"agent\":\"" jesc(agent_of[i]) "\""
|
|
587
621
|
out = out "}"
|
|
588
622
|
first = 0
|
|
589
623
|
}
|
|
@@ -1012,6 +1046,44 @@ section == "slices" && slice_shape != "heading" {
|
|
|
1012
1046
|
if (waits_note ~ "^(" PREFIXES ")/[^ \t]+$") has_waits = 1
|
|
1013
1047
|
else waits_note = ""
|
|
1014
1048
|
}
|
|
1049
|
+
# WHICH KIND OF AGENT THIS SLICE NEEDS: `<!-- agent: reviewer -->`.
|
|
1050
|
+
#
|
|
1051
|
+
# `--agent <name>` was the only selector and only an operator could type it.
|
|
1052
|
+
# `plot-registryd` hands a queued slice to a free agent with no `--agent`
|
|
1053
|
+
# anywhere in the path, so an unattended fleet ran every slice as the same
|
|
1054
|
+
# undifferentiated worker. The declaration has to live in the PLAN to reach a
|
|
1055
|
+
# dispatch nobody is watching.
|
|
1056
|
+
#
|
|
1057
|
+
# PER-BRANCH, NEVER A `## Status` FIELD. Every Status field is plan-level, so
|
|
1058
|
+
# one there could declare a single kind for a plan with several slices — and a
|
|
1059
|
+
# reviewer slice beside an implementer slice is the population this is for.
|
|
1060
|
+
#
|
|
1061
|
+
# The value runs to the closing marker, the way `builds:` does and unlike
|
|
1062
|
+
# `waits:`. A charter name is a bare word today, so both rules agree; this one
|
|
1063
|
+
# is chosen because it does not silently truncate if a name grows a space.
|
|
1064
|
+
#
|
|
1065
|
+
# IT CANNOT VALIDATE ITS VALUE, and that is the difference from `waits:` above.
|
|
1066
|
+
# A prerequisite has branch-prefix structure, which is what keeps a SYNTAX
|
|
1067
|
+
# EXAMPLE in prose from reading as a declaration; a charter name is a bare word
|
|
1068
|
+
# with no structure to check. What protects the templates instead is NESTING:
|
|
1069
|
+
# their documentation sits inside an outer `<!-- ... -->` block, so the inner
|
|
1070
|
+
# `agent:` carries no `<!--` of its own and this pattern never matches it —
|
|
1071
|
+
# the same accident of shape that already keeps `builds:` out of the parse of
|
|
1072
|
+
# a template. Documenting it as a bare `<!-- agent: reviewer -->` line would
|
|
1073
|
+
# hand a kind to every plan created from that template. A test pins it.
|
|
1074
|
+
#
|
|
1075
|
+
# `has_agent` carries presence separately from the value, as all three
|
|
1076
|
+
# annotations before it do: a slice naming no kind emits no key, so dispatch
|
|
1077
|
+
# reads a name or nothing and never a blank string that looks like one.
|
|
1078
|
+
agent_note = ""
|
|
1079
|
+
has_agent = 0
|
|
1080
|
+
if ($0 ~ /<!--[ \t]*agent:[ \t]*/) {
|
|
1081
|
+
_ag = $0
|
|
1082
|
+
sub(/^.*<!--[ \t]*agent:[ \t]*/, "", _ag)
|
|
1083
|
+
sub(/[ \t]*-->.*$/, "", _ag)
|
|
1084
|
+
agent_note = trim(_ag)
|
|
1085
|
+
if (agent_note != "") has_agent = 1
|
|
1086
|
+
}
|
|
1015
1087
|
# ONE LIST ITEM, AT MOST ONE CLAIM — an `if`, not the `while` this was.
|
|
1016
1088
|
#
|
|
1017
1089
|
# The old loop walked the line taking every backticked name on it, which is
|
|
@@ -1051,6 +1123,10 @@ section == "slices" && slice_shape != "heading" {
|
|
|
1051
1123
|
waits_set[n_branches] = has_waits
|
|
1052
1124
|
builds_of[n_branches] = builds_note
|
|
1053
1125
|
builds_set[n_branches] = has_builds
|
|
1126
|
+
# The kind travels with the branch, presence tracked separately so a slice
|
|
1127
|
+
# declaring none emits no key.
|
|
1128
|
+
agent_of[n_branches] = agent_note
|
|
1129
|
+
agent_set[n_branches] = has_agent
|
|
1054
1130
|
ordered_b[n_branches] = b
|
|
1055
1131
|
}
|
|
1056
1132
|
line = $0
|
|
@@ -1188,6 +1264,19 @@ section == "slices" && slice_shape == "heading" {
|
|
|
1188
1264
|
if (waits_note ~ "^(" PREFIXES ")/[^ \t]+$") has_waits = 1
|
|
1189
1265
|
else waits_note = ""
|
|
1190
1266
|
}
|
|
1267
|
+
# The agent kind, read exactly as the list-item spelling reads it. Both
|
|
1268
|
+
# dialects emit the same waves[], so a field added to one only would break that
|
|
1269
|
+
# contract the first time a plan migrated. See the list-item block for why the
|
|
1270
|
+
# value runs to the closing marker and why this one cannot validate itself.
|
|
1271
|
+
agent_note = ""
|
|
1272
|
+
has_agent = 0
|
|
1273
|
+
if ($0 ~ /<!--[ \t]*agent:[ \t]*/) {
|
|
1274
|
+
_ag = $0
|
|
1275
|
+
sub(/^.*<!--[ \t]*agent:[ \t]*/, "", _ag)
|
|
1276
|
+
sub(/[ \t]*-->.*$/, "", _ag)
|
|
1277
|
+
agent_note = trim(_ag)
|
|
1278
|
+
if (agent_note != "") has_agent = 1
|
|
1279
|
+
}
|
|
1191
1280
|
|
|
1192
1281
|
# The branch is the `Branch:` value, matched against the known prefixes exactly
|
|
1193
1282
|
# as the old shape matched the backticked name. Written unquoted in the heading
|
|
@@ -1211,6 +1300,8 @@ section == "slices" && slice_shape == "heading" {
|
|
|
1211
1300
|
waits_set[n_branches] = has_waits
|
|
1212
1301
|
builds_of[n_branches] = builds_note
|
|
1213
1302
|
builds_set[n_branches] = has_builds
|
|
1303
|
+
agent_of[n_branches] = agent_note
|
|
1304
|
+
agent_set[n_branches] = has_agent
|
|
1214
1305
|
ordered_b[n_branches] = b
|
|
1215
1306
|
}
|
|
1216
1307
|
|