@plainconceptsplatform/workflows 1.14.1 → 1.16.1

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 (41) hide show
  1. package/dist/workflow-catalog.d.ts +1 -1
  2. package/dist/workflow-catalog.js +0 -2
  3. package/loops/actions/add-issue-labels/action.yml +50 -50
  4. package/loops/actions/agent-output.cjs +17 -17
  5. package/loops/actions/apply-agent-bundle/action.yml +24 -24
  6. package/loops/actions/apply-agent-comments/action.yml +42 -42
  7. package/loops/actions/apply-agent-labels/action.yml +55 -55
  8. package/loops/actions/assess-blast-radius/assess-blast-radius.sh +0 -0
  9. package/loops/actions/assess-blast-radius/migration-is-additive.sh +0 -0
  10. package/loops/actions/cleanup-artifacts/action.yml +91 -91
  11. package/loops/actions/close-agent-issues/action.yml +43 -43
  12. package/loops/actions/create-issue-comment/action.yml +29 -29
  13. package/loops/actions/decide-agent-retry/classify-failure.sh +0 -0
  14. package/loops/actions/download-agent-output/action.yml +53 -53
  15. package/loops/actions/link-pr-to-issue/action.yml +40 -40
  16. package/loops/actions/list-open-issues/action.yml +33 -33
  17. package/loops/actions/load-issue-context/action.yml +45 -45
  18. package/loops/actions/merge-agent-pr/action.yml +49 -49
  19. package/loops/actions/merge-train/merge-train.sh +0 -0
  20. package/loops/actions/rebase-stale-patch/rebase-stale-patch.sh +0 -0
  21. package/loops/actions/remove-issue-labels/action.yml +37 -37
  22. package/loops/actions/update-agent-issues/action.yml +58 -58
  23. package/loops/actions/validate-merge-gate-output/validate-merge-gate-output.sh +0 -0
  24. package/loops/actions/validate-refine-output/validate-refine-output.sh +13 -1
  25. package/loops/actions/validate-review-output/action.yml +35 -35
  26. package/loops/actions/validate-triage-output/action.yml +36 -36
  27. package/loops/actions/verify-composite-actions/action.yml +9 -9
  28. package/loops/actions/verify-refine-output/action.yml +9 -9
  29. package/loops/actions/verify-refine-output/verify-refine-output.sh +4 -0
  30. package/loops/actions/verify-route-matrix/action.yml +9 -9
  31. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +32 -46
  32. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  33. package/loops/templates/issues/bug_report.yml +109 -109
  34. package/loops/templates/issues/feature_request.yml +75 -75
  35. package/loops/templates/quality-gates/scripts/dast-wait-stable.sh +0 -0
  36. package/loops/workflows/agent-refine.md +29 -118
  37. package/loops/workflows/shared/platform-defaults.md +53 -53
  38. package/package.json +1 -1
  39. package/loops/actions/report-workflow-errors/action.yml +0 -538
  40. package/loops/actions/report-workflow-errors/report-policy.cjs +0 -194
  41. package/loops/templates/agentics/agentics-error-report.yml +0 -101
@@ -43,15 +43,18 @@ env:
43
43
  REFINE_ATTEMPT_MARKER: "<!-- agent-refine-attempt -->"
44
44
  MAX_ATTEMPTS: "5"
45
45
  PARK_AT_ATTEMPT: "4"
