@plainconceptsplatform/workflows 0.4.34 → 0.5.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.
- package/README.md +88 -88
- package/loops/actions/agent-output.cjs +17 -17
- package/loops/actions/audit-close/action.yml +24 -0
- package/loops/actions/classify-route/action.yml +1 -3
- package/loops/actions/classify-route/classify-route.sh +40 -35
- package/loops/actions/update-changelog/action.yml +113 -0
- package/loops/actions/validate-merge-gate-output/action.yml +40 -40
- package/loops/actions/validate-refine-output/validate-refine-output.sh +16 -3
- package/loops/actions/validate-review-output/action.yml +35 -35
- package/loops/actions/validate-triage-output/action.yml +36 -36
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +91 -36
- package/loops/scripts/compile-agent-workflows.mjs +195 -6
- package/loops/templates/agentics/agentics-checks.yml +86 -86
- package/loops/templates/agentics/agentics-maintenance.yml +121 -121
- package/loops/templates/ci/app-ci-dotnet-next.yml +268 -171
- package/loops/templates/ci/app-ci-node-monorepo.yml +208 -178
- package/loops/templates/issues/bug_report.yml +109 -109
- package/loops/templates/issues/feature_request.yml +75 -75
- package/loops/templates/opencode/opencode.ci.json +49 -47
- package/loops/templates/opencode/opencode.ci.json.md +46 -41
- package/loops/templates/release/github-release.yml +30 -30
- package/loops/workflows/agent-apply-review.md +465 -443
- package/loops/workflows/agent-audit.md +213 -197
- package/loops/workflows/agent-implement.md +538 -414
- package/loops/workflows/agent-merge-gate.md +730 -584
- package/loops/workflows/agent-refine.md +609 -418
- package/loops/workflows/agent-release.md +258 -0
- package/loops/workflows/agent-triage.md +447 -439
- package/loops/workflows/authorize-bot-work.yml +85 -82
- package/loops/workflows/shared/opencode-ci.md +206 -197
- package/loops/workflows/shared/platform-defaults.md +19 -16
- package/loops/workflows/work-router.yml +862 -632
- package/package.json +7 -8
- package/dist/action-validation.test.d.ts +0 -1
- package/dist/action-validation.test.js +0 -84
- package/dist/catalog-installation.d.ts +0 -30
- package/dist/catalog-installation.js +0 -379
- package/dist/catalog-installation.test.d.ts +0 -1
- package/dist/catalog-installation.test.js +0 -448
- package/dist/catalog-listing.d.ts +0 -13
- package/dist/catalog-listing.js +0 -70
- package/dist/catalog-listing.test.d.ts +0 -1
- package/dist/catalog-listing.test.js +0 -150
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -218
- package/dist/index.test.d.ts +0 -1
- package/dist/index.test.js +0 -273
- package/dist/repository-inspection.d.ts +0 -23
- package/dist/repository-inspection.js +0 -113
- package/dist/repository-inspection.test.d.ts +0 -1
- package/dist/repository-inspection.test.js +0 -77
- package/dist/repository-state.d.ts +0 -18
- package/dist/repository-state.js +0 -77
- package/dist/repository-state.test.d.ts +0 -1
- package/dist/repository-state.test.js +0 -96
- package/dist/route-processing.d.ts +0 -6
- package/dist/route-processing.js +0 -150
- package/dist/route-processing.test.d.ts +0 -1
- package/dist/route-processing.test.js +0 -350
- package/dist/stack-defaults.d.ts +0 -11
- package/dist/stack-defaults.js +0 -104
- package/dist/stack-defaults.test.d.ts +0 -1
- package/dist/stack-defaults.test.js +0 -266
- package/dist/tui.d.ts +0 -25
- package/dist/tui.js +0 -287
- package/dist/tui.test.d.ts +0 -1
- package/dist/tui.test.js +0 -246
- package/dist/workflow-catalog.d.ts +0 -25
- package/dist/workflow-catalog.js +0 -59
- package/dist/workflow-catalog.test.d.ts +0 -1
- package/dist/workflow-catalog.test.js +0 -29
- package/loops/workflows/agent-direct.md +0 -375
- package/loops/workflows/agent-propose.md +0 -342
|
@@ -1,443 +1,465 @@
|
|
|
1
|
-
---
|
|
2
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-apply-review.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
|
-
env:
|
|
4
|
-
REPO_RULES: "Apply only actionable outstanding reviewer feedback to the selected bot pull request. Make minimal changes that address each comment. Preserve architecture and do not weaken tests. Run full verification after changes."
|
|
5
|
-
WORKING_LABEL: bot-working
|
|
6
|
-
REVIEW_LABEL: review
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- shared/
|
|
29
|
-
- shared/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
echo "$
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| grep -
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
echo "
|
|
108
|
-
echo "
|
|
109
|
-
echo "
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
needs.
|
|
188
|
-
needs.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
${{ env.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
runs-on
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
max-
|
|
295
|
-
max-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
gh
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
${{ env.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
1
|
+
---
|
|
2
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-apply-review.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
|
+
env:
|
|
4
|
+
REPO_RULES: "Apply only actionable outstanding reviewer feedback to the selected bot pull request. Make minimal changes that address each comment. Preserve architecture and do not weaken tests. Run full verification after changes."
|
|
5
|
+
WORKING_LABEL: bot-working
|
|
6
|
+
REVIEW_LABEL: review
|
|
7
|
+
PR_PENDING_LABEL: pr-pending
|
|
8
|
+
REVIEW_MARKER: "<!-- agent-apply-review -->"
|
|
9
|
+
INCOMPLETE_COMMENT: "Automated review feedback ended without an outcome. The issue remains for a retry."
|
|
10
|
+
ISSUE_CONTEXT_PATH: /tmp/gh-aw/agent/issue-context.json
|
|
11
|
+
GH_AW_ALLOWED_BOTS: "platform-devbox[bot],github-actions[bot]"
|
|
12
|
+
GIT_AUTHOR_NAME: "github-actions[bot]"
|
|
13
|
+
GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com"
|
|
14
|
+
GIT_COMMITTER_NAME: "github-actions[bot]"
|
|
15
|
+
GIT_COMMITTER_EMAIL: "github-actions[bot]@users.noreply.github.com"
|
|
16
|
+
description: |
|
|
17
|
+
Applies reviewer feedback to an open pull request the bot authored, then pushes the fixes to
|
|
18
|
+
the same branch. Called by the Work Router; does not trigger on public events.
|
|
19
|
+
|
|
20
|
+
`target: triggering` is enough for the push here, so unlike the merge gate this needs no
|
|
21
|
+
wildcard fetch: the event is the pull request and gh-aw has already checked out its head.
|
|
22
|
+
|
|
23
|
+
name: "Agent: Apply Review"
|
|
24
|
+
|
|
25
|
+
# Router-only worker. The Work Router owns triggers, classification, and rung 1-2 checks.
|
|
26
|
+
# This workflow receives the classified inputs and runs rung 3+.
|
|
27
|
+
imports:
|
|
28
|
+
- github/gh-aw/.github/workflows/shared/opencode.md@v0.87.5
|
|
29
|
+
- shared/platform-defaults.md
|
|
30
|
+
- shared/opencode-ci.md
|
|
31
|
+
|
|
32
|
+
on:
|
|
33
|
+
workflow_call:
|
|
34
|
+
inputs:
|
|
35
|
+
pr-number:
|
|
36
|
+
description: Pull request number to apply review feedback on.
|
|
37
|
+
required: true
|
|
38
|
+
type: string
|
|
39
|
+
linked-issue:
|
|
40
|
+
description: Issue number the pull request closes. May be empty.
|
|
41
|
+
required: false
|
|
42
|
+
type: string
|
|
43
|
+
|
|
44
|
+
# Rung 4. Router has classified the event; this job validates PR ownership and checks for
|
|
45
|
+
# substantive feedback. A custom job, not `on.steps`, because the prompt needs these values.
|
|
46
|
+
jobs:
|
|
47
|
+
subject:
|
|
48
|
+
runs-on: agents-arc
|
|
49
|
+
permissions:
|
|
50
|
+
contents: read
|
|
51
|
+
issues: read
|
|
52
|
+
pull-requests: read
|
|
53
|
+
outputs:
|
|
54
|
+
found: ${{ steps.subject.outputs.found }}
|
|
55
|
+
pr: ${{ steps.subject.outputs.pr }}
|
|
56
|
+
issue: ${{ steps.subject.outputs.issue }}
|
|
57
|
+
unresolved: ${{ steps.subject.outputs.unresolved }}
|
|
58
|
+
steps:
|
|
59
|
+
- name: Confirm this is our pull request and the feedback is substantive
|
|
60
|
+
id: subject
|
|
61
|
+
env:
|
|
62
|
+
GH_TOKEN: ${{ github.token }}
|
|
63
|
+
REPO: ${{ github.repository }}
|
|
64
|
+
PR: ${{ inputs.pr-number }}
|
|
65
|
+
LINKED_ISSUE: ${{ inputs.linked-issue }}
|
|
66
|
+
run: |
|
|
67
|
+
set -euo pipefail
|
|
68
|
+
|
|
69
|
+
none() {
|
|
70
|
+
echo "found=false" >> "$GITHUB_OUTPUT"
|
|
71
|
+
echo "$1"
|
|
72
|
+
exit 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pr=$(gh pr view "$PR" --repo "$REPO" --json number,state,author,title,body)
|
|
76
|
+
[ "$(printf '%s' "$pr" | jq -r '.state')" = "OPEN" ] || none "PR #$PR is not open"
|
|
77
|
+
|
|
78
|
+
# Use GitHub's bot flag because App login formats differ across APIs.
|
|
79
|
+
author=$(printf '%s' "$pr" | jq -r '.author.login')
|
|
80
|
+
is_bot=$(printf '%s' "$pr" | jq -r '.author.is_bot')
|
|
81
|
+
[ "$is_bot" = "true" ] || \
|
|
82
|
+
none "PR #$PR was authored by $author, a human. Never push to a human's branch."
|
|
83
|
+
|
|
84
|
+
# The issue this pull request closes, preferring the router-provided linked issue.
|
|
85
|
+
if [ -n "$LINKED_ISSUE" ]; then
|
|
86
|
+
issue="$LINKED_ISSUE"
|
|
87
|
+
else
|
|
88
|
+
issue=$(printf '%s' "$pr" | jq -r '.body // ""' \
|
|
89
|
+
| grep -oiE '(close[sd]?|fixe?[sd]?|resolve[sd]?) +#[0-9]+' \
|
|
90
|
+
| grep -oE '[0-9]+' | head -n 1 || true)
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Unresolved review threads, from the API rather than from the model's reading of it.
|
|
94
|
+
unresolved=$(gh api graphql -f query='
|
|
95
|
+
query($owner:String!, $name:String!, $number:Int!) {
|
|
96
|
+
repository(owner:$owner, name:$name) {
|
|
97
|
+
pullRequest(number:$number) {
|
|
98
|
+
reviewThreads(first:100) { nodes { isResolved isOutdated } }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}' \
|
|
102
|
+
-f owner="${REPO%/*}" -f name="${REPO#*/}" -F number="$PR" \
|
|
103
|
+
--jq '[.data.repository.pullRequest.reviewThreads.nodes[]
|
|
104
|
+
| select(.isResolved == false and .isOutdated == false)] | length')
|
|
105
|
+
|
|
106
|
+
{
|
|
107
|
+
echo "found=true"
|
|
108
|
+
echo "pr=$PR"
|
|
109
|
+
echo "issue=${issue:-}"
|
|
110
|
+
echo "unresolved=$unresolved"
|
|
111
|
+
} >> "$GITHUB_OUTPUT"
|
|
112
|
+
echo "PR #$PR has $unresolved unresolved thread(s)"
|
|
113
|
+
|
|
114
|
+
reserve:
|
|
115
|
+
needs: subject
|
|
116
|
+
if: needs.subject.outputs.found == 'true' && needs.subject.outputs.issue != ''
|
|
117
|
+
runs-on: agents-arc
|
|
118
|
+
permissions:
|
|
119
|
+
contents: read
|
|
120
|
+
issues: write
|
|
121
|
+
steps:
|
|
122
|
+
- name: Checkout workflow actions
|
|
123
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
124
|
+
with:
|
|
125
|
+
persist-credentials: false
|
|
126
|
+
- name: Create bot token
|
|
127
|
+
id: app-token
|
|
128
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
129
|
+
with:
|
|
130
|
+
client-id: ${{ secrets.BOT_APP_ID }}
|
|
131
|
+
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
132
|
+
- name: Mark the linked issue as in progress
|
|
133
|
+
uses: ./.github/actions/add-issue-labels
|
|
134
|
+
with:
|
|
135
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
136
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
137
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
138
|
+
- name: Clear the human-needed flag
|
|
139
|
+
uses: ./.github/actions/remove-issue-labels
|
|
140
|
+
with:
|
|
141
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
142
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
143
|
+
labels: ${{ env.REVIEW_LABEL }}
|
|
144
|
+
validate_output:
|
|
145
|
+
needs: [activation, subject, agent, safe_outputs]
|
|
146
|
+
if: always() && needs.agent.result == 'success' && needs.safe_outputs.result == 'success'
|
|
147
|
+
runs-on: agents-arc
|
|
148
|
+
permissions:
|
|
149
|
+
contents: read
|
|
150
|
+
pull-requests: read
|
|
151
|
+
outputs:
|
|
152
|
+
valid: ${{ steps.validate.outputs.valid }}
|
|
153
|
+
outcome: ${{ steps.validate.outputs.outcome }}
|
|
154
|
+
steps:
|
|
155
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
156
|
+
with:
|
|
157
|
+
persist-credentials: false
|
|
158
|
+
- id: output
|
|
159
|
+
uses: ./.github/actions/download-agent-output
|
|
160
|
+
with:
|
|
161
|
+
artifact-name: ${{ needs.activation.outputs.artifact_prefix }}agent
|
|
162
|
+
- name: Load unresolved review threads
|
|
163
|
+
env:
|
|
164
|
+
GH_TOKEN: ${{ github.token }}
|
|
165
|
+
REPO: ${{ github.repository }}
|
|
166
|
+
PR: ${{ needs.subject.outputs.pr }}
|
|
167
|
+
run: |
|
|
168
|
+
set -euo pipefail
|
|
169
|
+
gh api graphql -f query='
|
|
170
|
+
query($owner:String!, $name:String!, $number:Int!) {
|
|
171
|
+
repository(owner:$owner, name:$name) {
|
|
172
|
+
pullRequest(number:$number) {
|
|
173
|
+
reviewThreads(first:100) { nodes { id isResolved isOutdated } }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}' -f owner="${REPO%/*}" -f name="${REPO#*/}" -F number="$PR" \
|
|
177
|
+
--jq '.data.repository.pullRequest.reviewThreads.nodes' > /tmp/review-threads.json
|
|
178
|
+
- id: validate
|
|
179
|
+
uses: ./.github/actions/validate-review-output
|
|
180
|
+
with:
|
|
181
|
+
output-file: ${{ steps.output.outputs.output-file }}
|
|
182
|
+
pr-number: ${{ needs.subject.outputs.pr }}
|
|
183
|
+
review-threads-file: /tmp/review-threads.json
|
|
184
|
+
conclude:
|
|
185
|
+
needs: [activation, subject, agent, safe_outputs, validate_output]
|
|
186
|
+
if: >
|
|
187
|
+
needs.agent.result == 'success' &&
|
|
188
|
+
needs.safe_outputs.result == 'success' &&
|
|
189
|
+
needs.validate_output.outputs.valid == 'true'
|
|
190
|
+
runs-on: agents-arc
|
|
191
|
+
permissions:
|
|
192
|
+
contents: write
|
|
193
|
+
issues: write
|
|
194
|
+
pull-requests: write
|
|
195
|
+
steps:
|
|
196
|
+
- name: Create bot token
|
|
197
|
+
id: app-token
|
|
198
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
199
|
+
with:
|
|
200
|
+
client-id: ${{ secrets.BOT_APP_ID }}
|
|
201
|
+
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
202
|
+
- name: Checkout repository
|
|
203
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
204
|
+
with:
|
|
205
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
206
|
+
fetch-depth: 0
|
|
207
|
+
- name: Apply agent output
|
|
208
|
+
uses: ./.github/actions/apply-agent-output
|
|
209
|
+
with:
|
|
210
|
+
artifact-name: ${{ needs.activation.outputs.artifact_prefix }}agent
|
|
211
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
212
|
+
push-to-branch: 'true'
|
|
213
|
+
apply-labels: 'false'
|
|
214
|
+
- name: Release implemented review
|
|
215
|
+
if: needs.validate_output.outputs.outcome == 'implemented'
|
|
216
|
+
uses: ./.github/actions/remove-issue-labels
|
|
217
|
+
with:
|
|
218
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
219
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
220
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
221
|
+
- name: Flag review outcome
|
|
222
|
+
if: needs.validate_output.outputs.outcome != 'implemented'
|
|
223
|
+
uses: ./.github/actions/add-issue-labels
|
|
224
|
+
with:
|
|
225
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
226
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
227
|
+
labels: ${{ env.REVIEW_LABEL }}
|
|
228
|
+
- name: Release review outcome
|
|
229
|
+
if: needs.validate_output.outputs.outcome != 'implemented'
|
|
230
|
+
uses: ./.github/actions/remove-issue-labels
|
|
231
|
+
with:
|
|
232
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
233
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
234
|
+
labels: ${{ env.WORKING_LABEL }},${{ env.PR_PENDING_LABEL }}
|
|
235
|
+
incomplete:
|
|
236
|
+
needs: [subject, agent, safe_outputs, validate_output]
|
|
237
|
+
if: >
|
|
238
|
+
always() &&
|
|
239
|
+
needs.subject.outputs.found == 'true' &&
|
|
240
|
+
(needs.agent.result != 'success' || needs.safe_outputs.result != 'success' || needs.validate_output.outputs.valid != 'true')
|
|
241
|
+
runs-on: agents-arc
|
|
242
|
+
permissions:
|
|
243
|
+
contents: read
|
|
244
|
+
issues: write
|
|
245
|
+
steps:
|
|
246
|
+
- name: Checkout workflow actions
|
|
247
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
248
|
+
with:
|
|
249
|
+
persist-credentials: false
|
|
250
|
+
- name: Create bot token
|
|
251
|
+
id: app-token
|
|
252
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
253
|
+
with:
|
|
254
|
+
client-id: ${{ secrets.BOT_APP_ID }}
|
|
255
|
+
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
256
|
+
- name: Release the issue
|
|
257
|
+
uses: ./.github/actions/remove-issue-labels
|
|
258
|
+
with:
|
|
259
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
260
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
261
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
262
|
+
- name: Flag for human review
|
|
263
|
+
uses: ./.github/actions/add-issue-labels
|
|
264
|
+
with:
|
|
265
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
266
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
267
|
+
labels: ${{ env.REVIEW_LABEL }}
|
|
268
|
+
- name: Report missing review feedback outcome
|
|
269
|
+
uses: ./.github/actions/create-issue-comment
|
|
270
|
+
with:
|
|
271
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
272
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
273
|
+
body: |
|
|
274
|
+
${{ env.REVIEW_MARKER }}
|
|
275
|
+
${{ env.INCOMPLETE_COMMENT }}
|
|
276
|
+
[View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
|
277
|
+
|
|
278
|
+
if: needs.subject.outputs.found == 'true'
|
|
279
|
+
|
|
280
|
+
runs-on: agents-arc
|
|
281
|
+
runs-on-slim: agents-arc
|
|
282
|
+
|
|
283
|
+
secrets:
|
|
284
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
285
|
+
|
|
286
|
+
engine:
|
|
287
|
+
id: opencode
|
|
288
|
+
version: "1.2.14"
|
|
289
|
+
env:
|
|
290
|
+
OPENAI_BASE_URL: https://forge.plainconcepts.com/v1
|
|
291
|
+
|
|
292
|
+
model: openai/glm-5-3
|
|
293
|
+
|
|
294
|
+
max-turns: 300
|
|
295
|
+
max-turn-cache-misses: 3000
|
|
296
|
+
max-ai-credits: 5000
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
permissions: read-all
|
|
300
|
+
|
|
301
|
+
# Rung 3. A reviewer often makes one point across several comments, so the agent needs the
|
|
302
|
+
# whole conversation, not just the one that triggered it. Fetching all of it is deterministic.
|
|
303
|
+
steps:
|
|
304
|
+
- name: Load the issue context
|
|
305
|
+
uses: ./.github/actions/load-issue-context
|
|
306
|
+
with:
|
|
307
|
+
token: ${{ github.token }}
|
|
308
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
309
|
+
output-path: ${{ env.ISSUE_CONTEXT_PATH }}
|
|
310
|
+
- name: Fetch the whole review conversation and the diff
|
|
311
|
+
env:
|
|
312
|
+
GH_TOKEN: ${{ github.token }}
|
|
313
|
+
REPO: ${{ github.repository }}
|
|
314
|
+
PR: ${{ inputs.pr-number }}
|
|
315
|
+
run: |
|
|
316
|
+
set -euo pipefail
|
|
317
|
+
mkdir -p /tmp/gh-aw/agent
|
|
318
|
+
gh pr diff "$PR" --repo "$REPO" > /tmp/gh-aw/agent/diff.patch
|
|
319
|
+
gh api "repos/$REPO/pulls/$PR/comments" --paginate \
|
|
320
|
+
--jq '[.[] | {id, path, line, author: .user.login, body, in_reply_to_id}]' \
|
|
321
|
+
> /tmp/gh-aw/agent/review-comments.json
|
|
322
|
+
gh api "repos/$REPO/issues/$PR/comments" --paginate \
|
|
323
|
+
--jq '[.[] | {author: .user.login, body}]' \
|
|
324
|
+
> /tmp/gh-aw/agent/conversation.json
|
|
325
|
+
gh api graphql -f query='
|
|
326
|
+
query($owner:String!, $name:String!, $number:Int!) {
|
|
327
|
+
repository(owner:$owner, name:$name) {
|
|
328
|
+
pullRequest(number:$number) {
|
|
329
|
+
reviewThreads(first:100) {
|
|
330
|
+
nodes { id
|
|
331
|
+
isResolved isOutdated path
|
|
332
|
+
comments(first:50) { nodes { author { login } body } }
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}' \
|
|
338
|
+
-f owner="${REPO%/*}" -f name="${REPO#*/}" -F number="$PR" \
|
|
339
|
+
--jq '.data.repository.pullRequest.reviewThreads.nodes' \
|
|
340
|
+
> /tmp/gh-aw/agent/review-threads.json
|
|
341
|
+
|
|
342
|
+
safe-outputs:
|
|
343
|
+
# A failed run is already a red run. An issue per failure buries the real backlog
|
|
344
|
+
# under noise nobody closes.
|
|
345
|
+
report-failure-as-issue: false
|
|
346
|
+
threat-detection: false
|
|
347
|
+
# target "*" because these workers are dispatched, not triggered by the pull request:
|
|
348
|
+
# the default "triggering" target has no pull request in context and rejects the push
|
|
349
|
+
# with "requires pull request context", so the agent's fix is computed and discarded.
|
|
350
|
+
push-to-pull-request-branch:
|
|
351
|
+
target: "*"
|
|
352
|
+
add-comment:
|
|
353
|
+
target: "*"
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
timeout-minutes: 45
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
1. You are applying review feedback to pull request
|
|
360
|
+
**#${{ needs.subject.outputs.pr }}**, which has
|
|
361
|
+
**${{ needs.subject.outputs.unresolved }}** unresolved review thread(s).
|
|
362
|
+
|
|
363
|
+
It has already been confirmed that this pull request is open, that we authored it, and that
|
|
364
|
+
the reviewer has write access. Do not re-check any of that.
|
|
365
|
+
|
|
366
|
+
2. Read `${{ env.ISSUE_CONTEXT_PATH }}`. It contains the issue body and discussion this pull
|
|
367
|
+
request closes. The acceptance criteria there define what the implementation must still
|
|
368
|
+
satisfy after the feedback is applied.
|
|
369
|
+
|
|
370
|
+
3. Read the whole conversation, not just the comment that triggered you. It is already on disk:
|
|
371
|
+
|
|
372
|
+
- `/tmp/gh-aw/agent/review-threads.json` , every thread with its resolved and outdated state
|
|
373
|
+
- `/tmp/gh-aw/agent/review-comments.json` , every inline comment with its file and line
|
|
374
|
+
- `/tmp/gh-aw/agent/conversation.json` , every conversation comment
|
|
375
|
+
- `/tmp/gh-aw/agent/diff.patch` , the current diff
|
|
376
|
+
|
|
377
|
+
A reviewer often makes one point across several comments. Applying them one at a time
|
|
378
|
+
produces contradictory commits, so read everything before changing anything.
|
|
379
|
+
|
|
380
|
+
4. Work out which items are genuinely actionable and still outstanding. Skip anything already
|
|
381
|
+
addressed by a later commit, anything a bot wrote, anything from the pull request author,
|
|
382
|
+
and anything that is a question rather than a request. You must account for every unresolved
|
|
383
|
+
human review thread by its `PRRT_...` ID. Do not claim feedback was implemented when no branch
|
|
384
|
+
change is needed: that requires reviewer confirmation.
|
|
385
|
+
|
|
386
|
+
5. Load only skills required by the feedback, then apply it. Make only changes the feedback
|
|
387
|
+
justifies: a review comment is not licence for unrelated refactoring. Never read outside this
|
|
388
|
+
repository root. Follow repository documentation and established conventions. Keep changes
|
|
389
|
+
focused, protect secrets, and do not modify generated files unless the feedback requires it.
|
|
390
|
+
Adhere to ${{ env.REPO_RULES }}.
|
|
391
|
+
|
|
392
|
+
6. Run the repository verification commands below. The issue context at
|
|
393
|
+
`${{ env.ISSUE_CONTEXT_PATH }}` defines acceptance criteria the fix must satisfy. If a check
|
|
394
|
+
fails, fix what you broke and run it again. Do not push a branch that does not pass.
|
|
395
|
+
|
|
396
|
+
**Scoped verification.** This runner has limited memory, and a whole-repo lint or build
|
|
397
|
+
can be killed mid-run. Scope verification to the files you actually changed first, and
|
|
398
|
+
only escalate to the full suite when the scoped run passes and you are still unsure:
|
|
399
|
+
- Lint/format (biome, eslint, prettier, ruff, etc.): pass the changed file paths as
|
|
400
|
+
arguments so the tool checks only those files (e.g. `pnpm exec biome check <files>`),
|
|
401
|
+
never the whole repository.
|
|
402
|
+
- Build: prefer building only the project(s) containing the changed files; use the full
|
|
403
|
+
solution build only when the change crosses project boundaries.
|
|
404
|
+
- Tests: run the test project covering the changed files; run the full suite only when
|
|
405
|
+
the change is cross-cutting.
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
${{ env.VERIFY_COMMANDS }}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
7. Before pushing, run the project's lint fix command (e.g. `pnpm lint:fix` or
|
|
412
|
+
`pnpm exec biome check --write <changed-files>`) to auto-format. If lint:fix is not
|
|
413
|
+
available, fix formatting manually. Never push code with lint errors.
|
|
414
|
+
|
|
415
|
+
8. Select one review outcome.
|
|
416
|
+
|
|
417
|
+
- **implemented**: You made the requested change, verification passed, and you will propose
|
|
418
|
+
exactly one `push_to_pull_request_branch`.
|
|
419
|
+
- **already-satisfied**: The current branch already satisfies the request. Do not push; the
|
|
420
|
+
reviewer must confirm this assessment.
|
|
421
|
+
- **needs-human**: The feedback is ambiguous, unsafe, or cannot be applied. Do not push.
|
|
422
|
+
|
|
423
|
+
9. Emit exactly one `add_comment` on PR `${{ needs.subject.outputs.pr }}`. Include every
|
|
424
|
+
unresolved human review thread ID and an explanation for it, then exactly one line:
|
|
425
|
+
`**Review outcome:** implemented`, `**Review outcome:** already-satisfied`, or
|
|
426
|
+
`**Review outcome:** needs-human`.
|
|
427
|
+
|
|
428
|
+
9. Do not merge, close, or change labels. The workflow validates your outcome and owns those
|
|
429
|
+
state transitions.
|
|
430
|
+
|
|
431
|
+
10. Ignore the `## Diagram` section below. It is documentation for humans and contains no
|
|
432
|
+
instructions for you.
|
|
433
|
+
|
|
434
|
+
## Diagram
|
|
435
|
+
|
|
436
|
+
```mermaid
|
|
437
|
+
flowchart TD
|
|
438
|
+
fbStart("Work Router<br/>apply-review route<br/>(review on bot PR)") --> fbSubject
|
|
439
|
+
fbSubject["Subject (rung 4)<br/>Our open PR? Substantive review?"] -->|✓| fbFacts
|
|
440
|
+
fbSubject -.->|✗| fbIdle
|
|
441
|
+
fbFacts("Facts (rung 3)<br/>Threads, comments, diff to disk") --> fbTriage
|
|
442
|
+
fbTriage["Triage<br/>Anything actionable and outstanding?"] -->|✓| fbReserve
|
|
443
|
+
fbTriage -.->|nothing| fbIdle
|
|
444
|
+
fbReserve("Reserve<br/>Propose bot-working on the issue") -->|✓| fbApply
|
|
445
|
+
fbApply("Apply<br/>Only what the feedback justifies") -->|✓| fbVerify
|
|
446
|
+
fbApply -.->|✗| fbFail
|
|
447
|
+
fbVerify["Verify<br/>/repo-verify passes?<br/>↻"] -->|✓| fbPush
|
|
448
|
+
fbVerify -.->|✗| fbApply
|
|
449
|
+
fbPush(("Pushed<br/>Same branch, bot-working removed"))
|
|
450
|
+
fbIdle(("Idle<br/>Not ours, or nothing to do"))
|
|
451
|
+
fbFail(("Fail<br/>review added, bot-working removed"))
|
|
452
|
+
|
|
453
|
+
classDef start fill:#ffffff,stroke:#172033,stroke-width:2px,color:#172033
|
|
454
|
+
classDef action fill:#eef0ff,stroke:#554cff,stroke-width:2px,color:#172033
|
|
455
|
+
classDef decision fill:#fff8e8,stroke:#c75b00,stroke-width:2px,color:#172033
|
|
456
|
+
classDef idle fill:#202c40,stroke:#738198,stroke-width:2px,color:#ffffff
|
|
457
|
+
classDef failure fill:#fff0f0,stroke:#ef2929,stroke-width:2px,color:#8b1a1a
|
|
458
|
+
classDef success fill:#e8f8ec,stroke:#18883c,stroke-width:2px,color:#145a32
|
|
459
|
+
class fbStart start
|
|
460
|
+
class fbFacts,fbReserve,fbApply action
|
|
461
|
+
class fbSubject,fbTriage,fbVerify decision
|
|
462
|
+
class fbIdle idle
|
|
463
|
+
class fbFail failure
|
|
464
|
+
class fbPush success
|
|
465
|
+
```
|