@plainconceptsplatform/workflows 1.0.13 → 1.1.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.
@@ -1,350 +0,0 @@
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
- # The gate job the top-level `if:` reads. gh-aw folds that `if:` into the generated
47
- # activation job but gives activation no dependency on the job, so the reference resolves
48
- # to '' and the clause is false -- the agent would never run.
49
- needs: [subject]
50
-
51
- # Rung 3-4. The agent reads the verification plan and writes waypoints as JSON; a post-agent
52
- # shell step runs agent-browser to capture screenshots outside the awf sandbox.
53
- jobs:
54
- subject:
55
- runs-on: agents-arc
56
- timeout-minutes: 5
57
- permissions:
58
- contents: read
59
- issues: read
60
- pull-requests: read
61
- outputs:
62
- found: ${{ steps.subject.outputs.found }}
63
- pr: ${{ steps.subject.outputs.pr }}
64
- issue: ${{ steps.subject.outputs.issue }}
65
- enabled: ${{ steps.config.outputs.enabled }}
66
- steps:
67
- - name: Checkout workflow actions
68
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
69
- with:
70
- persist-credentials: false
71
- # Rung 4. A step can read the env: context; a job-level if: cannot. The enablement
72
- # check therefore happens here and the agent gate reads the output -- a runnable
73
- # snippet on an unconfigured repository burns the model otherwise.
74
- - name: Read the visual-verify configuration
75
- id: config
76
- env:
77
- ENABLED: ${{ env.VISUAL_VERIFY_ENABLED }}
78
- START_COMMAND: ${{ env.VISUAL_VERIFY_START_COMMAND }}
79
- run: |
80
- set -euo pipefail
81
- if [ "$ENABLED" = "true" ] && [ -n "$START_COMMAND" ]; then
82
- echo "enabled=true" >> "$GITHUB_OUTPUT"
83
- else
84
- echo "enabled=false" >> "$GITHUB_OUTPUT"
85
- echo "::notice::visual verification is not configured (VISUAL_VERIFY_ENABLED=$ENABLED, start command ${START_COMMAND:-unset}); the agent will not run."
86
- fi
87
- - name: Identify the pull request and its issue
88
- id: subject
89
- env:
90
- GH_TOKEN: ${{ github.token }}
91
- REPO: ${{ github.repository }}
92
- PR: ${{ inputs.pr-number }}
93
- ISSUE: ${{ inputs.linked-issue }}
94
- run: |
95
- set -euo pipefail
96
- pr=$(gh pr view "$PR" --repo "$REPO" --json state,closingIssuesReferences,body)
97
- [ "$(jq -r '.state' <<<"$pr")" = "OPEN" ] || { echo "found=false"; exit 0; }
98
- echo "found=true" >> "$GITHUB_OUTPUT"
99
- echo "pr=$PR" >> "$GITHUB_OUTPUT"
100
- issue="$ISSUE"
101
- if [ -z "$issue" ]; then
102
- issue="$(jq -r '.closingIssuesReferences[0].number // empty' <<<"$pr")"
103
- fi
104
- if [ -z "$issue" ]; then
105
- issue="$(jq -r '.body // ""' <<<"$pr" |
106
- grep -oiE '(close[sd]?|fixe?[sd]?|resolve[sd]?) +#[0-9]+' |
107
- grep -oE '[0-9]+' | head -n 1 || true)"
108
- fi
109
- echo "issue=$issue" >> "$GITHUB_OUTPUT"
110
-
111
- checkout:
112
- fetch: ["*"]
113
- fetch-depth: 0
114
-
115
- permissions: read-all
116
-
117
- steps:
118
- - name: Check out the pull request branch
119
- env:
120
- GH_TOKEN: ${{ github.token }}
121
- REPO: ${{ github.repository }}
122
- PR: ${{ needs.subject.outputs.pr }}
123
- run: |
124
- set -euo pipefail
125
- branch=$(gh pr view "$PR" --repo "$REPO" --json headRefName --jq '.headRefName')
126
- git switch --track "origin/$branch" 2>/dev/null || git switch "$branch"
127
- echo "On $(git branch --show-current) at $(git rev-parse --short HEAD)"
128
- - name: Load the issue context
129
- uses: ./.github/actions/load-issue-context
130
- with:
131
- token: ${{ github.token }}
132
- issue-number: ${{ needs.subject.outputs.issue }}
133
- output-path: ${{ env.ISSUE_CONTEXT_PATH }}
134
-
135
- safe-outputs:
136
- # Path A: the framework writes, so this worker does not stage and has no apply step in
137
- # conclude. Staged here would mean the framework writes nothing and conclude would have to
138
- # apply the comment, and conclude does not: the run would go green and the issue would
139
- # never hear anything. That is staged-without-apply, the failure refine shipped once, not a
140
- # property of staged itself. This worker stays on Path A and lets safe_outputs write.
141
- report-failure-as-issue: false
142
- threat-detection: false
143
- add-comment:
144
- target: "*"
145
-
146
- post-steps:
147
- - name: Install agent-browser
148
- if: env.VISUAL_VERIFY_ENABLED == 'true' && env.VISUAL_VERIFY_START_COMMAND != ''
149
- shell: bash
150
- run: |
151
- set -euo pipefail
152
- mkdir -p /tmp/gh-aw/screenshots
153
- # Unscoped: the package is Vercel Labs' agent-browser. The scoped
154
- # @anthropic-ai/agent-browser does not exist on npm and 404s the install, which
155
- # took down the agent job on the first run that ever reached this step.
156
- npm install -g "agent-browser@${{ env.AGENT_BROWSER_VERSION }}"
157
- # The npm package alone cannot drive a browser: the Chromium binary is a separate
158
- # download, and a self-hosted Linux runner needs the system dependencies too.
159
- # --with-deps is agent-browser's best effort but misses libraries on ubuntu-24.04.
160
- chrome_dir=$(agent-browser path 2>/dev/null | grep -oE '/[^ ]+chrome[^ ]*' | head -1 || true)
161
- if [ -n "$chrome_dir" ] && [ -d "$(dirname "$chrome_dir")/.." ]; then
162
- ldd "$chrome_dir" 2>/dev/null | grep "not found" || true
163
- fi
164
- # Install the known missing Chromium dependencies on Debian/ubuntu runners.
165
- # agent-browser install --with-deps may leave these out depending on the runner image.
166
- sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends \
167
- libnss3 libatk1.0-0 libatk-bridge2.0-0 libdrm2 libgbm1 \
168
- libasound2t64 libxkbcommon0 libcairo2 libpango-1.0-0 libxcomposite1 \
169
- libxdamage1 libxfixes3 libxrandr2 2>/dev/null || true
170
- agent-browser install --with-deps
171
- agent-browser --version
172
-
173
- - name: Build and start the app
174
- if: env.VISUAL_VERIFY_ENABLED == 'true' && env.VISUAL_VERIFY_START_COMMAND != ''
175
- id: app
176
- shell: bash
177
- env:
178
- BUILD_COMMAND: ${{ env.VISUAL_VERIFY_BUILD_COMMAND }}
179
- START_COMMAND: ${{ env.VISUAL_VERIFY_START_COMMAND }}
180
- PORT: ${{ env.VISUAL_VERIFY_PORT }}
181
- WAIT_SECONDS: ${{ env.VISUAL_VERIFY_WAIT_SECONDS }}
182
- run: |
183
- set -euo pipefail
184
- if [ -n "$BUILD_COMMAND" ]; then
185
- echo "::group::Build"
186
- $BUILD_COMMAND
187
- echo "::endgroup::"
188
- fi
189
- echo "::group::Start app"
190
- $START_COMMAND &
191
- APP_PID=$!
192
- echo "app_pid=$APP_PID" >> "$GITHUB_OUTPUT"
193
- for i in $(seq 1 "$WAIT_SECONDS"); do
194
- if curl -sf "http://localhost:$PORT" >/dev/null 2>&1; then
195
- echo "App is up on port $PORT after ${i}s"
196
- echo "app_started=true" >> "$GITHUB_OUTPUT"
197
- exit 0
198
- fi
199
- sleep 1
200
- done
201
- echo "::warning::App did not respond on port $PORT within ${WAIT_SECONDS}s"
202
- echo "app_started=false" >> "$GITHUB_OUTPUT"
203
-
204
- - name: Run agent-browser and capture screenshots
205
- if: steps.app.outputs.app_started == 'true'
206
- id: capture
207
- shell: bash
208
- env:
209
- PORT: ${{ env.VISUAL_VERIFY_PORT }}
210
- PLAN_FILE: /tmp/gh-aw/screenshots/plan.json
211
- OUTPUT_DIR: /tmp/gh-aw/screenshots
212
- run: |
213
- set -euo pipefail
214
- if [ ! -f "$PLAN_FILE" ]; then
215
- echo "::warning::No verification plan found at $PLAN_FILE; skipping browser capture"
216
- echo "captured=false" >> "$GITHUB_OUTPUT"
217
- exit 0
218
- fi
219
- waypoint_count=$(jq -r '.waypoints | length' "$PLAN_FILE")
220
- echo "Found $waypoint_count waypoints"
221
- captured=0
222
- i=0
223
- # agent-browser uses Chromium which needs --no-sandbox on CI runners where
224
- # there is no SYS_ADMIN capability. Set it once via the config file.
225
- export AGENT_BROWSER_ARGS="${AGENT_BROWSER_ARGS:-} --no-sandbox"
226
- while [ "$i" -lt "$waypoint_count" ]; do
227
- i=$((i + 1))
228
- url=$(jq -r ".waypoints[$((i - 1))].url" "$PLAN_FILE")
229
- selector=$(jq -r ".waypoints[$((i - 1))].selector // empty" "$PLAN_FILE")
230
- echo "Waypoint $i/$waypoint_count: $url (waiting for: ${selector:-nothing})"
231
- # A failing waypoint must not lose the screenshots already taken: the count is
232
- # reported and the run stays green, because evidence is what this worker produces.
233
- if ! agent-browser open "http://localhost:$PORT$url"; then
234
- echo "::warning::waypoint $i: could not open $url; skipping"
235
- continue
236
- fi
237
- if [ -n "$selector" ]; then
238
- agent-browser wait "$selector" || echo "::warning::waypoint $i: selector $selector did not appear; screenshotting anyway"
239
- fi
240
- agent-browser wait --load networkidle || true
241
- if agent-browser screenshot "$OUTPUT_DIR/waypoint-$i.png" --full; then
242
- captured=$((captured + 1))
243
- else
244
- echo "::warning::waypoint $i: screenshot failed"
245
- # Diagnostic: show what's in the output dir and any browser errors
246
- ls -la "$OUTPUT_DIR/" 2>/dev/null || true
247
- echo "--- agent-browser debug ---"
248
- agent-browser screenshot "$OUTPUT_DIR/debug-$i.png" --full 2>&1 || true
249
- fi
250
- done
251
- echo "captured=$([ "$captured" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
252
- echo "screenshot_count=$captured" >> "$GITHUB_OUTPUT"
253
-
254
- # The attach action reads the screenshots directory; on no capture it explains that
255
- # instead (app never started, no plan, or every waypoint failed). `always()` because it
256
- # must run even when the capture step above warned its way to the end.
257
- - name: Attach screenshots to the issue
258
- if: always() && env.VISUAL_VERIFY_ENABLED == 'true' && env.VISUAL_VERIFY_START_COMMAND != ''
259
- uses: ./.github/actions/attach-screenshots
260
- with:
261
- token: ${{ github.token }}
262
- issue-number: ${{ needs.subject.outputs.issue }}
263
- pr-number: ${{ needs.subject.outputs.pr }}
264
- screenshots-dir: /tmp/gh-aw/screenshots
265
- screenshot-count: ${{ steps.capture.outputs.screenshot_count || '0' }}
266
- app-started: ${{ steps.app.outputs.app_started || 'false' }}
267
- enabled: ${{ env.VISUAL_VERIFY_ENABLED }}
268
-
269
- - name: Shut down the app
270
- if: always() && steps.app.outputs.app_pid != ''
271
- shell: bash
272
- env:
273
- APP_PID: ${{ steps.app.outputs.app_pid }}
274
- run: |
275
- kill "$APP_PID" 2>/dev/null || true
276
-
277
- # `!failure()` because subject skips on an unconfigured repository and a closed pull
278
- # request: without a status function the implicit success() would let those skips look
279
- # like failures to every job that needs this one.
280
- if: needs.subject.outputs.found == 'true' && needs.subject.outputs.enabled == 'true' && !failure()
281
-
282
- timeout-minutes: 15
283
-
284
- engine:
285
- id: opencode
286
- version: "1.2.14"
287
- env:
288
- OPENAI_BASE_URL: https://forge.plainconcepts.com/v1
289
-
290
- model: openai/glm-5-2
291
- max-turns: 120
292
- max-turn-cache-misses: 3000
293
- max-ai-credits: 5000
294
- ---
295
-
296
- 1. You are visually verifying pull request **#${{ needs.subject.outputs.pr }}**, which closes
297
- issue **#${{ needs.subject.outputs.issue }}**. The merge gate has already determined this
298
- pull request can be auto-merged: CI is green, no conflicts, no blocking findings.
299
-
300
- You are on the pull request branch. Do not rebase, reset, amend or otherwise rewrite history.
301
- Do not push anything: this worker produces evidence, not code changes.
302
-
303
- 2. Read `${{ env.ISSUE_CONTEXT_PATH }}`. It contains the issue body and its discussion. The
304
- acceptance criteria there define what this implementation was supposed to do.
305
-
306
- 3. Run `/repo-verify`. This harness skill writes `verification-plan.md` into the change
307
- directory with a journey of agent-browser waypoints (open/find/wait/capture/expect). Each
308
- waypoint names a URL, a selector to wait for, and what to observe.
309
-
310
- If the plan says `not-applicable` (no UI surface is reachable for this change a backend-
311
- only change to an API or a migration, for example), that is a valid, expected outcome. Write
312
- a summary comment saying the change has no UI surface and stop. Do not attempt to start a
313
- browser.
314
-
315
- 4. If `${{ env.VISUAL_VERIFY_START_COMMAND }}` is empty, you cannot start the app. Write a
316
- summary comment noting that the verification plan was written but no browser session was
317
- available because the consumer has not set `VISUAL_VERIFY_START_COMMAND`. Stop.
318
-
319
- 5. Read `verification-plan.md`. Parse each waypoint into a JSON structure and write it to
320
- `/tmp/gh-aw/screenshots/plan.json`. Each waypoint has:
321
-
322
- ```json
323
- {
324
- "waypoints": [
325
- {
326
- "url": "/path",
327
- "selector": "button[data-testid='submit']",
328
- "description": "Submit form with new field visible",
329
- "expect": "form succeeds"
330
- }
331
- ]
332
- }
333
- ```
334
-
335
- Use relative paths; the workflow prepends `http://localhost:{{PORT}}` when it runs
336
- agent-browser in the post-agent step.
337
-
338
- 6. Emit exactly one `add_comment` targeting issue `${{ needs.subject.outputs.issue }}`,
339
- containing, in this order:
340
-
341
- 1. `${{ env.VERIFY_MARKER }}`
342
- 2. A heading: `## Visual verification for PR #${{ needs.subject.outputs.pr }}`
343
- 3. One line per waypoint: the description and the selector being waited for
344
- 4. A note that screenshots will be attached by the post-agent step
345
-
346
- The post-agent step runs agent-browser with your `plan.json`, captures a screenshot per
347
- waypoint, and attaches them to this comment. You do not capture screenshots yourself.
348
-
349
- If `/repo-verify` returned `not-applicable`, say so in the comment instead and emit no
350
- `plan.json`. The post-agent step will see no plan file and report that gracefully.