@plainconceptsplatform/workflows 0.16.2 → 0.16.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.
@@ -62,10 +62,17 @@ runs:
62
62
  const since = new Date(Date.now() - Number(process.env.LOOKBACK_HOURS) * 3600 * 1000);
63
63
 
64
64
  // Only workflows this package ships. A consumer's own workflow name is theirs and can
65
- // describe a product, a customer or an environment -- Lyceum really does have
66
- // `name: Deploy app to ${{ inputs.environment }}` -- so an allowlist by filename is the
67
- // boundary, and it is the only thing keeping those names out. Anything not matching is
68
- // counted and otherwise ignored.
65
+ // describe a product, a customer or an environment -- one consumer really does have a
66
+ // job named "Deploy app to" followed by an interpolated environment input -- so an
67
+ // allowlist by filename is the boundary, and it is the only thing keeping those names
68
+ // out. Anything not matching is counted and otherwise ignored.
69
+ //
70
+ // That example is described rather than quoted on purpose. An Actions expression is
71
+ // interpolated into this script by the runner BEFORE the JavaScript is parsed, comments
72
+ // included, so quoting one here made the comment a live substitution -- flagged by
73
+ // semgrep as github-script-injection, and invisible to a `node --check` pass because
74
+ // the checker has to mask the expression to parse the file at all. Everything this
75
+ // script needs arrives through `env:` above; nothing is interpolated into it.
69
76
  //
70
77
  // Exactly the filenames the package installs, and no pattern that admits a name it does
71
78
  // not. The previous version was written as a shape (`\.(yml|lock\.yml)$` over a group of
@@ -405,6 +405,83 @@ else
405
405
  fi
