@plainconceptsplatform/workflows 0.17.0 → 0.20.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.
Files changed (60) hide show
  1. package/dist/catalog-installation.js +25 -13
  2. package/dist/stack-defaults.js +16 -16
  3. package/dist/worker-env.js +24 -21
  4. package/loops/actions/add-issue-labels/action.yml +50 -50
  5. package/loops/actions/agent-output.cjs +17 -17
  6. package/loops/actions/apply-agent-bundle/action.yml +24 -24
  7. package/loops/actions/apply-agent-comments/action.yml +42 -42
  8. package/loops/actions/apply-agent-labels/action.yml +55 -55
  9. package/loops/actions/apply-agent-output/action.yml +108 -108
  10. package/loops/actions/assess-blast-radius/action.yml +148 -0
  11. package/loops/actions/assess-blast-radius/assess-blast-radius.sh +138 -0
  12. package/loops/actions/classify-route/action.yml +100 -100
  13. package/loops/actions/cleanup-artifacts/action.yml +91 -91
  14. package/loops/actions/close-agent-issues/action.yml +43 -43
  15. package/loops/actions/create-agent-issues/action.yml +52 -52
  16. package/loops/actions/create-issue-comment/action.yml +29 -29
  17. package/loops/actions/download-agent-output/action.yml +53 -53
  18. package/loops/actions/housekeeping/action.yml +55 -2
  19. package/loops/actions/identify-gate-subject/action.yml +134 -122
  20. package/loops/actions/link-pr-to-issue/action.yml +40 -40
  21. package/loops/actions/list-open-issues/action.yml +33 -33
  22. package/loops/actions/load-issue-context/action.yml +45 -45
  23. package/loops/actions/merge-agent-pr/action.yml +49 -49
  24. package/loops/actions/push-agent-branch/action.yml +45 -45
  25. package/loops/actions/remove-issue-labels/action.yml +37 -37
  26. package/loops/actions/require-open-issue/action.yml +59 -0
  27. package/loops/actions/update-agent-issues/action.yml +58 -58
  28. package/loops/actions/validate-merge-gate-output/action.yml +62 -40
  29. package/loops/actions/validate-merge-gate-output/validate-merge-gate-output.sh +147 -31
  30. package/loops/actions/validate-refine-output/action.yml +48 -44
  31. package/loops/actions/validate-refine-output/validate-refine-output.sh +15 -4
  32. package/loops/actions/validate-review-output/action.yml +35 -35
  33. package/loops/actions/validate-triage-output/action.yml +36 -36
  34. package/loops/actions/verify-composite-actions/action.yml +9 -9
  35. package/loops/actions/verify-refine-output/action.yml +9 -9
  36. package/loops/actions/verify-refine-output/verify-refine-output.sh +6 -1
  37. package/loops/actions/verify-route-matrix/action.yml +9 -9
  38. package/loops/actions/verify-route-matrix/verify-gate-metrics.mjs +51 -0
  39. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +630 -8
  40. package/loops/scripts/compile-agent-workflows.mjs +331 -331
  41. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  42. package/loops/templates/ci/app-ci-dotnet-next.yml +330 -330
  43. package/loops/templates/ci/app-ci-node-monorepo.yml +260 -260
  44. package/loops/templates/issues/bug_report.yml +109 -109
  45. package/loops/templates/issues/feature_request.yml +75 -75
  46. package/loops/templates/opencode/opencode.ci.json +55 -49
  47. package/loops/templates/opencode/opencode.ci.json.md +59 -49
  48. package/loops/templates/release/github-release.yml +30 -30
  49. package/loops/workflows/agent-apply-review.md +33 -3
  50. package/loops/workflows/agent-audit.md +36 -0
  51. package/loops/workflows/agent-implement.md +77 -5
  52. package/loops/workflows/agent-merge-gate.md +368 -148
  53. package/loops/workflows/agent-refine.md +93 -17
  54. package/loops/workflows/agent-release.md +4 -4
  55. package/loops/workflows/agent-triage.md +33 -1
  56. package/loops/workflows/authorize-bot-work.yml +105 -105
  57. package/loops/workflows/shared/opencode-ci.md +206 -206
  58. package/loops/workflows/shared/platform-defaults.md +19 -19
  59. package/loops/workflows/work-router.yml +25 -11
  60. package/package.json +2 -2
@@ -49,7 +49,37 @@ on:
49
49
 