46
- # The size gate. An issue over either limit is refused before any model runs: a real
47
- # one, Pliny-Bot #305 (13,398 chars, 92 work units), burned a full agent -- 17 minutes,
48
- # 2.4M tokens -- and died at the provider's 32K output cap with zero outcome, twice.
49
- # Both numbers are measured before the agent starts, so refusal is deterministic and
50
- # costs ten seconds instead of half an hour. The audit files under its own AUDIT_MAX_*,
51
- # which must stay below these or refine refuses what it files.
52
- REFINE_MAX_BODY_CHARS: "8000"
53
- REFINE_MAX_WORK_UNITS: "24"
54
- REFUSE_MARKER: "<!-- agent-refine-refused -->"
46
+ # No size gate. Refine and implement consume the issue identically (load-issue-context ->
47
+ # ISSUE_CONTEXT_PATH JSON, read in the prompt), so there is no reason to gate one on the
48
+ # issue's size and not the other. The failure the gate once existed for -- Pliny-Bot #305,
49
+ # 13,398 chars, the model writing one giant refined body and dying at the provider's 32K
50
+ # single-response cap -- is bounded on the OUTPUT side by the split mechanism
51
+ # (estimate >= 8 -> write <= MAX_SPLIT_CHILDREN small child bodies, one per turn), the
52
+ # prompt's "tighten the body" rule, and max-turns + max-ai-credits. None of that depends
53
+ # on the INPUT size. Keeping a finite limit just re-created the asymmetry that mis-routed
54
+ # Bots #1357 (a 23,388-char UX contract) to implement, which read it as nothing-to-do:
55
+ # refine refused it, so the router sent it where large issues were accepted. Removed
56
+ # entirely at 1.15.0; the measure-issue-size action stays for the audit workers, whose
57
+ # own AUDIT_MAX_* limits are about how they file, not about how refine reads.
55
58
  # Ten rather than six: the failure that prompted this took three minutes, and a slower one on a
56
59
  # worse day would fall outside a six-minute window and park for a fault that clears by itself.
57
60
  RETRY_UNDER_MINUTES: "10"
@@ -131,7 +134,7 @@ on:
131
134
  required: false
132
135
  type: string
133
136
  default: first
134
- needs: [still_open, size_guard, eligibility]
137
+ needs: [still_open, eligibility]
135
138
 
136
139
  jobs:
137
140
  # A route dispatched while the issue was open must not execute after it has been closed. The
@@ -205,99 +208,11 @@ jobs:
205
208
  exit 0
206
209
  fi
207
210
  echo "eligible=true" >> "$GITHUB_OUTPUT"
208
- # The size gate, rung 4. Measures the issue before any model starts, because an oversized
209
- # body does not fail fast on its own: it produces an agent that explores for seventeen
210
- # minutes and then dies mid-generation at the provider's response cap having written
211
- # nothing, which reads as a green run with no outcome (Pliny-Bot #305, twice). No
212
- # model, one issue read; over either limit it is a refusal, not a smaller attempt.
213
- size_guard:
211
+ reserve:
214
212
  needs: [still_open, eligibility]
215
213
  if: needs.still_open.outputs.open == 'true' && needs.eligibility.outputs.eligible == 'true'
216
214
  runs-on: agents-arc
217
215
  timeout-minutes: 5
