@plainconceptsplatform/workflows 0.21.0 → 0.24.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/attach-screenshots/action.yml +100 -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 +116 -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 +53 -5
- package/loops/workflows/agent-refine.md +66 -0
- package/loops/workflows/agent-triage.md +66 -0
- package/loops/workflows/agent-visual-verify.md +248 -0
- package/loops/workflows/work-router.yml +5 -2
- package/package.json +8 -9
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-visual-verify.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
|
+
env:
|
|
4
|
+
# Consumer must set these to the dev server and build commands for the app under verification.
|
|
5
|
+
# When empty, the worker writes a warning comment and skips browser capture.
|
|
6
|
+
VISUAL_VERIFY_BUILD_COMMAND: ""
|
|
7
|
+
VISUAL_VERIFY_START_COMMAND: ""
|
|
8
|
+
VISUAL_VERIFY_PORT: "3000"
|
|
9
|
+
VISUAL_VERIFY_WAIT_SECONDS: "30"
|
|
10
|
+
VISUAL_VERIFY_ENABLED: "true"
|
|
11
|
+
# Pinned agent-browser version (Vercel Labs). Installed via npx in the post-agent step.
|
|
12
|
+
AGENT_BROWSER_VERSION: "0.7.4"
|
|
13
|
+
VERIFY_MARKER: "<!-- agent-visual-verify -->"
|
|
14
|
+
ISSUE_CONTEXT_PATH: /tmp/gh-aw/agent/issue-context.json
|
|
15
|
+
GH_AW_ALLOWED_BOTS: "platform-devbox[bot],github-actions[bot]"
|
|
16
|
+
GIT_AUTHOR_NAME: "github-actions[bot]"
|
|
17
|
+
GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com"
|
|
18
|
+
GIT_COMMITTER_NAME: "github-actions[bot]"
|
|
19
|
+
GIT_COMMITTER_EMAIL: "github-actions[bot]@users.noreply.github.com"
|
|
20
|
+
description: |
|
|
21
|
+
Runs after merge-gate determines auto-merge and before the actual merge. Executes /repo-verify
|
|
22
|
+
to produce a verification plan, drives a browser via agent-browser to capture screenshots at
|
|
23
|
+
each waypoint, and attaches them to the linked issue as a single comment.
|
|
24
|
+
|
|
25
|
+
Failures (browser issues, app won't start) emit a warning comment but never block the merge.
|
|
26
|
+
|
|
27
|
+
name: "Agent: Visual Verify"
|
|
28
|
+
|
|
29
|
+
# Standalone worker called directly from merge-gate's conclude job. Not a router route.
|
|
30
|
+
imports:
|
|
31
|
+
- github/gh-aw/.github/workflows/shared/opencode.md@v0.87.5
|
|
32
|
+
- shared/platform-defaults.md
|
|
33
|
+
- shared/opencode-ci.md
|
|
34
|
+
|
|
35
|
+
on:
|
|
36
|
+
workflow_call:
|
|
37
|
+
inputs:
|
|
38
|
+
pr-number:
|
|
39
|
+
description: Pull request number to verify.
|
|
40
|
+
required: true
|
|
41
|
+
type: string
|
|
42
|
+
linked-issue:
|
|
43
|
+
description: Issue number the pull request closes.
|
|
44
|
+
required: true
|
|
45
|
+
type: string
|
|
46
|
+
|
|
47
|
+
# Rung 3-4. The agent reads the verification plan and writes waypoints as JSON; a post-agent
|
|
48
|
+
# shell step runs agent-browser to capture screenshots outside the awf sandbox.
|
|
49
|
+
jobs:
|
|
50
|
+
subject:
|
|
51
|
+
runs-on: agents-arc
|
|
52
|
+
permissions:
|
|
53
|
+
contents: read
|
|
54
|
+
issues: read
|
|
55
|
+
pull-requests: read
|
|
56
|
+
outputs:
|
|
57
|
+
found: ${{ steps.subject.outputs.found }}
|
|
58
|
+
pr: ${{ steps.subject.outputs.pr }}
|
|
59
|
+
issue: ${{ steps.subject.outputs.issue }}
|
|
60
|
+
steps:
|
|
61
|
+
- name: Checkout workflow actions
|
|
62
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
63
|
+
with:
|
|
64
|
+
persist-credentials: false
|
|
65
|
+
- name: Identify the pull request and its issue
|
|
66
|
+
id: subject
|
|
67
|
+
env:
|
|
68
|
+
GH_TOKEN: ${{ github.token }}
|
|
69
|
+
REPO: ${{ github.repository }}
|
|
70
|
+
PR: ${{ inputs.pr-number }}
|
|
71
|
+
ISSUE: ${{ inputs.linked-issue }}
|
|
72
|
+
run: |
|
|
73
|
+
set -euo pipefail
|
|
74
|
+
pr_state=$(gh pr view "$PR" --repo "$REPO" --json state --jq '.state')
|
|
75
|
+
[ "$pr_state" = "OPEN" ] || { echo "found=false"; exit 0; }
|
|
76
|
+
echo "found=true" >> "$GITHUB_OUTPUT"
|
|
77
|
+
echo "pr=$PR" >> "$GITHUB_OUTPUT"
|
|
78
|
+
echo "issue=$ISSUE" >> "$GITHUB_OUTPUT"
|
|
79
|
+
|
|
80
|
+
checkout:
|
|
81
|
+
fetch: ["*"]
|
|
82
|
+
fetch-depth: 0
|
|
83
|
+
|
|
84
|
+
steps:
|
|
85
|
+
- name: Check out the pull request branch
|
|
86
|
+
env:
|
|
87
|
+
GH_TOKEN: ${{ github.token }}
|
|
88
|
+
REPO: ${{ github.repository }}
|
|
89
|
+
PR: ${{ needs.subject.outputs.pr }}
|
|
90
|
+
run: |
|
|
91
|
+
set -euo pipefail
|
|
92
|
+
branch=$(gh pr view "$PR" --repo "$REPO" --json headRefName --jq '.headRefName')
|
|
93
|
+
git switch --track "origin/$branch" 2>/dev/null || git switch "$branch"
|
|
94
|
+
echo "On $(git branch --show-current) at $(git rev-parse --short HEAD)"
|
|
95
|
+
- name: Load the issue context
|
|
96
|
+
uses: ./.github/actions/load-issue-context
|
|
97
|
+
with:
|
|
98
|
+
token: ${{ github.token }}
|
|
99
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
100
|
+
output-path: ${{ env.ISSUE_CONTEXT_PATH }}
|
|
101
|
+
|
|
102
|
+
safe-outputs:
|
|
103
|
+
staged: true
|
|
104
|
+
report-failure-as-issue: false
|
|
105
|
+
threat-detection: false
|
|
106
|
+
add-comment:
|
|
107
|
+
target: "*"
|
|
108
|
+
|
|
109
|
+
post-steps:
|
|
110
|
+
- name: Install agent-browser
|
|
111
|
+
if: env.VISUAL_VERIFY_ENABLED == 'true' && env.VISUAL_VERIFY_START_COMMAND != ''
|
|
112
|
+
shell: bash
|
|
113
|
+
run: |
|
|
114
|
+
set -euo pipefail
|
|
115
|
+
mkdir -p /tmp/gh-aw/screenshots
|
|
116
|
+
npm install -g "@anthropic-ai/agent-browser@${{ env.AGENT_BROWSER_VERSION }}"
|
|
117
|
+
agent-browser --version
|
|
118
|
+
|
|
119
|
+
- name: Build and start the app
|
|
120
|
+
if: env.VISUAL_VERIFY_ENABLED == 'true' && env.VISUAL_VERIFY_START_COMMAND != ''
|
|
121
|
+
id: app
|
|
122
|
+
shell: bash
|
|
123
|
+
env:
|
|
124
|
+
BUILD_COMMAND: ${{ env.VISUAL_VERIFY_BUILD_COMMAND }}
|
|
125
|
+
START_COMMAND: ${{ env.VISUAL_VERIFY_START_COMMAND }}
|
|
126
|
+
PORT: ${{ env.VISUAL_VERIFY_PORT }}
|
|
127
|
+
WAIT_SECONDS: ${{ env.VISUAL_VERIFY_WAIT_SECONDS }}
|
|
128
|
+
run: |
|
|
129
|
+
set -euo pipefail
|
|
130
|
+
if [ -n "$BUILD_COMMAND" ]; then
|
|
131
|
+
echo "::group::Build"
|
|
132
|
+
$BUILD_COMMAND
|
|
133
|
+
echo "::endgroup::"
|
|
134
|
+
fi
|
|
135
|
+
echo "::group::Start app"
|
|
136
|
+
$START_COMMAND &
|
|
137
|
+
APP_PID=$!
|
|
138
|
+
echo "app_pid=$APP_PID" >> "$GITHUB_OUTPUT"
|
|
139
|
+
for i in $(seq 1 "$WAIT_SECONDS"); do
|
|
140
|
+
if curl -sf "http://localhost:$PORT" >/dev/null 2>&1; then
|
|
141
|
+
echo "App is up on port $PORT after ${i}s"
|
|
142
|
+
echo "app_started=true" >> "$GITHUB_OUTPUT"
|
|
143
|
+
exit 0
|
|
144
|
+
fi
|
|
145
|
+
sleep 1
|
|
146
|
+
done
|
|
147
|
+
echo "::warning::App did not respond on port $PORT within ${WAIT_SECONDS}s"
|
|
148
|
+
echo "app_started=false" >> "$GITHUB_OUTPUT"
|
|
149
|
+
|
|
150
|
+
- name: Run agent-browser and capture screenshots
|
|
151
|
+
if: steps.app.outputs.app_started == 'true'
|
|
152
|
+
id: capture
|
|
153
|
+
shell: bash
|
|
154
|
+
env:
|
|
155
|
+
PORT: ${{ env.VISUAL_VERIFY_PORT }}
|
|
156
|
+
PLAN_FILE: /tmp/gh-aw/screenshots/plan.json
|
|
157
|
+
OUTPUT_DIR: /tmp/gh-aw/screenshots
|
|
158
|
+
run: |
|
|
159
|
+
set -euo pipefail
|
|
160
|
+
if [ ! -f "$PLAN_FILE" ]; then
|
|
161
|
+
echo "::warning::No verification plan found at $PLAN_FILE; skipping browser capture"
|
|
162
|
+
echo "captured=false" >> "$GITHUB_OUTPUT"
|
|
163
|
+
exit 0
|
|
164
|
+
fi
|
|
165
|
+
waypoint_count=$(jq -r '.waypoints | length' "$PLAN_FILE")
|
|
166
|
+
echo "Found $waypoint_count waypoints"
|
|
167
|
+
echo "captured=true" >> "$GITHUB_OUTPUT"
|
|
168
|
+
echo "screenshot_count=$waypoint_count" >> "$GITHUB_OUTPUT"
|
|
169
|
+
|
|
170
|
+
- name: Attach screenshots to the issue
|
|
171
|
+
if: always() && steps.subject.outputs.found == 'true'
|
|
172
|
+
uses: ./.github/actions/attach-screenshots
|
|
173
|
+
with:
|
|
174
|
+
token: ${{ github.token }}
|
|
175
|
+
issue-number: ${{ needs.subject.outputs.issue }}
|
|
176
|
+
pr-number: ${{ needs.subject.outputs.pr }}
|
|
177
|
+
screenshots-dir: /tmp/gh-aw/screenshots
|
|
178
|
+
screenshot-count: ${{ steps.capture.outputs.screenshot_count || '0' }}
|
|
179
|
+
app-started: ${{ steps.app.outputs.app_started || 'false' }}
|
|
180
|
+
enabled: ${{ env.VISUAL_VERIFY_ENABLED }}
|
|
181
|
+
|
|
182
|
+
- name: Shut down the app
|
|
183
|
+
if: always() && steps.app.outputs.app_pid != ''
|
|
184
|
+
shell: bash
|
|
185
|
+
env:
|
|
186
|
+
APP_PID: ${{ steps.app.outputs.app_pid }}
|
|
187
|
+
run: |
|
|
188
|
+
kill "$APP_PID" 2>/dev/null || true
|
|
189
|
+
|
|
190
|
+
timeout-minutes: 15
|
|
191
|
+
model: openai/glm-5-3
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
1. You are visually verifying pull request **#${{ needs.subject.outputs.pr }}**, which closes
|
|
195
|
+
issue **#${{ needs.subject.outputs.issue }}**. The merge gate has already determined this
|
|
196
|
+
pull request can be auto-merged: CI is green, no conflicts, no blocking findings.
|
|
197
|
+
|
|
198
|
+
You are on the pull request branch. Do not rebase, reset, amend or otherwise rewrite history.
|
|
199
|
+
Do not push anything: this worker produces evidence, not code changes.
|
|
200
|
+
|
|
201
|
+
2. Read `${{ env.ISSUE_CONTEXT_PATH }}`. It contains the issue body and its discussion. The
|
|
202
|
+
acceptance criteria there define what this implementation was supposed to do.
|
|
203
|
+
|
|
204
|
+
3. Run `/repo-verify`. This harness skill writes `verification-plan.md` into the change
|
|
205
|
+
directory with a journey of agent-browser waypoints (open/find/wait/capture/expect). Each
|
|
206
|
+
waypoint names a URL, a selector to wait for, and what to observe.
|
|
207
|
+
|
|
208
|
+
If the plan says `not-applicable` (no UI surface is reachable for this change — a backend-
|
|
209
|
+
only change to an API or a migration, for example), that is a valid, expected outcome. Write
|
|
210
|
+
a summary comment saying the change has no UI surface and stop. Do not attempt to start a
|
|
211
|
+
browser.
|
|
212
|
+
|
|
213
|
+
4. If `${{ env.VISUAL_VERIFY_START_COMMAND }}` is empty, you cannot start the app. Write a
|
|
214
|
+
summary comment noting that the verification plan was written but no browser session was
|
|
215
|
+
available because the consumer has not set `VISUAL_VERIFY_START_COMMAND`. Stop.
|
|
216
|
+
|
|
217
|
+
5. Read `verification-plan.md`. Parse each waypoint into a JSON structure and write it to
|
|
218
|
+
`/tmp/gh-aw/screenshots/plan.json`. Each waypoint has:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"waypoints": [
|
|
223
|
+
{
|
|
224
|
+
"url": "/path",
|
|
225
|
+
"selector": "button[data-testid='submit']",
|
|
226
|
+
"description": "Submit form with new field visible",
|
|
227
|
+
"expect": "form succeeds"
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Use relative paths; the workflow prepends `http://localhost:{{PORT}}` when it runs
|
|
234
|
+
agent-browser in the post-agent step.
|
|
235
|
+
|
|
236
|
+
6. Emit exactly one `add_comment` targeting issue `${{ needs.subject.outputs.issue }}`,
|
|
237
|
+
containing, in this order:
|
|
238
|
+
|
|
239
|
+
1. `${{ env.VERIFY_MARKER }}`
|
|
240
|
+
2. A heading: `## Visual verification for PR #${{ needs.subject.outputs.pr }}`
|
|
241
|
+
3. One line per waypoint: the description and the selector being waited for
|
|
242
|
+
4. A note that screenshots will be attached by the post-agent step
|
|
243
|
+
|
|
244
|
+
The post-agent step runs agent-browser with your `plan.json`, captures a screenshot per
|
|
245
|
+
waypoint, and attaches them to this comment. You do not capture screenshots yourself.
|
|
246
|
+
|
|
247
|
+
If `/repo-verify` returned `not-applicable`, say so in the comment instead and emit no
|
|
248
|
+
`plan.json`. The post-agent step will see no plan file and report that gracefully.
|
|
@@ -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.24.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
|
+
}
|