50
50
  # Rung 4. Router has classified the event; this job validates PR ownership and checks for
51
51
  # substantive feedback. A custom job, not `on.steps`, because the prompt needs these values.
52
+ # The gate job that the top-level `if:` reads. gh-aw folds that `if:` into the generated
53
+ # activation job but gives activation no dependency on the job, so the reference resolves
54
+ # to '' and the clause is false -- the agent would never run. The package's own validator
55
+ # catches it after compilation; this is the line it asks for, the same one the merge gate
56
+ # uses for protected_changes.
57
+ needs: [still_open]
52
58
  jobs:
59
+ # A route dispatched while the issue was open must not execute after it has been closed. The
60
+ # classifier can only see `github.event.issue.state`, which is the state when the event fired
61
+ # and is absent on a workflow_dispatch, and this fleet queues for a runner for ten minutes and
62
+ # more. Numa #659 was closed one second after a comment dispatched refine; the run reached
63
+ # `reserve` thirteen minutes later and refined a closed issue to completion. Read now, once,
64
+ # and gate both the reservation and the agent on it.
65
+ still_open:
66
+ runs-on: agents-arc
67
+ permissions:
68
+ contents: read
69
+ issues: read
70
+ outputs:
71
+ open: ${{ steps.state.outputs.open }}
72
+ steps:
73
+ - name: Checkout workflow actions
74
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
75
+ with:
76
+ persist-credentials: false
77
+ - name: Read the issue state
78
+ id: state
79
+ uses: ./.github/actions/require-open-issue
80
+ with:
81
+ token: ${{ github.token }}
82
+ issue-number: ${{ inputs.issue-number }}
53
83
  subject:
54
84
  runs-on: agents-arc
55
85
  permissions:
@@ -118,8 +148,8 @@ jobs:
118
148
  echo "PR #$PR has $unresolved unresolved thread(s)"
119
149
 
120
150
  reserve:
121
- needs: subject
122
- if: needs.subject.outputs.found == 'true' && needs.subject.outputs.issue != ''
151
+ needs: [subject, still_open]
152
+ if: needs.subject.outputs.found == 'true' && needs.subject.outputs.issue != '' && needs.still_open.outputs.open == 'true'
123
153
  runs-on: agents-arc
124
154
  permissions:
125
155
  contents: read
@@ -290,7 +320,7 @@ jobs:
290
320
  ${{ env.INCOMPLETE_COMMENT }}