218
- permissions:
219
- contents: read
220
- issues: read
221
- outputs:
222
- too_big: ${{ steps.measure.outputs.too-big }}
223
- reason: ${{ steps.measure.outputs.reason }}
224
- steps:
225
- - name: Checkout workflow actions
226
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
227
- with:
228
- persist-credentials: false
229
- # The rule lives in issue-size.cjs, which the audit also runs before it files, so the two
230
- # cannot drift: every audit report from 2026-09-21 on was refused here for want of it.
231
- - name: Measure the issue against the size limits
232
- id: measure
233
- uses: ./.github/actions/measure-issue-size
234
- with:
235
- token: ${{ github.token }}
236
- issue-number: ${{ inputs.issue-number }}
237
- max-body-chars: ${{ env.REFINE_MAX_BODY_CHARS }}
238
- max-work-units: ${{ env.REFINE_MAX_WORK_UNITS }}
239
- # The deterministic refusal. Runs only when the size gate tripped, so it costs one
240
- # comment and no model. The refine label stays: a human who shrinks the body and
241
- # replies re-enters rerefine through the comment route, and the classifier ignores
242
- # bot comments, so this comment cannot re-trigger the worker.
243
- refuse_big_issue:
244
- needs: [still_open, size_guard, eligibility]
245
- if: needs.still_open.outputs.open == 'true' && needs.eligibility.outputs.eligible == 'true' && needs.size_guard.outputs.too_big == 'true'
246
- runs-on: agents-arc
247
- timeout-minutes: 5
248
- permissions:
249
- contents: read
250
- issues: write
251
- steps:
252
- - name: Checkout workflow actions
253
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
254
- with:
255
- persist-credentials: false
256
- - name: Create bot token
257
- id: app-token
258
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
259
- with:
260
- client-id: ${{ secrets.BOT_APP_ID }}
261
- private-key: ${{ secrets.BOT_PRIVATE_KEY }}
262
- - name: Refuse the oversized issue
263
- uses: ./.github/actions/create-issue-comment
264
- with:
265
- token: ${{ steps.app-token.outputs.token }}
266
- issue-number: ${{ inputs.issue-number }}
267
- body: |
268
- ${{ env.REFUSE_MARKER }}
269
- This issue is too large to refine automatically: ${{ needs.size_guard.outputs.reason }}.
270
- A story this size does not fail fast; it burns a full agent run and dies mid-generation at the provider's response cap, producing nothing.
271
-
272
- Split it into smaller issues, each describing one story, or edit this body down under the limits (at most ${{ env.REFINE_MAX_BODY_CHARS }} characters and ${{ env.REFINE_MAX_WORK_UNITS }} work-unit bullets), then reply here and refinement will run again.
273
- - name: Flag the issue for a person
274
- uses: ./.github/actions/add-issue-labels
275
- with:
276
- token: ${{ steps.app-token.outputs.token }}
277
- issue-number: ${{ inputs.issue-number }}
278
- labels: review
279
- - name: Clear a stale stalled flag
280
- uses: ./.github/actions/remove-issue-labels
281
- with:
282
- token: ${{ steps.app-token.outputs.token }}
283
- issue-number: ${{ inputs.issue-number }}
284
- labels: stalled
285
- # A refusal must also release the reservation. authorize-bot-work adds bot-working
286
- # before the router dispatches, and this job is the refused run's only terminal path:
287
- # incomplete is gated off it (see there), so without this removal a refused issue
288
- # stays reserved with no run behind it -- parked, invisible, not re-triggerable until
289
- # the hourly reconcile sweep clears it hours later.
290
- - name: Release the reservation
291
- uses: ./.github/actions/remove-issue-labels
292
- with:
293
- token: ${{ steps.app-token.outputs.token }}
294
- issue-number: ${{ inputs.issue-number }}
295
- labels: ${{ env.WORKING_LABEL }}
296
- reserve:
297
- needs: [still_open, size_guard, eligibility]
298
- if: needs.still_open.outputs.open == 'true' && needs.eligibility.outputs.eligible == 'true' && needs.size_guard.outputs.too_big != 'true'
299
- runs-on: agents-arc
300
- timeout-minutes: 5
301
216
  permissions:
302
217
  contents: read
303
218
  issues: write
@@ -369,9 +284,10 @@ jobs:
369
284
  conclude:
370
285
  needs: [activation, agent, safe_outputs, validate_output]
371
286
  # `always()` is load-bearing: without it GitHub prepends `success()`, which returns false
372
- # if any needed job was skipped. If activation was skipped (Pliny-Bot #321: refuse_big_issue
373
- # skip-poisoned activation in v0.28.0), both conclude AND incomplete skipped and the refine
374
- # label never transitioned. The explicit conditions below are still the guard; `always()`
287
+ # if any needed job was skipped. The historical trigger was the size gate's refuse_big_issue
288
+ # skip-poisoning activation (Pliny-Bot #321) -- that gate is gone at 1.15.0, but a skip in
289
+ # any need still short-circuits, so `always()` plus the explicit conditions below remain
290
+ # the guard. The explicit conditions below are still the guard; `always()`
375
291
  # just prevents a skipped need from short-circuiting the whole expression.
376
292
  if: >
377
293
  always() &&
@@ -672,16 +588,10 @@ jobs:
672
588
  incomplete:
673
589
  # activation for the artifact prefix the usage read needs; validate_output for its
674
590
  # valid output, which decides whether the usage read should look for truncation at all.
675
- # size_guard because a refusal is terminal: when too_big is true, refuse_big_issue has
676
- # already spoken on the issue and no attempt, retry or park may follow. Without this
677
- # clause every refused issue also looped here -- one human-label visit produced eight
678
- # refusal comments and eight "Attempt N of 5" comments, none of which could succeed
679
- # (Pliny-Bot #322).
680
- needs: [activation, agent, safe_outputs, validate_output, size_guard, eligibility]
591
+ needs: [activation, agent, safe_outputs, validate_output, eligibility]
681
592
  if: >
682
593
  always() &&
683
594
  needs.eligibility.outputs.eligible == 'true' &&
