@plot-pm/board 0.6.0-rc.2 → 0.6.0-rc.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plot-pm/board",
3
- "version": "0.6.0-rc.2",
3
+ "version": "0.6.0-rc.21",
4
4
  "description": "Local Kanban board for Plot — a glanceable view of plan phases from docs/plans, with sprint and story filters",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/plot-config.sh CHANGED
@@ -47,6 +47,13 @@
47
47
  # Tracker plot | jira | github-issues | linear (+ URL)
48
48
  # (plot = plans in this repo ARE the tracker; absent = same)
49
49
  # Git host github | bitbucket (resolves gh vs bb)
50
+ # CI jenkins | github-actions | none — which CI system this
51
+ # project uses. Recorded by /plot-board-setup; not yet
52
+ # read by the board.
53
+ # Jenkins instance the slug or URL passed to a Jenkins CLI's -I flag.
54
+ # Read back by /plot-board-setup to verify auth against
55
+ # the right instance — without it the only runnable
56
+ # check verifies nothing.
50
57
  #
51
58
  # `Plan template` is a repo-root-relative path to the plan template /plot-idea
52
59
  # instantiates; when absent, /plot-idea falls back to the shipped template.
package/plot-plan-meta.sh CHANGED
@@ -68,6 +68,12 @@
68
68
  # continuation line is not seen — keep it on the branch line.
69
69
  # prs PR numbers from `→ #NNN` links in the `## Branches`
70
70
  # section (sorted, unique)
71
+ # issues tracker issue numbers this plan answers, from the `## Status`
72
+ # `Issue:` line or front matter `issue:` (sorted, unique).
73
+ # A DEDICATED field, never a scan of the body for `#NNN`: a
74
+ # body scan cannot tell a signal from a citation, which is the
75
+ # same reason `prs` reads only `→ #NNN`. Accepts a list
76
+ # (`Issue: #226, #228`) because one plan can answer several.
71
77
  # review_raw the plan's review-channel answer as written (`## Status`
72
78
  # `Review:` or front matter `review:`); "" if absent
73
79
  # review normalized: pr|in-session|ballot|UNKNOWN|NONE
