@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.
@@ -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
- implement-attempts: ${{ steps.route.outputs.implement-attempts }}
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.implement-attempts }}
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.21.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
+ }