684
- needs.size_guard.outputs.too_big != 'true' &&
685
595
  (
686
596
  needs.agent.result != 'success' ||
687
597
  needs.safe_outputs.result != 'success' ||
@@ -819,15 +729,16 @@ jobs:
819
729
  ${{ steps.usage.outputs.truncated }}
820
730
  [View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
821
731
 
822
- # `!failure()` is load-bearing, not decoration. gh-aw adds every custom job to the agent's
823
- # `needs`, including refuse_big_issue -- a job that skips precisely when refinement should
824
- # run. Without a status function in this `if:` GitHub prepends an implicit `success()`,
825
- # and a skipped need fails it, so every under-limit issue skipped the agent in 0s and the
826
- # incomplete loop parked the issue (Pliny-Bot #320-322, 16 runs). `!failure()` suppresses
827
- # the implicit success(): a refused skip no longer poisons the agent, while any real
828
- # failure -- size_guard erroring, the refusal job erroring mid-refuse, reserve failing --
829
- # still blocks it and lands in incomplete.
830
- if: inputs.issue-number != '' && needs.still_open.outputs.open == 'true' && needs.eligibility.outputs.eligible == 'true' && needs.size_guard.outputs.too_big != 'true' && !failure()
732
+ # `!failure()` is load-bearing, not decoration. `still_open` and `eligibility` skip
733
+ # precisely when refinement should not run (issue closed, parked for review). Without a
734
+ # status function in this `if:` GitHub prepends an implicit `success()`, and a skipped
735
+ # need fails it, so every closed-or-reviewed issue skipped the agent in 0s and the
736
+ # incomplete loop parked it. `!failure()` suppresses the implicit success(): a
737
+ # skipped gate no longer poisons the agent, while any real failure -- eligibility
738
+ # erroring, reserve failing -- still blocks it and lands in incomplete. (The original
739
+ # motivation was the size gate's refuse_big_issue skip; that gate is gone at 1.15.0,
740
+ # but the same skip shape applies to still_open and eligibility, so the guard stays.)
741
+ if: inputs.issue-number != '' && needs.still_open.outputs.open == 'true' && needs.eligibility.outputs.eligible == 'true' && !failure()
831
742
 
832
743
  runs-on: agents-arc
833
744
  runs-on-slim: agents-arc
@@ -1,53 +1,53 @@
1
- ---
2
- # Managed by @plainconceptsplatform/workflows. Source: loops/workflows/shared/platform-defaults.md. Update with `workflows update --force`; consumer edits may be overwritten.
3
- description: Shared network and safe-output defaults for catalog agent workflows, plus the verification rules every worker that builds or pushes has to follow. The body below is runtime-imported into each worker's prompt ahead of its own steps.
4
-
5
- network:
6
- allowed:
7
- - defaults
8
- - forge.plainconcepts.com
9
- # opencode fetches its model registry from models.opencode.ai at startup; without
10
- # this host on the allowlist the fetch blocks bootstrap and the run stalls after
11
- # "init" until the runner kills it. The apex "opencode.ai" is not enough.
12
- - models.opencode.ai
13
- - node
14
- - github
15
- - dotnet
16
- - fonts
17
- - login.microsoftonline.com
18
- # centralised AgentMemory (App Service); the MCP shim needs it through the egress proxy
19
- - agentmemory-pro-01.azurewebsites.net
20
-
21
- safe-outputs:
22
- threat-detection: false
23
- ---
24
-
25
- ## These runs are not interactive
26
-
27
- - Nothing resumes you. A turn that ends with text and no tool call ends the whole run, and the work
28
- is discarded, whatever the text promised. "I'll read the context files first." as a last message
29
- is a finished run: the Pliny-Bot merge gate did exactly that on #801, ended in twenty-three
30
- seconds with exit code 0 and no outcome, and was retried twice before the pull request was handed
31
- to a person for nothing.
32
- - So every message you write carries a tool call until the last one, which is the safe-output call
33
- the task names. Say what you are about to do and do it in the same message. Never announce a step
34
- and stop, never wait for a command, a reviewer or a "next turn".
35
- - A command the bash tool reports as moved to the background is still running: poll its output file
36
- with tool calls (`sleep 40; tail -n 15 <file>`) until it shows an exit status, then go on.
37
-
38
- ## Verification, for every worker that builds or pushes
39
-
40
- - Scope every check to the files you changed. This is the runner, not a preference: a
41
- whole-repository lint, build or test run exhausts its memory and gets killed mid-run, which
42
- fails the job with no useful output. Pass the changed paths to the linter (`pnpm exec biome
43
- check <files>`), build only the projects containing them, and run only the test project that
44
- covers them. Escalate to the full suite only after the scoped run passes and only when the
45
- change crosses a project boundary.
46
- - Run nothing at all for a change that touches only documentation. A cold Release build on a
47
- shared runner is minutes the run does not get back.
48
- - Never make a check pass by weakening it: not a deleted test, not a lowered threshold, not a
49
- skipped step. Fix the cause and run it again.
50
- - Once the checks pass, run the project's lint fix command over the files you changed
51
- (`pnpm lint:fix`, `pnpm exec biome check --write <changed-files>`, or its equivalent), or
52
- correct what lint reports where no fix command exists. A branch that arrives with lint errors
53
- gets sent back for them.
1
+ ---
2
+ # Managed by @plainconceptsplatform/workflows. Source: loops/workflows/shared/platform-defaults.md. Update with `workflows update --force`; consumer edits may be overwritten.
3
+ description: Shared network and safe-output defaults for catalog agent workflows, plus the verification rules every worker that builds or pushes has to follow. The body below is runtime-imported into each worker's prompt ahead of its own steps.
4
+
5
+ network:
6
+ allowed:
7
+ - defaults
8
+ - forge.plainconcepts.com
9
+ # opencode fetches its model registry from models.opencode.ai at startup; without
10
+ # this host on the allowlist the fetch blocks bootstrap and the run stalls after
11
+ # "init" until the runner kills it. The apex "opencode.ai" is not enough.
12
+ - models.opencode.ai
13
+ - node
14
+ - github
15
+ - dotnet
16
+ - fonts
17
+ - login.microsoftonline.com
18
+ # centralised AgentMemory (App Service); the MCP shim needs it through the egress proxy
19
+ - agentmemory-pro-01.azurewebsites.net
20
+
21
+ safe-outputs:
22
+ threat-detection: false
23
+ ---
24
+
25
+ ## These runs are not interactive
26
+
27
+ - Nothing resumes you. A turn that ends with text and no tool call ends the whole run, and the work
28
+ is discarded, whatever the text promised. "I'll read the context files first." as a last message
29
+ is a finished run: the Pliny-Bot merge gate did exactly that on #801, ended in twenty-three
30
+ seconds with exit code 0 and no outcome, and was retried twice before the pull request was handed
31
+ to a person for nothing.
32
+ - So every message you write carries a tool call until the last one, which is the safe-output call
33
+ the task names. Say what you are about to do and do it in the same message. Never announce a step
34
+ and stop, never wait for a command, a reviewer or a "next turn".
35
+ - A command the bash tool reports as moved to the background is still running: poll its output file
36
+ with tool calls (`sleep 40; tail -n 15 <file>`) until it shows an exit status, then go on.
37
+
38
+ ## Verification, for every worker that builds or pushes
39
+
40
+ - Scope every check to the files you changed. This is the runner, not a preference: a
41
+ whole-repository lint, build or test run exhausts its memory and gets killed mid-run, which
42
+ fails the job with no useful output. Pass the changed paths to the linter (`pnpm exec biome
43
+ check <files>`), build only the projects containing them, and run only the test project that
44
+ covers them. Escalate to the full suite only after the scoped run passes and only when the
45
+ change crosses a project boundary.
46
+ - Run nothing at all for a change that touches only documentation. A cold Release build on a
47
+ shared runner is minutes the run does not get back.
48
+ - Never make a check pass by weakening it: not a deleted test, not a lowered threshold, not a
49
+ skipped step. Fix the cause and run it again.
50
+ - Once the checks pass, run the project's lint fix command over the files you changed
51
+ (`pnpm lint:fix`, `pnpm exec biome check --write <changed-files>`, or its equivalent), or
52
+ correct what lint reports where no fix command exists. A branch that arrives with lint errors
53
+ gets sent back for them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plainconceptsplatform/workflows",
3
- "version": "1.14.1",
3
+ "version": "1.16.1",
4
4
  "description": "Install and update Platform GitHub agentic workflows.",
5
5
  "keywords": [
6
6
  "github-actions",