@plainconceptsplatform/workflows 0.21.0 → 0.23.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.
- package/dist/index.js +0 -0
- package/loops/actions/classify-route/action.yml +100 -100
- package/loops/actions/classify-route/classify-route.sh +10 -9
- package/loops/actions/decide-agent-retry/action.yml +72 -0
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +78 -7
- package/loops/templates/ci/app-ci-dotnet-next.yml +9 -9
- package/loops/templates/ci/app-ci-node-monorepo.yml +11 -11
- package/loops/templates/release/github-release.yml +1 -1
- package/loops/workflows/agent-apply-review.md +65 -0
- package/loops/workflows/agent-merge-gate.md +38 -5
- package/loops/workflows/agent-refine.md +66 -0
- package/loops/workflows/agent-triage.md +66 -0
- package/loops/workflows/work-router.yml +5 -2
- package/package.json +8 -9
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/classify-route/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
name: Classify route
|
|
3
|
-
description: Classify the triggering event into exactly one route, and resolve the issue a gated pull request closes.
|
|
4
|
-
inputs:
|
|
5
|
-
token:
|
|
6
|
-
description: GitHub token with pull-requests read access.
|
|
7
|
-
required: true
|
|
8
|
-
outputs:
|
|
9
|
-
route:
|
|
10
|
-
description: The single route this event selects, or `none`.
|
|
11
|
-
value: ${{ steps.classify.outputs.route }}
|
|
12
|
-
issue-number:
|
|
13
|
-
description: Issue number the route operates on.
|
|
14
|
-
value: ${{ steps.classify.outputs.issue-number }}
|
|
15
|
-
pr-number:
|
|
16
|
-
description: Pull request number the route operates on.
|
|
17
|
-
value: ${{ steps.classify.outputs.pr-number }}
|
|
18
|
-
linked-issue:
|
|
19
|
-
description: Issue the pull request closes. Empty for routes that do not target one.
|
|
20
|
-
value: ${{ steps.linked-issue.outputs.issue }}
|
|
21
|
-
ci-conclusion:
|
|
22
|
-
description: Conclusion reported by the CI run that triggered a merge-gate route.
|
|
23
|
-
value: ${{ steps.classify.outputs.ci-conclusion }}
|
|
24
|
-
ci-run-id:
|
|
25
|
-
description: Workflow run ID of that CI run.
|
|
26
|
-
value: ${{ steps.classify.outputs.ci-run-id }}
|
|
27
|
-
merge-gate-attempts:
|
|
28
|
-
description: Failed merge-gate attempts already made against this CI verdict.
|
|
29
|
-
value: ${{ steps.classify.outputs.merge-gate-attempts }}
|
|
30
|
-
|
|
31
|
-
description:
|
|
32
|
-
value: ${{ steps.classify.outputs.
|
|
33
|
-
refine-mode:
|
|
34
|
-
description: first or rerefine.
|
|
35
|
-
value: ${{ steps.classify.outputs.refine-mode }}
|
|
36
|
-
triage-mode:
|
|
37
|
-
description: first or retriage.
|
|
38
|
-
value: ${{ steps.classify.outputs.triage-mode }}
|
|
39
|
-
trigger-kind:
|
|
40
|
-
description: scheduled or manual.
|
|
41
|
-
value: ${{ steps.classify.outputs.trigger-kind }}
|
|
42
|
-
runs:
|
|
43
|
-
using: composite
|
|
44
|
-
steps:
|
|
45
|
-
- name: Classify the event
|
|
46
|
-
id: classify
|
|
47
|
-
shell: bash
|
|
48
|
-
env:
|
|
49
|
-
EVENT: ${{ github.event_name }}
|
|
50
|
-
ACTION: ${{ github.event.action }}
|
|
51
|
-
LABEL: ${{ github.event.label.name }}
|
|
52
|
-
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
|
|
53
|
-
ISSUE_STATE: ${{ github.event.issue.state }}
|
|
54
|
-
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
55
|
-
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
56
|
-
COMMENT_ON_PR: ${{ github.event.issue.pull_request != '' }}
|
|
57
|
-
COMMENT_SENDER_TYPE: ${{ github.event.comment.user.type }}
|
|
58
|
-
ACTOR: ${{ github.actor }}
|
|
59
|
-
RUN_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
|
|
60
|
-
RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
|
61
|
-
RUN_ID: ${{ github.event.workflow_run.id }}
|
|
62
|
-
SCHEDULE: ${{ github.event.schedule }}
|
|
63
|
-
OPERATION: ${{ github.event.inputs.operation }}
|
|
64
|
-
INPUT_ISSUE_NUMBER: ${{ github.event.inputs.issue-number }}
|
|
65
|
-
INPUT_PR_NUMBER: ${{ github.event.inputs.pr-number }}
|
|
66
|
-
INPUT_MODE: ${{ github.event.inputs.mode }}
|
|
67
|
-
INPUT_CI_CONCLUSION: ${{ github.event.inputs.ci-conclusion }}
|
|
68
|
-
INPUT_CI_RUN_ID: ${{ github.event.inputs.ci-run-id }}
|
|
69
|
-
INPUT_ATTEMPTS_SO_FAR: ${{ github.event.inputs.attempts_so_far }}
|
|
70
|
-
INPUT_TRIGGER_KIND: ${{ github.event.inputs.trigger-kind }}
|
|
71
|
-
run: |
|
|
72
|
-
set -euo pipefail
|
|
73
|
-
|
|
74
|
-
bash "${GITHUB_ACTION_PATH}/classify-route.sh" | tee "$RUNNER_TEMP/route.env"
|
|
75
|
-
cat "$RUNNER_TEMP/route.env" >> "$GITHUB_OUTPUT"
|
|
76
|
-
|
|
77
|
-
route="$(sed -n 's/^route=//p' "$RUNNER_TEMP/route.env")"
|
|
78
|
-
reason="$(sed -n 's/^error=//p' "$RUNNER_TEMP/route.env")"
|
|
79
|
-
|
|
80
|
-
if [ "$route" = "none" ] && [ -n "$reason" ]; then
|
|
81
|
-
echo "::notice::No route for this event: $reason"
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
- name: Resolve the issue the pull request closes
|
|
85
|
-
id: linked-issue
|
|
86
|
-
if: steps.classify.outputs.route == 'merge-gate' || steps.classify.outputs.route == 'apply-review'
|
|
87
|
-
shell: bash
|
|
88
|
-
env:
|
|
89
|
-
GH_TOKEN: ${{ inputs.token }}
|
|
90
|
-
REPO: ${{ github.repository }}
|
|
91
|
-
PR_NUMBER: ${{ steps.classify.outputs.pr-number }}
|
|
92
|
-
run: |
|
|
93
|
-
set -euo pipefail
|
|
94
|
-
|
|
95
|
-
issue="$(gh pr view "$PR_NUMBER" --repo "$REPO" \
|
|
96
|
-
--json closingIssuesReferences \
|
|
97
|
-
--jq '.closingIssuesReferences[0].number // empty')"
|
|
98
|
-
|
|
99
|
-
echo "issue=${issue}" >> "$GITHUB_OUTPUT"
|
|
100
|
-
echo "Pull request #${PR_NUMBER} closes issue #${issue:-<none>}"
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/classify-route/action.yml. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
+
name: Classify route
|
|
3
|
+
description: Classify the triggering event into exactly one route, and resolve the issue a gated pull request closes.
|
|
4
|
+
inputs:
|
|
5
|
+
token:
|
|
6
|
+
description: GitHub token with pull-requests read access.
|
|
7
|
+
required: true
|
|
8
|
+
outputs:
|
|
9
|
+
route:
|
|
10
|
+
description: The single route this event selects, or `none`.
|
|
11
|
+
value: ${{ steps.classify.outputs.route }}
|
|
12
|
+
issue-number:
|
|
13
|
+
description: Issue number the route operates on.
|
|
14
|
+
value: ${{ steps.classify.outputs.issue-number }}
|
|
15
|
+
pr-number:
|
|
16
|
+
description: Pull request number the route operates on.
|
|
17
|
+
value: ${{ steps.classify.outputs.pr-number }}
|
|
18
|
+
linked-issue:
|
|
19
|
+
description: Issue the pull request closes. Empty for routes that do not target one.
|
|
20
|
+
value: ${{ steps.linked-issue.outputs.issue }}
|
|
21
|
+
ci-conclusion:
|
|
22
|
+
description: Conclusion reported by the CI run that triggered a merge-gate route.
|
|
23
|
+
value: ${{ steps.classify.outputs.ci-conclusion }}
|
|
24
|
+
ci-run-id:
|
|
25
|
+
description: Workflow run ID of that CI run.
|
|
26
|
+
value: ${{ steps.classify.outputs.ci-run-id }}
|
|
27
|
+
merge-gate-attempts:
|
|
28
|
+
description: Failed merge-gate attempts already made against this CI verdict.
|
|
29
|
+
value: ${{ steps.classify.outputs.merge-gate-attempts }}
|
|
30
|
+
attempts:
|
|
31
|
+
description: Runs already made for this piece of work that died before producing an answer. Carried by every worker that re-dispatches itself, so the budget is bounded.
|
|
32
|
+
value: ${{ steps.classify.outputs.attempts }}
|
|
33
|
+
refine-mode:
|
|
34
|
+
description: first or rerefine.
|
|
35
|
+
value: ${{ steps.classify.outputs.refine-mode }}
|
|
36
|
+
triage-mode:
|
|
37
|
+
description: first or retriage.
|
|
38
|
+
value: ${{ steps.classify.outputs.triage-mode }}
|
|
39
|
+
trigger-kind:
|
|
40
|
+
description: scheduled or manual.
|
|
41
|
+
value: ${{ steps.classify.outputs.trigger-kind }}
|
|
42
|
+
runs:
|
|
43
|
+
using: composite
|
|
44
|
+
steps:
|
|
45
|
+
- name: Classify the event
|
|
46
|
+
id: classify
|
|
47
|
+
shell: bash
|
|
48
|
+
env:
|
|
49
|
+
EVENT: ${{ github.event_name }}
|
|
50
|
+
ACTION: ${{ github.event.action }}
|
|
51
|
+
LABEL: ${{ github.event.label.name }}
|
|
52
|
+
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
|
|
53
|
+
ISSUE_STATE: ${{ github.event.issue.state }}
|
|
54
|
+
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
55
|
+
EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
56
|
+
COMMENT_ON_PR: ${{ github.event.issue.pull_request != '' }}
|
|
57
|
+
COMMENT_SENDER_TYPE: ${{ github.event.comment.user.type }}
|
|
58
|
+
ACTOR: ${{ github.actor }}
|
|
59
|
+
RUN_PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
|
|
60
|
+
RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
|
61
|
+
RUN_ID: ${{ github.event.workflow_run.id }}
|
|
62
|
+
SCHEDULE: ${{ github.event.schedule }}
|
|
63
|
+
OPERATION: ${{ github.event.inputs.operation }}
|
|
64
|
+
INPUT_ISSUE_NUMBER: ${{ github.event.inputs.issue-number }}
|
|
65
|
+
INPUT_PR_NUMBER: ${{ github.event.inputs.pr-number }}
|
|
66
|
+
INPUT_MODE: ${{ github.event.inputs.mode }}
|
|
67
|
+
INPUT_CI_CONCLUSION: ${{ github.event.inputs.ci-conclusion }}
|
|
68
|
+
INPUT_CI_RUN_ID: ${{ github.event.inputs.ci-run-id }}
|
|
69
|
+
INPUT_ATTEMPTS_SO_FAR: ${{ github.event.inputs.attempts_so_far }}
|
|
70
|
+
INPUT_TRIGGER_KIND: ${{ github.event.inputs.trigger-kind }}
|
|
71
|
+
run: |
|
|
72
|
+
set -euo pipefail
|
|
73
|
+
|
|
74
|
+
bash "${GITHUB_ACTION_PATH}/classify-route.sh" | tee "$RUNNER_TEMP/route.env"
|
|
75
|
+
cat "$RUNNER_TEMP/route.env" >> "$GITHUB_OUTPUT"
|
|
76
|
+
|
|
77
|
+
route="$(sed -n 's/^route=//p' "$RUNNER_TEMP/route.env")"
|
|
78
|
+
reason="$(sed -n 's/^error=//p' "$RUNNER_TEMP/route.env")"
|
|
79
|
+
|
|
80
|
+
if [ "$route" = "none" ] && [ -n "$reason" ]; then
|
|
81
|
+
echo "::notice::No route for this event: $reason"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
- name: Resolve the issue the pull request closes
|
|
85
|
+
id: linked-issue
|
|
86
|
+
if: steps.classify.outputs.route == 'merge-gate' || steps.classify.outputs.route == 'apply-review'
|
|
87
|
+
shell: bash
|
|
88
|
+
env:
|
|
89
|
+
GH_TOKEN: ${{ inputs.token }}
|
|
90
|
+
REPO: ${{ github.repository }}
|
|
91
|
+
PR_NUMBER: ${{ steps.classify.outputs.pr-number }}
|
|
92
|
+
run: |
|
|
93
|
+
set -euo pipefail
|
|
94
|
+
|
|
95
|
+
issue="$(gh pr view "$PR_NUMBER" --repo "$REPO" \
|
|
96
|
+
--json closingIssuesReferences \
|
|
97
|
+
--jq '.closingIssuesReferences[0].number // empty')"
|
|
98
|
+
|
|
99
|
+
echo "issue=${issue}" >> "$GITHUB_OUTPUT"
|
|
100
|
+
echo "Pull request #${PR_NUMBER} closes issue #${issue:-<none>}"
|
|
@@ -28,7 +28,7 @@ is_issue_number() {
|
|
|
28
28
|
|
|
29
29
|
classify_route() {
|
|
30
30
|
local route="none" error=""
|
|
31
|
-
local issue_number="" pr_number="" ci_conclusion="" ci_run_id="" merge_gate_attempts="0"
|
|
31
|
+
local issue_number="" pr_number="" ci_conclusion="" ci_run_id="" merge_gate_attempts="0" attempts="0"
|
|
32
32
|
local refine_mode="" triage_mode="" trigger_kind=""
|
|
33
33
|
|
|
34
34
|
case "${EVENT:-}" in
|
|
@@ -190,13 +190,12 @@ classify_route() {
|
|
|
190
190
|
if is_issue_number "${INPUT_ISSUE_NUMBER:-}"; then
|
|
191
191
|
route="${OPERATION}"
|
|
192
192
|
issue_number="${INPUT_ISSUE_NUMBER}"
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
else
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
fi
|
|
193
|
+
# Both, not one or the other. A worker re-dispatches itself when a run dies before
|
|
194
|
+
# doing any work and carries the count so the budget is bounded; refine also carries
|
|
195
|
+
# the mode it was started in. Written as an if/else, a refine retry arrived as attempt
|
|
196
|
+
# zero every time and could never reach the park.
|
|
197
|
+
[ "$OPERATION" = "refine" ] && refine_mode="${INPUT_MODE:-first}"
|
|
198
|
+
attempts="${INPUT_ATTEMPTS_SO_FAR:-0}"
|
|
200
199
|
else
|
|
201
200
|
error="operation '${OPERATION}' needs a positive issue-number, got '${INPUT_ISSUE_NUMBER:-}'"
|
|
202
201
|
fi
|
|
@@ -206,6 +205,7 @@ classify_route() {
|
|
|
206
205
|
route="triage"
|
|
207
206
|
issue_number="${INPUT_ISSUE_NUMBER}"
|
|
208
207
|
triage_mode="${INPUT_MODE:-first}"
|
|
208
|
+
attempts="${INPUT_ATTEMPTS_SO_FAR:-0}"
|
|
209
209
|
else
|
|
210
210
|
error="operation 'triage' needs a positive issue-number, got '${INPUT_ISSUE_NUMBER:-}'"
|
|
211
211
|
fi
|
|
@@ -214,6 +214,7 @@ classify_route() {
|
|
|
214
214
|
if is_issue_number "${INPUT_PR_NUMBER:-}"; then
|
|
215
215
|
route="apply-review"
|
|
216
216
|
pr_number="${INPUT_PR_NUMBER}"
|
|
217
|
+
attempts="${INPUT_ATTEMPTS_SO_FAR:-0}"
|
|
217
218
|
else
|
|
218
219
|
error="operation 'apply-review' needs a positive pr-number, got '${INPUT_PR_NUMBER:-}'"
|
|
219
220
|
fi
|
|
@@ -257,7 +258,7 @@ pr-number=${pr_number}
|
|
|
257
258
|
ci-conclusion=${ci_conclusion}
|
|
258
259
|
ci-run-id=${ci_run_id}
|
|
259
260
|
merge-gate-attempts=${merge_gate_attempts}
|
|
260
|
-
|
|
261
|
+
attempts=${attempts}
|
|
261
262
|
refine-mode=${refine_mode}
|
|
262
263
|
triage-mode=${triage_mode}
|
|
263
264
|
trigger-kind=${trigger_kind}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/actions/decide-agent-retry/action.yml. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
2
|
+
name: Decide agent retry
|
|
3
|
+
description: Say whether an agent job that failed is worth running again, from how long it ran and how many attempts have been made.
|
|
4
|
+
inputs:
|
|
5
|
+
token:
|
|
6
|
+
description: GitHub token with actions read access, used to read this run's own jobs.
|
|
7
|
+
required: true
|
|
8
|
+
attempts-so-far:
|
|
9
|
+
description: Attempts already made against this piece of work. Empty counts as none.
|
|
10
|
+
required: false
|
|
11
|
+
default: '0'
|
|
12
|
+
park-at:
|
|
13
|
+
description: Stop retrying once this many attempts have been made.
|
|
14
|
+
required: true
|
|
15
|
+
under-minutes:
|
|
16
|
+
description: Only a run shorter than this is repeated.
|
|
17
|
+
required: true
|
|
18
|
+
outputs:
|
|
19
|
+
retry:
|
|
20
|
+
description: '"true" when this failure is worth repeating.'
|
|
21
|
+
value: ${{ steps.decide.outputs.retry }}
|
|
22
|
+
next:
|
|
23
|
+
description: The attempt number a retry would be.
|
|
24
|
+
value: ${{ steps.decide.outputs.next }}
|
|
25
|
+
minutes:
|
|
26
|
+
description: How long the agent job ran, or -1 when that could not be read.
|
|
27
|
+
value: ${{ steps.decide.outputs.minutes }}
|
|
28
|
+
runs:
|
|
29
|
+
using: composite
|
|
30
|
+
steps:
|
|
31
|
+
# A run that died in three minutes produced no answer, so repeating it costs three minutes. A
|
|
32
|
+
# run that worked for half an hour and then failed produced an answer that was wrong, and
|
|
33
|
+
# repeating it buys the same wrong answer half an hour later. Duration is what separates them,
|
|
34
|
+
# and it needs no model and no log parsing to read.
|
|
35
|
+
#
|
|
36
|
+
# Written once here because the rule was in the implement worker alone, and refine, triage and
|
|
37
|
+
# apply-review parked instead -- an Odyssey refine died in three minutes on
|
|
38
|
+
# `Model 'glm-5-3' not found`, a gateway fault that clears in seconds, and waited on the
|
|
39
|
+
# janitor's six-hourly sweep. Five copies of sixty lines is how one rule becomes five.
|
|
40
|
+
- name: Decide whether this failure is worth repeating
|
|
41
|
+
id: decide
|
|
42
|
+
shell: bash
|
|
43
|
+
env:
|
|
44
|
+
GH_TOKEN: ${{ inputs.token }}
|
|
45
|
+
REPO: ${{ github.repository }}
|
|
46
|
+
RUN_ID: ${{ github.run_id }}
|
|
47
|
+
ATTEMPTS: ${{ inputs.attempts-so-far }}
|
|
48
|
+
PARK_AT: ${{ inputs.park-at }}
|
|
49
|
+
UNDER_MINUTES: ${{ inputs.under-minutes }}
|
|
50
|
+
run: |
|
|
51
|
+
set -euo pipefail
|
|
52
|
+
# The agent job belongs to this same run: a called workflow shares the caller's run id.
|
|
53
|
+
read -r started finished <<<"$(gh api "repos/$REPO/actions/runs/$RUN_ID/jobs?per_page=100" \
|
|
54
|
+
--jq '[.jobs[] | select(.name | endswith("agent"))] | last // empty
|
|
55
|
+
| "\(.started_at // "") \(.completed_at // "")"')"
|
|
56
|
+
minutes=-1
|
|
57
|
+
if [ -n "${started:-}" ] && [ -n "${finished:-}" ]; then
|
|
58
|
+
minutes=$(( ( $(date -u -d "$finished" +%s) - $(date -u -d "$started" +%s) ) / 60 ))
|
|
59
|
+
fi
|
|
60
|
+
attempts="${ATTEMPTS:-0}"
|
|
61
|
+
[ -n "$attempts" ] || attempts=0
|
|
62
|
+
retry=false
|
|
63
|
+
# An unknown duration is treated as a long run: never retry on a guess.
|
|
64
|
+
if [ "$minutes" -ge 0 ] && [ "$minutes" -lt "$UNDER_MINUTES" ] && [ "$attempts" -lt "$PARK_AT" ]; then
|
|
65
|
+
retry=true
|
|
66
|
+
fi
|
|
67
|
+
{
|
|
68
|
+
echo "retry=$retry"
|
|
69
|
+
echo "next=$((attempts + 1))"
|
|
70
|
+
echo "minutes=$minutes"
|
|
71
|
+
} >> "$GITHUB_OUTPUT"
|
|
72
|
+
echo "agent job ran for ${minutes}m; attempts so far ${attempts}; retry=${retry}"
|
|
@@ -240,14 +240,14 @@ assert "merge-gate dispatch defaults its attempt count to zero" 0 \
|
|
|
240
240
|
"$(route_field merge-gate-attempts EVENT=workflow_dispatch OPERATION=merge-gate INPUT_PR_NUMBER=7)"
|
|
241
241
|
assert "merge-gate dispatch forwards the attempt count" 3 \
|
|
242
242
|
"$(route_field merge-gate-attempts EVENT=workflow_dispatch OPERATION=merge-gate INPUT_PR_NUMBER=7 INPUT_ATTEMPTS_SO_FAR=3)"
|
|
243
|
-
#
|
|
244
|
-
#
|
|
245
|
-
|
|
246
|
-
|
|
243
|
+
# A worker re-dispatches itself when a run dies before producing an answer, so the count has to
|
|
244
|
+
# survive the round trip or the budget never advances and the retry never stops. The field is
|
|
245
|
+
# `attempts` for every route now: it was `implement-attempts`, set in the else-branch of a test
|
|
246
|
+
# on refine, so a refine retry arrived as attempt zero and could never reach the park.
|
|
247
|
+
assert "a dispatch defaults its attempt count to zero" 0 \
|
|
248
|
+
"$(route_field attempts EVENT=workflow_dispatch OPERATION=implement INPUT_ISSUE_NUMBER=42)"
|
|
247
249
|
assert "implement dispatch forwards the attempt count" 2 \
|
|
248
|
-
"$(route_field
|
|
249
|
-
assert "a refine dispatch carries no implement attempts" 0 \
|
|
250
|
-
"$(route_field implement-attempts EVENT=workflow_dispatch OPERATION=refine INPUT_ISSUE_NUMBER=42 INPUT_ATTEMPTS_SO_FAR=2)"
|
|
250
|
+
"$(route_field attempts EVENT=workflow_dispatch OPERATION=implement INPUT_ISSUE_NUMBER=42 INPUT_ATTEMPTS_SO_FAR=2)"
|
|
251
251
|
assert_route "release dispatch needs no numbers" release \
|
|
252
252
|
EVENT=workflow_dispatch OPERATION=release
|
|
253
253
|
assert_route "reconcile-bot-pr-runs dispatch needs no numbers" reconcile-bot-pr-runs \
|
|
@@ -1522,6 +1522,14 @@ if [ -f "$GATE_VALIDATOR" ] && worker_installed merge-gate; then
|
|
|
1522
1522
|
gate_case "remediated with two pushes" invalid "$(gate_items "$(gate_comment remediated),${gate_push},${gate_push}")" failure low
|
|
1523
1523
|
gate_case "an assessment carrying a push" invalid "$(gate_items "$(gate_comment assessed),${gate_push}")" success low
|
|
1524
1524
|
|
|
1525
|
+
# Correctness remediation: the agent found the diff does not satisfy the acceptance
|
|
1526
|
+
# criteria, pushed a fix, and emitted remediated. The validator treats this the same as a
|
|
1527
|
+
# CI-failure or conflict remediation -- one push and the remediated word -- regardless of
|
|
1528
|
+
# what acceptanceCriteriaMet says in the report. The next gate cycle re-evaluates the fix.
|
|
1529
|
+
gate_unmet_remedied='{\"findings\":[{\"verified\":false,\"severity\":\"medium\",\"category\":\"correctness\",\"file\":\"src/app.ts\",\"line\":42,\"finding\":\"criterion X not implemented\"}],\"recoverability\":\"high\",\"acceptanceCriteriaMet\":false,\"confidence\":0.9}'
|
|
1530
|
+
gate_case "remediated for unmet acceptance criteria" \
|
|
1531
|
+
remediated "$(gate_items "$(gate_comment remediated "$gate_unmet_remedied"),${gate_push}")" success low
|
|
1532
|
+
|
|
1525
1533
|
# Output from a worker version that predates the disposition table. Applying its vocabulary
|
|
1526
1534
|
# would merge on a word this validator no longer means the same thing by.
|
|
1527
1535
|
gate_case "the old merge vocabulary is refused" invalid '{"items":[{"type":"add_comment","item_number":7,"body":"<!-- agent-merge-gate -->\\n**Verdict:** merge"}]}' success low
|
|
@@ -1940,6 +1948,69 @@ if worker_installed audit; then
|
|
|
1940
1948
|
if [ "$AUDIT_EMPTY_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
|
|
1941
1949
|
fi
|
|
1942
1950
|
|
|
1951
|
+
echo "── Retrying a run that died early ────────────────────────────────────────"
|
|
1952
|
+
|
|
1953
|
+
# A run that died before producing anything is worth repeating; one that worked and then failed
|
|
1954
|
+
# produced an answer that was wrong, and repeating it buys the same wrong answer later. Only the
|
|
1955
|
+
# implement worker knew that. An Odyssey refine died in three minutes on `Model 'glm-5-3' not
|
|
1956
|
+
# found` -- a gateway fault that clears in seconds -- parked, and waited on the janitor's
|
|
1957
|
+
# six-hourly sweep.
|
|
1958
|
+
RETRY_OK=1
|
|
1959
|
+
for route in refine implement triage apply-review; do
|
|
1960
|
+
worker_md="${WORKFLOWS_DIR}/agent-${route}.md"
|
|
1961
|
+
[ -f "$worker_md" ] || continue
|
|
1962
|
+
|
|
1963
|
+
# Both halves. A worker with only the park is the state this fixes; one with only the retry
|
|
1964
|
+
# never stops trying.
|
|
1965
|
+
if [ "$(count -cE "^ *if:.*steps\.decide\.outputs\.retry == 'true'" "$worker_md")" -eq 0 ] ||
|
|
1966
|
+
[ "$(count -cE "^ *if:.*steps\.decide\.outputs\.retry != 'true'" "$worker_md")" -eq 0 ]; then
|
|
1967
|
+
RETRY_OK=0
|
|
1968
|
+
echo "FAIL: agent-${route} does not have both a retry and a park path, so an early failure either never repeats or never stops" >&2
|
|
1969
|
+
fi
|
|
1970
|
+
|
|
1971
|
+
# Each worker re-dispatches its own route. `-f operation=implement` pasted into refine produces
|
|
1972
|
+
# a plausible run doing the wrong work, and nothing anywhere goes red.
|
|
1973
|
+
# Only the retry dispatch: it is the line that also carries the attempt count. implement
|
|
1974
|
+
# dispatches reconcile-bot-pr-runs too, after it opens a pull request, and that is not a retry.
|
|
1975
|
+
# Only the retry dispatch: it is the line that also carries the attempt count. implement
|
|
1976
|
+
# dispatches reconcile-bot-pr-runs too, after it opens a pull request, and that is not a retry.
|
|
1977
|
+
#
|
|
1978
|
+
# Both greps go through `count`. A worker with no retry dispatch matches nothing, grep exits 1,
|
|
1979
|
+
# and under `set -o pipefail` that killed the whole suite after printing the failure above it --
|
|
1980
|
+
# the tally never ran, so a mutation that removed a retry branch looked like it had been caught
|
|
1981
|
+
# when in fact nothing after it had been checked.
|
|
1982
|
+
dispatched=$(count -E 'operation=[a-z-]+.*attempts_so_far' "$worker_md" \
|
|
1983
|
+
| { count -oE 'operation=[a-z-]+' || true; } | sed 's/operation=//' | sort -u | tr '\n' ' ')
|
|
1984
|
+
dispatched="${dispatched% }"
|
|
1985
|
+
if [ -n "$dispatched" ] && [ "$dispatched" != "$route" ]; then
|
|
1986
|
+
RETRY_OK=0
|
|
1987
|
+
echo "FAIL: agent-${route} re-dispatches '${dispatched}' rather than its own route; that run would do the wrong work and still look healthy" >&2
|
|
1988
|
+
fi
|
|
1989
|
+
|
|
1990
|
+
# The count has to survive the round trip or the park is never reached.
|
|
1991
|
+
if ! grep -qE '^ attempts_so_far:' "$worker_md"; then
|
|
1992
|
+
RETRY_OK=0
|
|
1993
|
+
echo "FAIL: agent-${route} does not take attempts_so_far, so every retry arrives as the first and the budget never binds" >&2
|
|
1994
|
+
fi
|
|
1995
|
+
if [ "$(count -cF 'attempts_so_far: ${{ needs.classify.outputs.attempts }}' "$ROUTER_YML")" -eq 0 ]; then
|
|
1996
|
+
RETRY_OK=0
|
|
1997
|
+
echo "FAIL: the router does not pass classify's attempts to its workers" >&2
|
|
1998
|
+
fi
|
|
1999
|
+
done
|
|
2000
|
+
|
|
2001
|
+
# The classifier carries the count for every dispatched route. refine needs its mode *and* its
|
|
2002
|
+
# count: written as an if/else, a refine retry arrived as attempt zero every time.
|
|
2003
|
+
for op in refine implement triage; do
|
|
2004
|
+
got=$(route_field attempts EVENT=workflow_dispatch OPERATION="$op" INPUT_ISSUE_NUMBER=42 INPUT_ATTEMPTS_SO_FAR=3)
|
|
2005
|
+
assert "a dispatched ${op} carries its attempt count" 3 "$got"
|
|
2006
|
+
done
|
|
2007
|
+
assert "a dispatched apply-review carries its attempt count" 3 \
|
|
2008
|
+
"$(route_field attempts EVENT=workflow_dispatch OPERATION=apply-review INPUT_PR_NUMBER=9 INPUT_ATTEMPTS_SO_FAR=3)"
|
|
2009
|
+
assert "a dispatched refine still carries its mode alongside the count" rerefine \
|
|
2010
|
+
"$(route_field refine-mode EVENT=workflow_dispatch OPERATION=refine INPUT_ISSUE_NUMBER=42 INPUT_MODE=rerefine INPUT_ATTEMPTS_SO_FAR=3)"
|
|
2011
|
+
|
|
2012
|
+
if [ "$RETRY_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
|
|
2013
|
+
|
|
1943
2014
|
echo "── Ways a run ends with nothing ──────────────────────────────────────────"
|
|
1944
2015
|
|
|
1945
2016
|
# A run can end without a pull request in five ways and they need different answers. One sentence
|
|
@@ -62,7 +62,7 @@ jobs:
|
|
|
62
62
|
api:
|
|
63
63
|
name: API (.NET)
|
|
64
64
|
if: github.event_name != 'schedule'
|
|
65
|
-
runs-on:
|
|
65
|
+
runs-on: RunnerLandingZone
|
|
66
66
|
timeout-minutes: 30
|
|
67
67
|
services:
|
|
68
68
|
sqlserver:
|
|
@@ -123,7 +123,7 @@ jobs:
|
|
|
123
123
|
mutation-api:
|
|
124
124
|
name: Mutation (.NET, diff)
|
|
125
125
|
if: github.event_name == 'pull_request'
|
|
126
|
-
runs-on:
|
|
126
|
+
runs-on: RunnerLandingZone
|
|
127
127
|
timeout-minutes: 25
|
|
128
128
|
defaults:
|
|
129
129
|
run:
|
|
@@ -169,7 +169,7 @@ jobs:
|
|
|
169
169
|
web:
|
|
170
170
|
name: Web (Next.js)
|
|
171
171
|
if: github.event_name != 'schedule'
|
|
172
|
-
runs-on:
|
|
172
|
+
runs-on: RunnerLandingZone
|
|
173
173
|
timeout-minutes: 15
|
|
174
174
|
steps:
|
|
175
175
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -200,7 +200,7 @@ jobs:
|
|
|
200
200
|
mutation-web:
|
|
201
201
|
name: Mutation (web, diff)
|
|
202
202
|
if: github.event_name == 'pull_request'
|
|
203
|
-
runs-on:
|
|
203
|
+
runs-on: RunnerLandingZone
|
|
204
204
|
timeout-minutes: 25
|
|
205
205
|
steps:
|
|
206
206
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -241,14 +241,14 @@ jobs:
|
|
|
241
241
|
|
|
242
242
|
secret-scan:
|
|
243
243
|
name: Secret scan (TruffleHog)
|
|
244
|
-
runs-on:
|
|
244
|
+
runs-on: RunnerLandingZone
|
|
245
245
|
steps:
|
|
246
246
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
247
247
|
- run: docker run --rm -v "${{ github.workspace }}:/repo" "$TRUFFLEHOG_IMAGE" filesystem /repo --only-verified --fail --no-update
|
|
248
248
|
|
|
249
249
|
deps-and-iac-scan:
|
|
250
250
|
name: Dependencies and IaC (Trivy)
|
|
251
|
-
runs-on:
|
|
251
|
+
runs-on: RunnerLandingZone
|
|
252
252
|
steps:
|
|
253
253
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
254
254
|
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
@@ -262,14 +262,14 @@ jobs:
|
|
|
262
262
|
|
|
263
263
|
sast-scan:
|
|
264
264
|
name: SAST (Semgrep)
|
|
265
|
-
runs-on:
|
|
265
|
+
runs-on: RunnerLandingZone
|
|
266
266
|
steps:
|
|
267
267
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
268
268
|
- run: docker run --rm -v "${{ github.workspace }}:/src" -w /src "$SEMGREP_IMAGE" semgrep scan --error --metrics=off --oss-only --disable-version-check --config p/default --config p/security-audit --config p/secrets --config p/csharp --config p/typescript --config p/react --exclude-rule yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag
|
|
269
269
|
|
|
270
270
|
sbom:
|
|
271
271
|
name: SBOM (CycloneDX)
|
|
272
|
-
runs-on:
|
|
272
|
+
runs-on: RunnerLandingZone
|
|
273
273
|
steps:
|
|
274
274
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
275
275
|
- uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
|
|
@@ -296,7 +296,7 @@ jobs:
|
|
|
296
296
|
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
297
297
|
github.event.pull_request.draft == false &&
|
|
298
298
|
github.event.pull_request.user.type == 'Bot'
|
|
299
|
-
runs-on:
|
|
299
|
+
runs-on: RunnerLandingZone
|
|
300
300
|
timeout-minutes: 5
|
|
301
301
|
permissions:
|
|
302
302
|
contents: read
|
|
@@ -38,7 +38,7 @@ jobs:
|
|
|
38
38
|
lint:
|
|
39
39
|
name: Lint (Biome)
|
|
40
40
|
if: github.event_name != 'schedule'
|
|
41
|
-
runs-on:
|
|
41
|
+
runs-on: RunnerLandingZone
|
|
42
42
|
steps:
|
|
43
43
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
44
44
|
- uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
|
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
web:
|
|
52
52
|
name: Web (Next.js static export)
|
|
53
53
|
if: github.event_name != 'schedule'
|
|
54
|
-
runs-on:
|
|
54
|
+
runs-on: RunnerLandingZone
|
|
55
55
|
steps:
|
|
56
56
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
57
57
|
- uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
|
|
@@ -73,7 +73,7 @@ jobs:
|
|
|
73
73
|
mutation-web:
|
|
74
74
|
name: Mutation (web, diff)
|
|
75
75
|
if: github.event_name == 'pull_request'
|
|
76
|
-
runs-on:
|
|
76
|
+
runs-on: RunnerLandingZone
|
|
77
77
|
timeout-minutes: 25
|
|
78
78
|
steps:
|
|
79
79
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -103,7 +103,7 @@ jobs:
|
|
|
103
103
|
name: E2E (Playwright)
|
|
104
104
|
if: github.event_name != 'schedule'
|
|
105
105
|
needs: web
|
|
106
|
-
runs-on:
|
|
106
|
+
runs-on: RunnerLandingZone
|
|
107
107
|
steps:
|
|
108
108
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
109
109
|
- uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
|
|
@@ -128,7 +128,7 @@ jobs:
|
|
|
128
128
|
name: Desktop (Electron)
|
|
129
129
|
if: github.event_name != 'schedule'
|
|
130
130
|
needs: web
|
|
131
|
-
runs-on:
|
|
131
|
+
runs-on: RunnerLandingZone
|
|
132
132
|
steps:
|
|
133
133
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
134
134
|
- uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
|
|
@@ -152,7 +152,7 @@ jobs:
|
|
|
152
152
|
mobile:
|
|
153
153
|
name: Mobile (Capacitor)
|
|
154
154
|
if: github.event_name != 'schedule'
|
|
155
|
-
runs-on:
|
|
155
|
+
runs-on: RunnerLandingZone
|
|
156
156
|
steps:
|
|
157
157
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
158
158
|
- uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4
|
|
@@ -171,14 +171,14 @@ jobs:
|
|
|
171
171
|
|
|
172
172
|
secret-scan:
|
|
173
173
|
name: Secret scan (TruffleHog)
|
|
174
|
-
runs-on:
|
|
174
|
+
runs-on: RunnerLandingZone
|
|
175
175
|
steps:
|
|
176
176
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
177
177
|
- run: docker run --rm -v "${{ github.workspace }}:/repo" "$TRUFFLEHOG_IMAGE" filesystem /repo --only-verified --fail --no-update
|
|
178
178
|
|
|
179
179
|
deps-and-iac-scan:
|
|
180
180
|
name: Dependencies and IaC (Trivy)
|
|
181
|
-
runs-on:
|
|
181
|
+
runs-on: RunnerLandingZone
|
|
182
182
|
steps:
|
|
183
183
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
184
184
|
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
@@ -192,14 +192,14 @@ jobs:
|
|
|
192
192
|
|
|
193
193
|
sast-scan:
|
|
194
194
|
name: SAST (Semgrep)
|
|
195
|
-
runs-on:
|
|
195
|
+
runs-on: RunnerLandingZone
|
|
196
196
|
steps:
|
|
197
197
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
198
198
|
- run: docker run --rm -v "${{ github.workspace }}:/src" -w /src "$SEMGREP_IMAGE" semgrep scan --error --metrics=off --oss-only --disable-version-check --config p/default --config p/security-audit --config p/secrets --config p/typescript --config p/react --exclude-rule yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag
|
|
199
199
|
|
|
200
200
|
sbom:
|
|
201
201
|
name: SBOM (CycloneDX)
|
|
202
|
-
runs-on:
|
|
202
|
+
runs-on: RunnerLandingZone
|
|
203
203
|
steps:
|
|
204
204
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
205
205
|
- uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
|
|
@@ -226,7 +226,7 @@ jobs:
|
|
|
226
226
|
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
227
227
|
github.event.pull_request.draft == false &&
|
|
228
228
|
github.event.pull_request.user.type == 'Bot'
|
|
229
|
-
runs-on:
|
|
229
|
+
runs-on: RunnerLandingZone
|
|
230
230
|
timeout-minutes: 5
|
|
231
231
|
permissions:
|
|
232
232
|
contents: read
|
|
@@ -12,6 +12,18 @@ env:
|
|
|
12
12
|
STALLED_LABEL: stalled
|
|
13
13
|
PR_PENDING_LABEL: pr-pending
|
|
14
14
|
REVIEW_MARKER: "<!-- agent-apply-review -->"
|
|
15
|
+
# A run that died before it produced anything is worth repeating; one that worked and then
|
|
16
|
+
# failed produced an answer that was wrong, and repeating it buys the same wrong answer later.
|
|
17
|
+
# Duration is what separates them. Odyssey #190 died in three minutes on `Model 'glm-5-3' not
|
|
18
|
+
# found`, a gateway fault that clears in seconds, and waited on the janitor's six-hourly sweep
|
|
19
|
+
# because only the implement worker could do this.
|
|
20
|
+
APPLY_REVIEW_ATTEMPT_MARKER: "<!-- agent-apply-review-attempt -->"
|
|
21
|
+
MAX_ATTEMPTS: "5"
|
|
22
|
+
PARK_AT_ATTEMPT: "4"
|
|
23
|
+
# Ten rather than six: the failure that prompted this took three minutes, and a slower one on a
|
|
24
|
+
# worse day would fall outside a six-minute window and park for a fault that clears by itself.
|
|
25
|
+
RETRY_UNDER_MINUTES: "10"
|
|
26
|
+
RETRY_COMMENT: "That is what a provider outage looks like -- the run ended before it could produce an answer -- so this is being tried again from the start. It is a fresh run rather than a continuation: nothing is carried over from the attempt that failed."
|
|
15
27
|
INCOMPLETE_COMMENT: "Applying the review feedback ended without an outcome. This worker has no retry of its own: it runs again when somebody reviews or comments on the pull request, and the issue is flagged so it is not lost until then."
|
|
16
28
|
ISSUE_CONTEXT_PATH: /tmp/gh-aw/agent/issue-context.json
|
|
17
29
|
GH_AW_ALLOWED_BOTS: "platform-devbox[bot],github-actions[bot]"
|
|
@@ -38,6 +50,11 @@ imports:
|
|
|
38
50
|
on:
|
|
39
51
|
workflow_call:
|
|
40
52
|
inputs:
|
|
53
|
+
attempts_so_far:
|
|
54
|
+
description: Runs already made for this work that died before producing an answer. Filled by the worker when it re-dispatches itself, not by people.
|
|
55
|
+
required: false
|
|
56
|
+
type: string
|
|
57
|
+
default: '0'
|
|
41
58
|
pr-number:
|
|
42
59
|
description: Pull request number to apply review feedback on.
|
|
43
60
|
required: true
|
|
@@ -285,6 +302,8 @@ jobs:
|
|
|
285
302
|
permissions:
|
|
286
303
|
contents: read
|
|
287
304
|
issues: write
|
|
305
|
+
# the retry re-enters through the router, which is a workflow_dispatch
|
|
306
|
+
actions: write
|
|
288
307
|
steps:
|
|
289
308
|
- name: Checkout workflow actions
|
|
290
309
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -296,13 +315,58 @@ jobs:
|
|
|
296
315
|
with:
|
|
297
316
|
client-id: ${{ secrets.BOT_APP_ID }}
|
|
298
317
|
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
318
|
+
- name: Decide whether this failure is worth repeating
|
|
319
|
+
id: decide
|
|
320
|
+
uses: ./.github/actions/decide-agent-retry
|
|
321
|
+
with:
|
|
322
|
+
token: ${{ github.token }}
|
|
323
|
+
attempts-so-far: ${{ inputs.attempts_so_far }}
|
|
324
|
+
park-at: ${{ env.PARK_AT_ATTEMPT }}
|
|
325
|
+
under-minutes: ${{ env.RETRY_UNDER_MINUTES }}
|
|
326
|
+
# Recorded before any label moves, so a failure in the steps below leaves a run that can be
|
|
327
|
+
# counted rather than work released with nothing to show for it.
|
|
328
|
+
- name: Report the failed attempt
|
|
329
|
+
if: steps.decide.outputs.retry == 'true'
|
|
330
|
+
uses: ./.github/actions/create-issue-comment
|
|
331
|
+
with:
|
|
332
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
333
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
334
|
+
body: |
|
|
335
|
+
${{ env.APPLY_REVIEW_ATTEMPT_MARKER }}
|
|
336
|
+
Attempt ${{ steps.decide.outputs.next }} of ${{ env.MAX_ATTEMPTS }} ended after ${{ steps.decide.outputs.minutes }} minutes, before the run could produce an answer.
|
|
337
|
+
${{ env.RETRY_COMMENT }}
|
|
338
|
+
[View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
|
339
|
+
- name: Release the reservation for the retry
|
|
340
|
+
if: steps.decide.outputs.retry == 'true'
|
|
341
|
+
uses: ./.github/actions/remove-issue-labels
|
|
342
|
+
with:
|
|
343
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
344
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
345
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
346
|
+
- name: Send the work back through the router
|
|
347
|
+
if: steps.decide.outputs.retry == 'true'
|
|
348
|
+
env:
|
|
349
|
+
GH_TOKEN: ${{ github.token }}
|
|
350
|
+
REPO: ${{ github.repository }}
|
|
351
|
+
REF: ${{ github.event.repository.default_branch }}
|
|
352
|
+
SUBJECT: ${{ inputs.pr-number }}
|
|
353
|
+
NEXT: ${{ steps.decide.outputs.next }}
|
|
354
|
+
run: |
|
|
355
|
+
set -euo pipefail
|
|
356
|
+
# The provider recovers in seconds, so pause before re-entering rather than dispatching
|
|
357
|
+
# back into the same outage. The router's own classify and authorize jobs add more.
|
|
358
|
+
sleep 30
|
|
359
|
+
gh workflow run work-router.yml --repo "$REPO" --ref "$REF" -f operation=apply-review -f pr-number="$SUBJECT" -f attempts_so_far="$NEXT"
|
|
360
|
+
echo "Re-dispatched apply-review for $SUBJECT as attempt $NEXT."
|
|
299
361
|
- name: Release the issue
|
|
362
|
+
if: steps.decide.outputs.retry != 'true'
|
|
300
363
|
uses: ./.github/actions/remove-issue-labels
|
|
301
364
|
with:
|
|
302
365
|
token: ${{ steps.app-token.outputs.token }}
|
|
303
366
|
issue-number: ${{ needs.subject.outputs.issue }}
|
|
304
367
|
labels: ${{ env.WORKING_LABEL }}
|
|
305
368
|
- name: Flag for human review
|
|
369
|
+
if: steps.decide.outputs.retry != 'true'
|
|
306
370
|
uses: ./.github/actions/add-issue-labels
|
|
307
371
|
with:
|
|
308
372
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -311,6 +375,7 @@ jobs:
|
|
|
311
375
|
${{ env.REVIEW_LABEL }}
|
|
312
376
|
${{ env.STALLED_LABEL }}
|
|
313
377
|
- name: Report missing review feedback outcome
|
|
378
|
+
if: steps.decide.outputs.retry != 'true'
|
|
314
379
|
uses: ./.github/actions/create-issue-comment
|
|
315
380
|
with:
|
|
316
381
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -898,9 +898,35 @@ timeout-minutes: 120
|
|
|
898
898
|
reassurance about their absence.
|
|
899
899
|
|
|
900
900
|
**5c. Check the acceptance criteria.** The issue context at `${{ env.ISSUE_CONTEXT_PATH }}`
|
|
901
|
-
says what this change was supposed to do. Confirm the diff does it.
|
|
902
|
-
|
|
903
|
-
|
|
901
|
+
says what this change was supposed to do. Confirm the diff does it. The double-check the
|
|
902
|
+
rest of this step asks you to do is the gate's own: a defect an agent did not notice and CI
|
|
903
|
+
did not catch is a more expensive rollback than a wrong auto-merge.
|
|
904
|
+
|
|
905
|
+
Set `acceptanceCriteriaMet` to `false` when you can name a criterion the diff does not
|
|
906
|
+
satisfy. Name every criterion that is missing or wrong, in `reason` and in `findings` if
|
|
907
|
+
the gap is a defect (it often is). An empty `findings` array with
|
|
908
|
+
`acceptanceCriteriaMet: false` is accepted but carries little evidence: prefer one entry
|
|
909
|
+
per gap, with `category: correctness`, the file and line, and a `suggestedFix`.
|
|
910
|
+
|
|
911
|
+
**Correctness remediation.** When `acceptanceCriteriaMet` is `false`, you may fix the
|
|
912
|
+
gap and push the fix the same way you fix a failed CI run — this is the same
|
|
913
|
+
`remediated` verdict the merge-conflict and CI-failure paths use, and it re-runs CI and
|
|
914
|
+
the gate on the new head. To do it:
|
|
915
|
+
|
|
916
|
+
- Produce the patch that closes each unmet criterion. Every criterion you named must be
|
|
917
|
+
addressed by the patch, or the next gate cycle reproduces this verdict.
|
|
918
|
+
- Run the verification commands below (scoped to the files you changed) before the push.
|
|
919
|
+
- Push the fix using `push_to_pull_request_branch` (pr_number: ${{ needs.subject.outputs.pr }},
|
|
920
|
+
branch: the current PR branch), then emit the `add_comment` with
|
|
921
|
+
**Verdict:** remediated. Set `acceptanceCriteriaMet` to `false` in the report: the
|
|
922
|
+
criteria were unmet when you reviewed, and the fix is what addresses them. The next
|
|
923
|
+
cycle validates the fix.
|
|
924
|
+
- Do not rebase, reset, amend or otherwise rewrite history: the push is fast-forward only.
|
|
925
|
+
|
|
926
|
+
If you cannot fix a criterion in one pass — it needs a decision, a question, or a code path
|
|
927
|
+
you cannot trace — do not push. Report `assessed` with `acceptanceCriteriaMet: false` and no
|
|
928
|
+
push. The workflow sends the pull request to a human, which is the correct action when the
|
|
929
|
+
gap is beyond a focused repair.
|
|
904
930
|
|
|
905
931
|
**5d. Answer the recoverability checklist.** How easy would this be to undo if it were
|
|
906
932
|
wrong? Cite the diff for each answer, and record the ones that fired in
|
|
@@ -991,8 +1017,9 @@ timeout-minutes: 120
|
|
|
991
1017
|
|
|
992
1018
|
7. Say which of two things you did, and nothing more.
|
|
993
1019
|
|
|
994
|
-
- **`remediated`** — CI failed
|
|
995
|
-
|
|
1020
|
+
- **`remediated`** — something was wrong (CI failed, the branch conflicted, or the diff did
|
|
1021
|
+
not satisfy the acceptance criteria), you fixed it, you verified the fix, and you are
|
|
1022
|
+
pushing it. Exactly one `push_to_pull_request_branch` goes with this word.
|
|
996
1023
|
- **`assessed`** — you reviewed the change and are reporting what you found. No push.
|
|
997
1024
|
|
|
998
1025
|
These are the only two words the workflow accepts. You do not write `merge`, `review`,
|
|
@@ -1041,6 +1068,12 @@ timeout-minutes: 120
|
|
|
1041
1068
|
|
|
1042
1069
|
`"findings": []` on a clean change is the expected output, not a failure to do the job.
|
|
1043
1070
|
|
|
1071
|
+
When `acceptanceCriteriaMet` is `false` and you are pushing a fix, the verdict must be
|
|
1072
|
+
`remediated`, not `assessed`: the validator refuses an `assessed` verdict that carries a
|
|
1073
|
+
push. Set `acceptanceCriteriaMet` to `false` in the report you push with the fix — it
|
|
1074
|
+
describes the code you reviewed, not the fix you just produced. The next gate cycle
|
|
1075
|
+
re-evaluates the updated diff and sets it to `true` (or finds another gap).
|
|
1076
|
+
|
|
1044
1077
|
The workflow applies comments, labels, merges, and closures with the App token. Reading the
|
|
1045
1078
|
repository, running verification commands and delegating a finding to be checked are all part
|
|
1046
1079
|
of the job. What is restricted is what leaves this run: the only safe outputs you may call are
|
|
@@ -24,6 +24,18 @@ env:
|
|
|
24
24
|
# re-running a decision produces the same decision. Created idempotently where it is applied.
|
|
25
25
|
STALLED_LABEL: stalled
|
|
26
26
|
REFINE_MARKER: "<!-- agent-refine -->"
|
|
27
|
+
# A run that died before it produced anything is worth repeating; one that worked and then
|
|
28
|
+
# failed produced an answer that was wrong, and repeating it buys the same wrong answer later.
|
|
29
|
+
# Duration is what separates them. Odyssey #190 died in three minutes on `Model 'glm-5-3' not
|
|
30
|
+
# found`, a gateway fault that clears in seconds, and waited on the janitor's six-hourly sweep
|
|
31
|
+
# because only the implement worker could do this.
|
|
32
|
+
REFINE_ATTEMPT_MARKER: "<!-- agent-refine-attempt -->"
|
|
33
|
+
MAX_ATTEMPTS: "5"
|
|
34
|
+
PARK_AT_ATTEMPT: "4"
|
|
35
|
+
# Ten rather than six: the failure that prompted this took three minutes, and a slower one on a
|
|
36
|
+
# worse day would fall outside a six-minute window and park for a fault that clears by itself.
|
|
37
|
+
RETRY_UNDER_MINUTES: "10"
|
|
38
|
+
RETRY_COMMENT: "That is what a provider outage looks like -- the run ended before it could produce an answer -- so this is being tried again from the start. It is a fresh run rather than a continuation: nothing is carried over from the attempt that failed."
|
|
27
39
|
DRAFT_MARKER: "<!-- agent-refine-draft -->"
|
|
28
40
|
INITIAL_MODE: first
|
|
29
41
|
RESPONSE_MODE: rerefine
|
|
@@ -72,6 +84,11 @@ imports:
|
|
|
72
84
|
on:
|
|
73
85
|
workflow_call:
|
|
74
86
|
inputs:
|
|
87
|
+
attempts_so_far:
|
|
88
|
+
description: Runs already made for this work that died before producing an answer. Filled by the worker when it re-dispatches itself, not by people.
|
|
89
|
+
required: false
|
|
90
|
+
type: string
|
|
91
|
+
default: '0'
|
|
75
92
|
issue-number:
|
|
76
93
|
description: Issue number to refine.
|
|
77
94
|
required: true
|
|
@@ -369,6 +386,8 @@ jobs:
|
|
|
369
386
|
permissions:
|
|
370
387
|
contents: read
|
|
371
388
|
issues: write
|
|
389
|
+
# the retry re-enters through the router, which is a workflow_dispatch
|
|
390
|
+
actions: write
|
|
372
391
|
steps:
|
|
373
392
|
- name: Checkout workflow actions
|
|
374
393
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -378,13 +397,59 @@ jobs:
|
|
|
378
397
|
with:
|
|
379
398
|
client-id: ${{ secrets.BOT_APP_ID }}
|
|
380
399
|
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
400
|
+
- name: Decide whether this failure is worth repeating
|
|
401
|
+
id: decide
|
|
402
|
+
uses: ./.github/actions/decide-agent-retry
|
|
403
|
+
with:
|
|
404
|
+
token: ${{ github.token }}
|
|
405
|
+
attempts-so-far: ${{ inputs.attempts_so_far }}
|
|
406
|
+
park-at: ${{ env.PARK_AT_ATTEMPT }}
|
|
407
|
+
under-minutes: ${{ env.RETRY_UNDER_MINUTES }}
|
|
408
|
+
# Recorded before any label moves, so a failure in the steps below leaves a run that can be
|
|
409
|
+
# counted rather than work released with nothing to show for it.
|
|
410
|
+
- name: Report the failed attempt
|
|
411
|
+
if: steps.decide.outputs.retry == 'true'
|
|
412
|
+
uses: ./.github/actions/create-issue-comment
|
|
413
|
+
with:
|
|
414
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
415
|
+
issue-number: ${{ inputs.issue-number }}
|
|
416
|
+
body: |
|
|
417
|
+
${{ env.REFINE_ATTEMPT_MARKER }}
|
|
418
|
+
Attempt ${{ steps.decide.outputs.next }} of ${{ env.MAX_ATTEMPTS }} ended after ${{ steps.decide.outputs.minutes }} minutes, before the run could produce an answer.
|
|
419
|
+
${{ env.RETRY_COMMENT }}
|
|
420
|
+
[View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
|
421
|
+
- name: Release the reservation for the retry
|
|
422
|
+
if: steps.decide.outputs.retry == 'true'
|
|
423
|
+
uses: ./.github/actions/remove-issue-labels
|
|
424
|
+
with:
|
|
425
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
426
|
+
issue-number: ${{ inputs.issue-number }}
|
|
427
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
428
|
+
- name: Send the work back through the router
|
|
429
|
+
if: steps.decide.outputs.retry == 'true'
|
|
430
|
+
env:
|
|
431
|
+
GH_TOKEN: ${{ github.token }}
|
|
432
|
+
REPO: ${{ github.repository }}
|
|
433
|
+
REF: ${{ github.event.repository.default_branch }}
|
|
434
|
+
SUBJECT: ${{ inputs.issue-number }}
|
|
435
|
+
NEXT: ${{ steps.decide.outputs.next }}
|
|
436
|
+
MODE: ${{ inputs.mode }}
|
|
437
|
+
run: |
|
|
438
|
+
set -euo pipefail
|
|
439
|
+
# The provider recovers in seconds, so pause before re-entering rather than dispatching
|
|
440
|
+
# back into the same outage. The router's own classify and authorize jobs add more.
|
|
441
|
+
sleep 30
|
|
442
|
+
gh workflow run work-router.yml --repo "$REPO" --ref "$REF" -f operation=refine -f issue-number="$SUBJECT" -f mode="$MODE" -f attempts_so_far="$NEXT"
|
|
443
|
+
echo "Re-dispatched refine for $SUBJECT as attempt $NEXT."
|
|
381
444
|
- name: Release the issue
|
|
445
|
+
if: steps.decide.outputs.retry != 'true'
|
|
382
446
|
uses: ./.github/actions/remove-issue-labels
|
|
383
447
|
with:
|
|
384
448
|
token: ${{ steps.app-token.outputs.token }}
|
|
385
449
|
issue-number: ${{ inputs.issue-number }}
|
|
386
450
|
labels: ${{ env.WORKING_LABEL }}
|
|
387
451
|
- name: Flag for human review
|
|
452
|
+
if: steps.decide.outputs.retry != 'true'
|
|
388
453
|
uses: ./.github/actions/add-issue-labels
|
|
389
454
|
with:
|
|
390
455
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -393,6 +458,7 @@ jobs:
|
|
|
393
458
|
${{ env.REVIEW_LABEL }}
|
|
394
459
|
${{ env.STALLED_LABEL }}
|
|
395
460
|
- name: Report missing refinement outcome
|
|
461
|
+
if: steps.decide.outputs.retry != 'true'
|
|
396
462
|
uses: ./.github/actions/create-issue-comment
|
|
397
463
|
with:
|
|
398
464
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -17,6 +17,18 @@ env:
|
|
|
17
17
|
STALLED_LABEL: stalled
|
|
18
18
|
REFINE_LABEL: refine
|
|
19
19
|
TRIAGE_MARKER: "<!-- agent-triage -->"
|
|
20
|
+
# A run that died before it produced anything is worth repeating; one that worked and then
|
|
21
|
+
# failed produced an answer that was wrong, and repeating it buys the same wrong answer later.
|
|
22
|
+
# Duration is what separates them. Odyssey #190 died in three minutes on `Model 'glm-5-3' not
|
|
23
|
+
# found`, a gateway fault that clears in seconds, and waited on the janitor's six-hourly sweep
|
|
24
|
+
# because only the implement worker could do this.
|
|
25
|
+
TRIAGE_ATTEMPT_MARKER: "<!-- agent-triage-attempt -->"
|
|
26
|
+
MAX_ATTEMPTS: "5"
|
|
27
|
+
PARK_AT_ATTEMPT: "4"
|
|
28
|
+
# Ten rather than six: the failure that prompted this took three minutes, and a slower one on a
|
|
29
|
+
# worse day would fall outside a six-minute window and park for a fault that clears by itself.
|
|
30
|
+
RETRY_UNDER_MINUTES: "10"
|
|
31
|
+
RETRY_COMMENT: "That is what a provider outage looks like -- the run ended before it could produce an answer -- so this is being tried again from the start. It is a fresh run rather than a continuation: nothing is carried over from the attempt that failed."
|
|
20
32
|
MAX_TRIAGE_ROUNDS: "3"
|
|
21
33
|
INCOMPLETE_COMMENT: "Automated triage ended without an outcome. The triage label remains for a retry."
|
|
22
34
|
SAFE_OUTPUT_COMMENT_PREFIX: "Triage assessment"
|
|
@@ -61,6 +73,11 @@ imports:
|
|
|
61
73
|
on:
|
|
62
74
|
workflow_call:
|
|
63
75
|
inputs:
|
|
76
|
+
attempts_so_far:
|
|
77
|
+
description: Runs already made for this work that died before producing an answer. Filled by the worker when it re-dispatches itself, not by people.
|
|
78
|
+
required: false
|
|
79
|
+
type: string
|
|
80
|
+
default: '0'
|
|
64
81
|
issue-number:
|
|
65
82
|
description: Issue number to triage.
|
|
66
83
|
required: true
|
|
@@ -295,6 +312,8 @@ jobs:
|
|
|
295
312
|
permissions:
|
|
296
313
|
contents: read
|
|
297
314
|
issues: write
|
|
315
|
+
# the retry re-enters through the router, which is a workflow_dispatch
|
|
316
|
+
actions: write
|
|
298
317
|
steps:
|
|
299
318
|
- name: Checkout workflow actions
|
|
300
319
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -304,13 +323,59 @@ jobs:
|
|
|
304
323
|
with:
|
|
305
324
|
client-id: ${{ secrets.BOT_APP_ID }}
|
|
306
325
|
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
326
|
+
- name: Decide whether this failure is worth repeating
|
|
327
|
+
id: decide
|
|
328
|
+
uses: ./.github/actions/decide-agent-retry
|
|
329
|
+
with:
|
|
330
|
+
token: ${{ github.token }}
|
|
331
|
+
attempts-so-far: ${{ inputs.attempts_so_far }}
|
|
332
|
+
park-at: ${{ env.PARK_AT_ATTEMPT }}
|
|
333
|
+
under-minutes: ${{ env.RETRY_UNDER_MINUTES }}
|
|
334
|
+
# Recorded before any label moves, so a failure in the steps below leaves a run that can be
|
|
335
|
+
# counted rather than work released with nothing to show for it.
|
|
336
|
+
- name: Report the failed attempt
|
|
337
|
+
if: steps.decide.outputs.retry == 'true'
|
|
338
|
+
uses: ./.github/actions/create-issue-comment
|
|
339
|
+
with:
|
|
340
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
341
|
+
issue-number: ${{ inputs.issue-number }}
|
|
342
|
+
body: |
|
|
343
|
+
${{ env.TRIAGE_ATTEMPT_MARKER }}
|
|
344
|
+
Attempt ${{ steps.decide.outputs.next }} of ${{ env.MAX_ATTEMPTS }} ended after ${{ steps.decide.outputs.minutes }} minutes, before the run could produce an answer.
|
|
345
|
+
${{ env.RETRY_COMMENT }}
|
|
346
|
+
[View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
|
347
|
+
- name: Release the reservation for the retry
|
|
348
|
+
if: steps.decide.outputs.retry == 'true'
|
|
349
|
+
uses: ./.github/actions/remove-issue-labels
|
|
350
|
+
with:
|
|
351
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
352
|
+
issue-number: ${{ inputs.issue-number }}
|
|
353
|
+
labels: ${{ env.WORKING_LABEL }}
|
|
354
|
+
- name: Send the work back through the router
|
|
355
|
+
if: steps.decide.outputs.retry == 'true'
|
|
356
|
+
env:
|
|
357
|
+
GH_TOKEN: ${{ github.token }}
|
|
358
|
+
REPO: ${{ github.repository }}
|
|
359
|
+
REF: ${{ github.event.repository.default_branch }}
|
|
360
|
+
SUBJECT: ${{ inputs.issue-number }}
|
|
361
|
+
NEXT: ${{ steps.decide.outputs.next }}
|
|
362
|
+
MODE: ${{ inputs.mode }}
|
|
363
|
+
run: |
|
|
364
|
+
set -euo pipefail
|
|
365
|
+
# The provider recovers in seconds, so pause before re-entering rather than dispatching
|
|
366
|
+
# back into the same outage. The router's own classify and authorize jobs add more.
|
|
367
|
+
sleep 30
|
|
368
|
+
gh workflow run work-router.yml --repo "$REPO" --ref "$REF" -f operation=triage -f issue-number="$SUBJECT" -f mode="$MODE" -f attempts_so_far="$NEXT"
|
|
369
|
+
echo "Re-dispatched triage for $SUBJECT as attempt $NEXT."
|
|
307
370
|
- name: Release the issue
|
|
371
|
+
if: steps.decide.outputs.retry != 'true'
|
|
308
372
|
uses: ./.github/actions/remove-issue-labels
|
|
309
373
|
with:
|
|
310
374
|
token: ${{ steps.app-token.outputs.token }}
|
|
311
375
|
issue-number: ${{ inputs.issue-number }}
|
|
312
376
|
labels: ${{ env.WORKING_LABEL }}
|
|
313
377
|
- name: Flag for human review
|
|
378
|
+
if: steps.decide.outputs.retry != 'true'
|
|
314
379
|
uses: ./.github/actions/add-issue-labels
|
|
315
380
|
with:
|
|
316
381
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -319,6 +384,7 @@ jobs:
|
|
|
319
384
|
${{ env.REVIEW_LABEL }}
|
|
320
385
|
${{ env.STALLED_LABEL }}
|
|
321
386
|
- name: Report missing triage outcome
|
|
387
|
+
if: steps.decide.outputs.retry != 'true'
|
|
322
388
|
uses: ./.github/actions/create-issue-comment
|
|
323
389
|
with:
|
|
324
390
|
token: ${{ steps.app-token.outputs.token }}
|
|
@@ -358,7 +358,7 @@ jobs:
|
|
|
358
358
|
ci-conclusion: ${{ steps.route.outputs.ci-conclusion }}
|
|
359
359
|
ci-run-id: ${{ steps.route.outputs.ci-run-id }}
|
|
360
360
|
merge-gate-attempts: ${{ steps.route.outputs.merge-gate-attempts }}
|
|
361
|
-
|
|
361
|
+
attempts: ${{ steps.route.outputs.attempts }}
|
|
362
362
|
refine-mode: ${{ steps.route.outputs.refine-mode }}
|
|
363
363
|
triage-mode: ${{ steps.route.outputs.triage-mode }}
|
|
364
364
|
trigger-kind: ${{ steps.route.outputs.trigger-kind }}
|
|
@@ -388,6 +388,7 @@ jobs:
|
|
|
388
388
|
with:
|
|
389
389
|
issue-number: ${{ needs.classify.outputs.issue-number }}
|
|
390
390
|
mode: ${{ needs.classify.outputs.refine-mode }}
|
|
391
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
391
392
|
secrets:
|
|
392
393
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
393
394
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -481,7 +482,7 @@ jobs:
|
|
|
481
482
|
permissions: write-all
|
|
482
483
|
with:
|
|
483
484
|
issue-number: ${{ needs.classify.outputs.issue-number }}
|
|
484
|
-
attempts_so_far: ${{ needs.classify.outputs.
|
|
485
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
485
486
|
secrets:
|
|
486
487
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
487
488
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -533,6 +534,7 @@ jobs:
|
|
|
533
534
|
with:
|
|
534
535
|
issue-number: ${{ needs.classify.outputs.issue-number }}
|
|
535
536
|
mode: ${{ needs.classify.outputs.triage-mode }}
|
|
537
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
536
538
|
secrets:
|
|
537
539
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
538
540
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
@@ -552,6 +554,7 @@ jobs:
|
|
|
552
554
|
with:
|
|
553
555
|
pr-number: ${{ needs.classify.outputs.pr-number }}
|
|
554
556
|
linked-issue: ${{ needs.classify.outputs.linked-issue }}
|
|
557
|
+
attempts_so_far: ${{ needs.classify.outputs.attempts }}
|
|
555
558
|
secrets:
|
|
556
559
|
OPENAI_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
|
557
560
|
CODEX_API_KEY: ${{ secrets.FORGE_API_KEY }}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plainconceptsplatform/workflows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Install and update Platform GitHub agentic workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -25,13 +25,6 @@
|
|
|
25
25
|
"loops",
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
|
-
"scripts": {
|
|
29
|
-
"build": "tsc --project tsconfig.build.json && node ./scripts/copy-loops.mjs",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
32
|
-
"release": "pnpm build && pnpm test && npm publish --access public",
|
|
33
|
-
"prepack": "node ./scripts/copy-loops.mjs"
|
|
34
|
-
},
|
|
35
28
|
"engines": {
|
|
36
29
|
"node": ">=20.19"
|
|
37
30
|
},
|
|
@@ -42,5 +35,11 @@
|
|
|
42
35
|
"@types/node": "^22.0.0",
|
|
43
36
|
"typescript": "^5.8.0",
|
|
44
37
|
"vitest": "^3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc --project tsconfig.build.json && node ./scripts/copy-loops.mjs",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
43
|
+
"release": "pnpm build && pnpm test && npm publish --access public"
|
|
45
44
|
}
|
|
46
|
-
}
|
|
45
|
+
}
|