291
321
  [View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
292
322
 
293
- if: needs.subject.outputs.found == 'true'
323
+ if: needs.subject.outputs.found == 'true' && needs.still_open.outputs.open == 'true'
294
324
 
295
325
  runs-on: agents-arc
296
326
  runs-on-slim: agents-arc
@@ -119,6 +119,42 @@ jobs:
119
119
  bug
120
120
  refine
121
121
 
122
+ # An audit that produced nothing reported success. The whole run -- a full agent, its tokens,
123
+ # its half hour -- ended with `conclude` skipped, because that job requires a processed item,
124
+ # and a skipped job leaves the run green. Numa's audit on 2026-09-10 did exactly that:
125
+ # `agent_output.json` 24 bytes, `safe-output-items.jsonl` empty, every job success, no report
126
+ # filed and nothing anywhere saying so. The next scheduled audit would have looked identical.
127
+ #
128
+ # This fires when the agent succeeded and the safe-outputs handler processed nothing at all.
129
+ #
130
+ # Step 6 of the prompt gives a clean codebase its own outcome -- call `noop` and stop -- and
131
+ # that outcome must not be reported as a failure. `noop` is a registered safe output here
132
+ # (`noop: max 1`), so the handler receives it as a message and the processed count is not zero,
133
+ # which keeps a deliberately empty audit out of this branch. That is read from the handler
134
+ # configuration rather than observed in a run: no audit in these repositories has yet emitted a
135
+ # noop. The direct signal, `noop_message`, belongs to gh-aw's `conclusion` job, and that job
136
+ # depends on every custom job here, so naming it is a dependency cycle and the worker will not
137
+ # compile -- the first version of this job was rejected for exactly that.
138
+ #
139
+ # If a genuinely clean audit ever fails here, that is the assumption breaking, and the fix is to
140
+ # carry the noop through a job this one can depend on rather than to widen the condition.
141
+ empty_run:
142
+ needs: [agent, safe_outputs]
143
+ if: >
144
+ needs.agent.result == 'success' &&
145
+ needs.safe_outputs.outputs.process_safe_outputs_processed_count == '0'
146
+ runs-on: agents-arc
147
+ permissions:
148
+ contents: read
149
+ steps:
150
+ - name: Report an audit that produced no outcome
151
+ env:
152
+ RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
153
+ run: |
154
+ set -euo pipefail
155
+ echo "::error::The audit agent finished without emitting a report or a noop. The prompt requires one of the two: create_issue with the findings, or noop when the codebase is clean. Nothing was filed and no reason was given, so this run is a failure rather than a clean audit. The next scheduled audit will try again. ${RUN_URL}"
156
+ exit 1
157
+
122
158
  safe-outputs:
123
159
  # A failed run is already a red run. An issue per failure buries the real backlog
124
160
  # under noise nobody closes.
@@ -17,6 +17,13 @@ env:
17
17
  STALLED_LABEL: stalled
18
18
  PR_PENDING_LABEL: pr-pending
19
19
  NO_PULL_REQUEST_COMMENT: "The implementation run finished without producing a pull request. Nothing was lost, but nothing landed either: the issue keeps `implement` and is flagged for a retry."
20
+ # Said when the agent DID write the code and the push failed. gh-aw pushes through the GraphQL
21
+ # signed-commits API, which rebases onto the current parent, so a `main` that moved under a long
22
+ # run conflicts; gh-aw keeps the work by filing the patch as an issue rather than dropping it,
23
+ # and comments the link on this issue itself. Telling someone "nothing landed" over the top of
24
+ # that sends them to reimplement work that already exists. One line: the compiler flattens a
25
+ # multi-line env value.
26
+ PUSH_CONFLICT_COMMENT: "The implementation produced a patch, but pushing it failed: it no longer applies to `main`, which moved while this ran. gh-aw filed the patch as a separate issue rather than losing it, and linked it in its own comment above. The work is there and needs rebasing onto current `main`, not writing again."
20
27
  GIT_AUTHOR_NAME: "github-actions[bot]"
21
28
  GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com"
22
29
  GIT_COMMITTER_NAME: "github-actions[bot]"
@@ -70,7 +77,37 @@ on:
70
77
  required: false
71
78
  type: string
72
79
  default: '0'
80
+ # The gate job that the top-level `if:` reads. gh-aw folds that `if:` into the generated
81
+ # activation job but gives activation no dependency on the job, so the reference resolves
82
+ # to '' and the clause is false -- the agent would never run. The package's own validator
83
+ # catches it after compilation; this is the line it asks for, the same one the merge gate
84
+ # uses for protected_changes.
85
+ needs: [still_open]
73
86
  jobs:
87
+ # A route dispatched while the issue was open must not execute after it has been closed. The
88
+ # classifier can only see `github.event.issue.state`, which is the state when the event fired
89
+ # and is absent on a workflow_dispatch, and this fleet queues for a runner for ten minutes and
90
+ # more. Numa #659 was closed one second after a comment dispatched refine; the run reached
91
+ # `reserve` thirteen minutes later and refined a closed issue to completion. Read now, once,
92
+ # and gate both the reservation and the agent on it.
93
+ still_open:
94
+ runs-on: agents-arc
95
+ permissions:
96
+ contents: read
97
+ issues: read
98
+ outputs:
99
+ open: ${{ steps.state.outputs.open }}
100
+ steps:
101
+ - name: Checkout workflow actions
102
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
103
+ with:
104
+ persist-credentials: false
105
+ - name: Read the issue state
106
+ id: state
107
+ uses: ./.github/actions/require-open-issue
108
+ with:
109
+ token: ${{ github.token }}
110
+ issue-number: ${{ inputs.issue-number }}
74
111
  eligibility:
75
112
  runs-on: agents-arc
76
113
  permissions:
@@ -107,8 +144,8 @@ jobs:
107
144
  echo "eligible=true" >> "$GITHUB_OUTPUT"
108
145
 
109
146
  reserve:
110
- needs: [eligibility]
111
- if: needs.eligibility.outputs.eligible == 'true'
147
+ needs: [eligibility, still_open]
148
+ if: needs.eligibility.outputs.eligible == 'true' && needs.still_open.outputs.open == 'true'
112
149
  runs-on: agents-arc
113
150
  permissions:
114
151
  contents: read
@@ -227,8 +264,43 @@ jobs:
227
264
  # comment, and no bot-working — which also hid it from the hourly stale-reservation sweep.
228
265
  # Comments do not re-trigger implement, so nothing on any path would ever look at it again.
229
266
  # It was the only failure in the fleet that signalled nobody at all.
267
+ #
268
+ # There are two ways to reach "no pull request", and telling a person they are the same
269
+ # thing wastes their time. gh-aw pushes through the GraphQL signed-commits API, which
270
+ # rebases the commit range onto the current parent; when `main` has moved under a long run
271
+ # the rebase conflicts, and gh-aw keeps the work by filing the patch as an issue instead of
272
+ # dropping it, commenting the link on this issue itself. Saying "nothing landed" over the
273
+ # top of that is false: the patch exists and needs rebasing, not reimplementing. Seen on
274
+ # Numa #657, where the same change had landed on main by hand while the agent was writing
275
+ # it, and reproduced deliberately on dogfood #10 -> #11.
276
+ #
277
+ # The signal is the item counter, not `code_push_failure_count`. gh-aw treats the fallback
278
+ # as a *successful* outcome for the item -- the dogfood run logged `Status: success`,
279
+ # `Successful: 1` and a resolved `GH_AW_CODE_PUSH_FAILURE_COUNT: 0` while filing #11 -- so
280
+ # gating on that count posted the wrong message. `create_pull_request` is the only safe
281
+ # output this worker permits, so one succeeded item with no pull request number can only
282
+ # mean the push fell back to an issue. Nothing produced at all leaves the counter at 0.
283
+ - name: Flag a patch that could not be pushed
284
+ if: needs.safe_outputs.outputs.created_pr_number == '' && needs.safe_outputs.outputs.process_safe_outputs_items_succeeded != '0' && needs.safe_outputs.outputs.process_safe_outputs_items_succeeded != ''
285
+ uses: ./.github/actions/add-issue-labels
286
+ with:
287
+ token: ${{ steps.app-token.outputs.token }}
288
+ issue-number: ${{ inputs.issue-number }}
289
+ labels: |-
290
+ ${{ env.REVIEW_LABEL }}
291
+ ${{ env.STALLED_LABEL }}
292
+ - name: Say where the patch went
293
+ if: needs.safe_outputs.outputs.created_pr_number == '' && needs.safe_outputs.outputs.process_safe_outputs_items_succeeded != '0' && needs.safe_outputs.outputs.process_safe_outputs_items_succeeded != ''
294
+ uses: ./.github/actions/create-issue-comment
295
+ with:
296
+ token: ${{ steps.app-token.outputs.token }}
297
+ issue-number: ${{ inputs.issue-number }}
298
+ body: |
299
+ ${{ env.IMPLEMENT_MARKER }}
300
+ ${{ env.PUSH_CONFLICT_COMMENT }}
301
+ [View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
230
302
  - name: Flag a run that produced no pull request
231
- if: needs.safe_outputs.outputs.created_pr_number == ''
303
+ if: needs.safe_outputs.outputs.created_pr_number == '' && (needs.safe_outputs.outputs.process_safe_outputs_items_succeeded == '0' || needs.safe_outputs.outputs.process_safe_outputs_items_succeeded == '')
232
304
  uses: ./.github/actions/add-issue-labels
233
305
  with:
234
306
  token: ${{ steps.app-token.outputs.token }}
@@ -237,7 +309,7 @@ jobs:
237
309
  ${{ env.REVIEW_LABEL }}
238
310
  ${{ env.STALLED_LABEL }}
239
311
  - name: Say so on the issue
240
- if: needs.safe_outputs.outputs.created_pr_number == ''
312
+ if: needs.safe_outputs.outputs.created_pr_number == '' && (needs.safe_outputs.outputs.process_safe_outputs_items_succeeded == '0' || needs.safe_outputs.outputs.process_safe_outputs_items_succeeded == '')
241
313
  uses: ./.github/actions/create-issue-comment
242
314
  with:
243
315
  token: ${{ steps.app-token.outputs.token }}
@@ -436,7 +508,7 @@ jobs:
436
508
  needs: [eligibility]
437
509
  if: needs.eligibility.outputs.eligible == 'true'
438
510
 
439
- if: inputs.issue-number != ''
511
+ if: inputs.issue-number != '' && needs.still_open.outputs.open == 'true'
440
512
 
441
513
  runs-on: agents-arc
442
514
  runs-on-slim: agents-arc