406
406
  if [ "$BELT_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
407
407
 
408
+ # The two guards in the stale-reservation sweep, executed rather than read.
409
+ #
410
+ # Both were dead in production for as long as they existed. They matched `#N` with a `\b` word
411
+ # boundary written as `\\b` inside a double-quoted bash string -- and bash halves that to `\b`,
412
+ # which jq's string parser reads as a BACKSPACE (0x08), not as a boundary. So the regex hunted
413
+ # for a character no title or body contains, both guards returned 0 every time, and the sweep
414
+ # cleared `bot-working` off every issue that carried it: live queued runs included. Watched it
415
+ # happen in the dogfood repository twenty minutes after that repository existed.
416
+ #
417
+ # Implement carries the same filter to decide whether a pull request already closes its issue,
418
+ # and it was dead the same way, which is the most likely source of the same-day duplicate pull
419
+ # requests in the backlog.
420
+ #
421
+ # There is no backslash in the fix and no way to reintroduce one by accident: the subject gains a
422
+ # trailing space, so a number at the end of a body is still followed by a non-digit, and the
423
+ # pattern matches `[^0-9]`. Four quoting layers cannot eat a character class.
424
+ SWEEP_OK=1
425
+ if ! grep -qF 'test(\"#${issue}[^0-9]\")' "$ROUTER_YML"; then
426
+ SWEEP_OK=0
427
+ echo "FAIL: the stale-reservation sweep does not match a run title with a character class; a word boundary there is eaten by bash and jq" >&2
428
+ else
429
+ sweep_runs='{"workflow_runs":[
430
+ {"status":"queued","display_title":"Working (Refine): a thing (#1)"},
431
+ {"status":"completed","display_title":"Working (Refine): another (#2)"},
432
+ {"status":"in_progress","display_title":"Working (Implement): more (#10)"}
433
+ ]}'
434
+ # The same program the router runs, with the same escaping, expanded the same way.
435
+ live_for() {
436
+ jq "[.workflow_runs[]
437
+ | select(.status == \"queued\" or .status == \"in_progress\" or .status == \"pending\" or .status == \"waiting\")
438
+ | select((.display_title + \" \") | test(\"#${1}[^0-9]\"))] | length" <<<"$sweep_runs"
439
+ }
440
+ for probe in "1:1" "2:0" "10:1" "3:0"; do
441
+ issue="${probe%%:*}"; want="${probe##*:}"
442
+ got=$(live_for "$issue" 2>&1)
443
+ if [ "$got" != "$want" ]; then
444
+ SWEEP_OK=0
445
+ echo "FAIL: the sweep's live-run check said ${got} live run(s) for #${issue}, expected ${want}" >&2
446
+ fi
447
+ done
448
+
449
+ sweep_pulls='[
450
+ {"number":11,"body":"Closes #1 and some detail."},
451
+ {"number":12,"body":"fixes #22"},
452
+ {"number":13,"body":"Mentions #9 but closes nothing."},
453
+ {"number":14,"body":"closes #5"}
454
+ ]'
455
+ has_pr_for() {
456
+ jq "[.[] | select(((.body // \"\") + \" \") | ascii_downcase | test(\"clos(e|es|ed) #${1}[^0-9]|fix(es|ed)? #${1}[^0-9]|resolves? #${1}[^0-9]\"))] | length" <<<"$sweep_pulls"
457
+ }
458
+ # The last cases are what the boundary is for: a bare mention is not a close, `#22` must not
459
+ # answer for `#2`, and the trailing space is what makes `closes #5` at the end of a body match.
460
+ for probe in "1:1" "22:1" "9:0" "2:0" "5:1" "999:0"; do
461
+ issue="${probe%%:*}"; want="${probe##*:}"
462
+ got=$(has_pr_for "$issue" 2>&1)
463
+ if [ "$got" != "$want" ]; then
464
+ SWEEP_OK=0
465
+ echo "FAIL: the open-pull-request guard found ${got} for #${issue}, expected ${want}" >&2
466
+ fi
467
+ done
468
+ fi
469
+ if [ "$SWEEP_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
470
+
471
+ # And nothing may go back to a word boundary in a double-quoted jq program anywhere, because the
472
+ # failure is silent in both directions: the filter matches nothing and the job stays green.
473
+ BOUNDARY_OK=1
474
+ for candidate in "$ROUTER_YML" "${WORKFLOWS_DIR}"/agent-*.md; do
475
+ [ -f "$candidate" ] || continue
476
+ offenders=$(grep -n -- '--jq "' "$candidate" 2>/dev/null | grep -F '\b' || true)
477
+ if [ -n "$offenders" ]; then
478
+ BOUNDARY_OK=0
479
+ echo "FAIL: $(basename "$candidate") uses a word boundary inside a double-quoted jq program, which bash and jq turn into a backspace:" >&2
480
+ printf ' %s\n' "$offenders" | cut -c1-160 >&2
481
+ fi
482
+ done
483
+ if [ "$BOUNDARY_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
484
+
408
485
  # `review` must not be a one-way door. It used to be: authorize-bot-work refused to fire on an
409
486
  # issue carrying it, and the classifier refuses to route while it is set, so a person adding
410
487
  # `refine` to a parked issue got nothing at all — no run, no comment, no error. Triage's own
@@ -42,9 +42,9 @@ jobs:
42
42
  steps:
43
43
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
44
44
  - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
45
- with: { version: ${{ env.PNPM_VERSION }}, run_install: false }
45
+ with: { version: "${{ env.PNPM_VERSION }}", run_install: false }
46
46
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
47
- with: { node-version: ${{ env.NODE_VERSION }}, cache: pnpm }
47
+ with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
48
48
  - run: pnpm install --frozen-lockfile
49
49
  - run: pnpm exec biome check .
50
50
 
@@ -55,9 +55,9 @@ jobs:
55
55
  steps:
56
56
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
57
57
  - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
58
- with: { version: ${{ env.PNPM_VERSION }}, run_install: false }
58
+ with: { version: "${{ env.PNPM_VERSION }}", run_install: false }
59
59
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
60
- with: { node-version: ${{ env.NODE_VERSION }}, cache: pnpm }
60
+ with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
61
61
  - run: pnpm install --frozen-lockfile
62
62
  - run: pnpm --filter "$WEB_PACKAGE" run typecheck
63
63
  - run: pnpm --filter "$WEB_PACKAGE" run test
@@ -80,9 +80,9 @@ jobs:
80
80
  with:
81
81
  fetch-depth: 0
82
82
  - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
83
- with: { version: ${{ env.PNPM_VERSION }}, run_install: false }
83
+ with: { version: "${{ env.PNPM_VERSION }}", run_install: false }
84
84
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
85
- with: { node-version: ${{ env.NODE_VERSION }}, cache: pnpm }
85
+ with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
86
86
  - name: Detect web changes
87
87
  id: scope
88
88
  run: |
@@ -107,9 +107,9 @@ jobs:
107
107
  steps:
108
108
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
109
109
  - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
110
- with: { version: ${{ env.PNPM_VERSION }}, run_install: false }
110
+ with: { version: "${{ env.PNPM_VERSION }}", run_install: false }
111
111
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
112
- with: { node-version: ${{ env.NODE_VERSION }}, cache: pnpm }
112
+ with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
113
113
  - run: pnpm install --frozen-lockfile
114
114
  - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
115
115
  with:
@@ -132,9 +132,9 @@ jobs:
132
132
  steps:
133
133
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
134
134
  - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
135
- with: { version: ${{ env.PNPM_VERSION }}, run_install: false }
135
+ with: { version: "${{ env.PNPM_VERSION }}", run_install: false }
136
136
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
137
- with: { node-version: ${{ env.NODE_VERSION }}, cache: pnpm }
137
+ with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
138
138
  - run: pnpm install --frozen-lockfile
139
139
  - run: pnpm --filter "$DESKTOP_PACKAGE" run typecheck
140
140
  - run: pnpm --filter "$DESKTOP_PACKAGE" run build
@@ -156,9 +156,9 @@ jobs:
156
156
  steps:
157
157
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
158
158
  - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
159
- with: { version: ${{ env.PNPM_VERSION }}, run_install: false }
159
+ with: { version: "${{ env.PNPM_VERSION }}", run_install: false }
160
160
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
161
- with: { node-version: ${{ env.NODE_VERSION }}, cache: pnpm }
161
+ with: { node-version: "${{ env.NODE_VERSION }}", cache: pnpm }
162
162
  - run: pnpm install --frozen-lockfile
163
163
  - run: pnpm --filter "$MOBILE_PACKAGE" run typecheck
164
164
  - run: |
@@ -404,7 +404,7 @@ jobs:
404
404
  | grep -oE '<!-- implement-pr: [0-9]+ -->' | head -1 | grep -oE '[0-9]+' || true)
405
405
  if [ -z "$pr" ]; then
406
406
  pr=$(gh pr list --repo "$REPO" --state open --json number,body \
407
- --jq "[.[] | select((.body // \"\") | ascii_downcase | test(\"clos(e|es|ed) #${ISSUE}\\b|fix(es|ed)? #${ISSUE}\\b|resolves? #${ISSUE}\\b\"))][0].number // empty")
407
+ --jq "[.[] | select(((.body // \"\") + \" \") | ascii_downcase | test(\"clos(e|es|ed) #${ISSUE}[^0-9]|fix(es|ed)? #${ISSUE}[^0-9]|resolves? #${ISSUE}[^0-9]\"))][0].number // empty")
408
408
  fi
409
409
  if [ -z "$pr" ]; then
410
410
  echo "::notice::No pull request found for #$ISSUE; nothing to wait for."
@@ -943,11 +943,11 @@ jobs:
943
943
  live=$(gh api "repos/$REPO/actions/runs?per_page=100" \
944
944
  --jq "[.workflow_runs[]
945
945
  | select(.status == \"queued\" or .status == \"in_progress\" or .status == \"pending\" or .status == \"waiting\")
946
- | select(.display_title | test(\"#${issue}\\b\"))] | length")
946
+ | select((.display_title + \" \") | test(\"#${issue}[^0-9]\"))] | length")
947
947
  [ "${live:-0}" -eq 0 ] || continue
948
948
 
949
949
  has_pr=$(gh pr list --repo "$REPO" --state open --json body \
950
- --jq "[.[] | select((.body // \"\") | ascii_downcase | test(\"clos(e|es|ed) #${issue}\\b|fix(es|ed)? #${issue}\\b|resolves? #${issue}\\b\"))] | length")
950
+ --jq "[.[] | select(((.body // \"\") + \" \") | ascii_downcase | test(\"clos(e|es|ed) #${issue}[^0-9]|fix(es|ed)? #${issue}[^0-9]|resolves? #${issue}[^0-9]\"))] | length")
951
951
  [ "${has_pr:-0}" -eq 0 ] || continue
952
952
 
953
953
  echo "Clearing a stale bot-working reservation on #$issue: no run, no pull request."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plainconceptsplatform/workflows",
3
- "version": "0.16.2",
3
+ "version": "0.16.4",
4
4
  "description": "Install and update Platform GitHub agentic workflows.",
5
5
  "keywords": [
6
6
  "github-actions",