@@ -132,7 +138,7 @@ if [ ${#files[@]} -eq 0 ] && [ ${#missing[@]} -eq 0 ]; then
132
138
  fi
133
139
 
134
140
  for f in ${missing[@]+"${missing[@]}"}; do
135
- printf '{"file":"%s","format":"none","error":"file not found","phase_raw":"","phase":"NONE","phase_alt_raw":"","phase_alt":"NONE","type":"","title":"","sprint":"","story":"","assignee":"","branches":[],"prs":[],"review_raw":"","review":"NONE","impl_raw":"","impl":"NONE","approved_raw":"","released_raw":"","delivered_raw":"","started_raw":[]}\n' \
141
+ printf '{"file":"%s","format":"none","error":"file not found","phase_raw":"","phase":"NONE","phase_alt_raw":"","phase_alt":"NONE","type":"","title":"","sprint":"","story":"","assignee":"","branches":[],"prs":[],"issues":[],"review_raw":"","review":"NONE","impl_raw":"","impl":"NONE","approved_raw":"","released_raw":"","delivered_raw":"","started_raw":[]}\n' \
136
142
  "$(printf '%s' "$f" | sed 's/\\/\\\\/g; s/"/\\"/g')"
137
143
  done
138
144
 
@@ -218,11 +224,13 @@ function reset_state() {
218
224
  in_fm = 0; section = ""; in_comment = 0; in_challenge = 0; branches_seen = 0
219
225
  delete branches; n_branches = 0
220
226
  delete prs; n_prs = 0
227
+ fm_issue = ""; canon_issue = ""
228
+ delete issues; n_issues = 0
221
229
  delete wave_names; delete wave_of; delete wave_seq; delete wave_count
222
230
  delete deferred_of; delete claimed_of; delete ordered_b; n_waves = 0
223
231
  delete started; n_started = 0
224
232
  }
225
- function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assignee, review, impl, approved, delivered, i, j, out, sorted_b, sorted_p, nb, np) {
233
+ function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assignee, review, impl, approved, delivered, issue, i, j, out, sorted_b, sorted_p, sorted_i, nb, np, ni) {
226
234
  if (fm_status != "" || fm_phase != "") {
227
235
  fmt = "frontmatter"
228
236
  praw = (fm_status != "") ? fm_status : fm_phase
@@ -245,6 +253,20 @@ function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assigne
245
253
  released = strip_placeholder((fm_released != "") ? fm_released : canon_released)
246
254
  delivered = strip_placeholder((fm_delivered != "") ? fm_delivered : canon_delivered)
247
255
  if (fm_started != "" && strip_placeholder(fm_started) != "") started[++n_started] = fm_started
256
+ # `Issue:` names the tracker signals this plan answers, and it is a DEDICATED
257
+ # field rather than a scan of the body for `#NNN`. A body scan cannot tell a
258
+ # signal from a citation: plans in this repo mention PR numbers constantly,
259
+ # and one of them cites `#226`, `#227`, `#228` as history in its Motivation
260
+ # while naming `PR #232` two sections later. That is the same ambiguity `prs`
261
+ # already answered by reading only `-> #NNN` — one field, one meaning.
262
+ #
263
+ # A LIST, because one plan can answer several signals; the plan that
264
+ # introduced this field subsumes three.
265
+ issue = strip_placeholder((fm_issue != "") ? fm_issue : canon_issue)
266
+ while (match(issue, /#[0-9]+/)) {
267
+ issues[++n_issues] = substr(issue, RSTART + 1, RLENGTH - 1)
268
+ issue = substr(issue, RSTART + RLENGTH)
269
+ }
248
270
  # Insertion sort + dedupe (portable: no gawk asort).
249
271
  nb = 0
250
272
  for (i = 1; i <= n_branches; i++) {
@@ -260,6 +282,13 @@ function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assigne
260
282
  for (j = np; j >= 1 && sorted_p[j] > prs[i]+0; j--) sorted_p[j+1] = sorted_p[j]
261
283
  sorted_p[j+1] = prs[i]+0; np++
262
284
  }
285
+ ni = 0
286
+ for (i = 1; i <= n_issues; i++) {
287
+ for (j = 1; j <= ni && sorted_i[j] != issues[i]+0; j++) ;
288
+ if (j <= ni) continue
289
+ for (j = ni; j >= 1 && sorted_i[j] > issues[i]+0; j--) sorted_i[j+1] = sorted_i[j]
290
+ sorted_i[j+1] = issues[i]+0; ni++
291
+ }
263
292
  out = "{\"file\":\"" jesc(cur_file) "\",\"format\":\"" fmt "\""
264
293
  out = out ",\"phase_raw\":\"" jesc(praw) "\",\"phase\":\"" norm_phase(praw) "\""
265
294
  out = out ",\"phase_alt_raw\":\"" jesc(palt_raw) "\",\"phase_alt\":\"" norm_phase(palt_raw) "\""
@@ -270,6 +299,8 @@ function emit_record( fmt, praw, palt_raw, traw, title, sprint, story, assigne
270
299
  for (i = 1; i <= nb; i++) out = out (i > 1 ? "," : "") "\"" jesc(sorted_b[i]) "\""
271
300
  out = out "],\"prs\":["
272
301
  for (i = 1; i <= np; i++) out = out (i > 1 ? "," : "") sorted_p[i]
302
+ out = out "],\"issues\":["
303
+ for (i = 1; i <= ni; i++) out = out (i > 1 ? "," : "") sorted_i[i]
273
304
  out = out "]"
274
305
  # waves[]: branches grouped by `### ` subheading, in document order. A plan
275
306
  # with no subheadings yields one wave with an empty name.
@@ -315,6 +346,7 @@ in_fm {
315
346
  else if (lower ~ /^title:/ && fm_title == "") fm_title = val_after_colon($0)
316
347
  else if (lower ~ /^sprint:/ && fm_sprint == "") fm_sprint = val_after_colon($0)
317
348
  else if (lower ~ /^story:/ && fm_story == "") fm_story = val_after_colon($0)
349
+ else if (lower ~ /^issue:/ && fm_issue == "") fm_issue = val_after_colon($0)
318
350
  else if (lower ~ /^assignee:/ && fm_assignee == "") fm_assignee = val_after_colon($0)
319
351
  else if (lower ~ /^review:/ && fm_review == "") fm_review = val_after_colon($0)
320
352
  else if (lower ~ /^impl:/ && fm_impl == "") fm_impl = val_after_colon($0)
@@ -368,6 +400,7 @@ section == "status" {
368
400
  else if (lower ~ /^[ \t]*[-*]?[ \t]*\**type[:*]/ && canon_type == "") canon_type = val_after_colon($0)
369
401
  else if (lower ~ /^[ \t]*[-*]?[ \t]*\**sprint[:*]/ && canon_sprint == "") canon_sprint = val_after_colon($0)
370
402
  else if (lower ~ /^[ \t]*[-*]?[ \t]*\**story[:*]/ && canon_story == "") canon_story = val_after_colon($0)
403
+ else if (lower ~ /^[ \t]*[-*]?[ \t]*\**issue[:*]/ && canon_issue == "") canon_issue = val_after_colon($0)
371
404
  else if (lower ~ /^[ \t]*[-*]?[ \t]*\**review[:*]/ && canon_review == "") canon_review = val_after_colon($0)
372
405
  else if (lower ~ /^[ \t]*[-*]?[ \t]*\**impl[:*]/ && canon_impl == "") canon_impl = val_after_colon($0)
373
406
  else if (lower ~ /^[ \t]*[-*]?[ \t]*\**approved[:*]/ && canon_approved == "") canon_approved = val_after_colon($0)