@plainconceptsplatform/workflows 0.23.0 → 0.24.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.
@@ -0,0 +1,100 @@
1
+ # Managed by @plainconceptsplatform/workflows. Source: loops/actions/attach-screenshots/action.yml. Update with `workflows update --force`; consumer edits may be overwritten.
2
+ name: Attach screenshots
3
+ description: Post screenshots captured by agent-browser to the linked issue as a single comment.
4
+ inputs:
5
+ token:
6
+ description: GitHub token with issues:write access.
7
+ required: true
8
+ issue-number:
9
+ description: Issue number to comment on.
10
+ required: true
11
+ pr-number:
12
+ description: Pull request number being verified.
13
+ required: true
14
+ screenshots-dir:
15
+ description: Directory containing screenshots and plan.json.
16
+ required: true
17
+ screenshot-count:
18
+ description: Number of screenshots captured (0 if none).
19
+ required: false
20
+ default: '0'
21
+ app-started:
22
+ description: Whether the app started successfully.
23
+ required: false
24
+ default: 'false'
25
+ enabled:
26
+ description: Whether visual verification was enabled.
27
+ required: false
28
+ default: 'true'
29
+ runs:
30
+ using: composite
31
+ steps:
32
+ - name: Attach screenshots to issue
33
+ shell: bash
34
+ env:
35
+ GH_TOKEN: ${{ inputs.token }}
36
+ REPO: ${{ github.repository }}
37
+ ISSUE: ${{ inputs.issue-number }}
38
+ PR: ${{ inputs.pr-number }}
39
+ SCREENSHOTS_DIR: ${{ inputs.screenshots-dir }}
40
+ SCREENSHOT_COUNT: ${{ inputs.screenshot-count }}
41
+ APP_STARTED: ${{ inputs.app-started }}
42
+ ENABLED: ${{ inputs.enabled }}
43
+ run: |
44
+ set -euo pipefail
45
+
46
+ if [ "$ENABLED" != "true" ]; then
47
+ echo "Visual verification disabled; nothing to attach."
48
+ exit 0
49
+ fi
50
+
51
+ if [ -f "$SCREENSHOTS_DIR/plan.json" ] && [ "$APP_STARTED" = "true" ] && [ "$SCREENSHOT_COUNT" -gt 0 ]; then
52
+ # Build the comment body from the plan's descriptions.
53
+ body=$(jq -r --arg pr "$PR" '
54
+ "<!-- agent-visual-verify -->\n## Visual verification for PR #" + $pr + "\n",
55
+ (.waypoints | to_entries[] |
56
+ "**\(.key + 1). " + (.value.description // .value.expect // "waypoint") + "**\n" +
57
+ "Selector: `" + (.value.selector // "n/a") + "`\n"
58
+ )
59
+ ' "$SCREENSHOTS_DIR/plan.json")
60
+
61
+ # Collect all screenshot files.
62
+ attaches=()
63
+ for png in "$SCREENSHOTS_DIR"/*.png; do
64
+ [ -f "$png" ] || continue
65
+ attaches+=("$png")
66
+ done
67
+
68
+ if [ ${#attaches[@]} -gt 0 ]; then
69
+ gh issue comment "$ISSUE" --repo "$REPO" \
70
+ --body "attach" \
71
+ "${attaches[@]/#/--attach=}" \
72
+ --body-file - <<< "$body" 2>/dev/null || \
73
+ {
74
+ # Fallback: gh CLI does not support --body-file with --attach in all versions.
75
+ # Post the body as the comment and attach each screenshot individually.
76
+ gh issue comment "$ISSUE" --repo "$REPO" --body "$body"
77
+ for png in "${attaches[@]}"; do
78
+ gh issue comment "$ISSUE" --repo "$REPO" --attach "$png" --body ""
79
+ done
80
+ }
81
+ echo "Attached ${#attaches[@]} screenshots to issue #$ISSUE"
82
+ else
83
+ gh issue comment "$ISSUE" --repo "$REPO" --body "$body"
84
+ echo "No screenshots found; posted plan summary only"
85
+ fi
86
+ else
87
+ # App didn't start or no plan was produced. Post a warning.
88
+ reason="visual verification could not produce screenshots"
89
+ if [ "$APP_STARTED" != "true" ]; then
90
+ reason="$reason (app did not start)"
91
+ elif [ ! -f "$SCREENSHOTS_DIR/plan.json" ]; then
92
+ reason="$reason (no verification plan was written)"
93
+ fi
94
+
95
+ warning_body="<!-- agent-visual-verify -->## Visual verification for PR #$PR
96
+
97
+ ⚠ $reason. The merge proceeds without visual evidence."
98
+ gh issue comment "$ISSUE" --repo "$REPO" --body "$warning_body"
99
+ echo "Posted warning: $reason"
100
+ fi
@@ -1652,6 +1652,44 @@ if worker_installed merge-gate; then
1652
1652
  if [ "$GATE_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
1653
1653
  fi
1654
1654
 
1655
+ echo "── Visual verify wiring ───────────────────────────────────────────────────"
1656
+
1657
+ # The merge-gate's conclude job dispatches agent-visual-verify before merging when the outcome
1658
+ # is auto-merge and VISUAL_VERIFY_ENABLED is true. Each of these is one line an edit could drop
1659
+ # with nothing going red: a missing dispatch step, a missing env var, or a missing worker file.
1660
+ if worker_installed merge-gate; then
1661
+ VV_OK=1
1662
+
1663
+ vv() {
1664
+ grep -qE "$1" "$2" || { VV_OK=0; echo "FAIL: $3" >&2; }
1665
+ }
1666
+
1667
+ # The dispatch step exists and runs only on auto-merge with the feature flag on.
1668
+ vv 'name: Run visual verification' "$MERGE_GATE_WORKER_MD" 'merge-gate has no visual-verify dispatch step'
1669
+ vv "outcome == 'auto-merge'.*VISUAL_VERIFY_ENABLED == 'true'" "$MERGE_GATE_WORKER_MD" 'visual-verify dispatch has wrong guard'
1670
+ vv 'continue-on-error: true' "$MERGE_GATE_WORKER_MD" 'visual-verify dispatch must not block the merge'
1671
+
1672
+ # The env vars the worker needs.
1673
+ vv 'VISUAL_VERIFY_ENABLED:' "$MERGE_GATE_WORKER_MD" 'merge-gate missing VISUAL_VERIFY_ENABLED env var'
1674
+ vv 'VISUAL_VERIFY_START_COMMAND:' "$MERGE_GATE_WORKER_MD" 'merge-gate missing VISUAL_VERIFY_START_COMMAND env var'
1675
+
1676
+ # The worker file exists.
1677
+ VV_WORKER_MD="${WORKFLOWS_DIR}/agent-visual-verify.md"
1678
+ if [ ! -f "$VV_WORKER_MD" ]; then
1679
+ VV_OK=0
1680
+ echo "FAIL: agent-visual-verify.md does not exist" >&2
1681
+ fi
1682
+
1683
+ # The attach-screenshots action exists.
1684
+ VV_ATTACH="${HERE}/../attach-screenshots/action.yml"
1685
+ if [ ! -f "$VV_ATTACH" ]; then
1686
+ VV_OK=0
1687
+ echo "FAIL: attach-screenshots/action.yml does not exist" >&2
1688
+ fi
1689
+
1690
+ if [ "$VV_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
1691
+ fi
1692
+
1655
1693
  echo "── Expression functions ──────────────────────────────────────────────────"
1656
1694
 
1657
1695
  # GitHub's expression language has eleven functions and no more. There is no `split()`, no
@@ -72,6 +72,14 @@ env:
72
72
  # one unusable report can keep every other bot pull request in the repository waiting.
73
73
  PARK_AT_UNUSABLE_OUTPUT: "2"
74
74
  ISSUE_CONTEXT_PATH: /tmp/gh-aw/agent/issue-context.json
75
+ # Visual verification runs after auto-merge is decided and before the merge itself. The worker
76
+ # runs /repo-verify, drives a browser via agent-browser, and attaches screenshots to the linked
77
+ # issue. Failures never block the merge. Consumers opt out by setting this to "false".
78
+ VISUAL_VERIFY_ENABLED: "true"
79
+ VISUAL_VERIFY_BUILD_COMMAND: ""
80
+ VISUAL_VERIFY_START_COMMAND: ""
81
+ VISUAL_VERIFY_PORT: "3000"
82
+ VISUAL_VERIFY_WAIT_SECONDS: "30"
75
83
  GH_AW_ALLOWED_BOTS: "platform-devbox[bot],github-actions[bot]"
76
84
  GIT_AUTHOR_NAME: "github-actions[bot]"
77
85
  GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com"
@@ -449,6 +457,13 @@ jobs:
449
457
  ```
450
458
 
451
459
  Findings and verification on the linked issue: #${{ needs.subject.outputs.issue }}. [View this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
460
+ - name: Run visual verification
461
+ if: needs.validate_output.outputs.outcome == 'auto-merge' && env.VISUAL_VERIFY_ENABLED == 'true'
462
+ uses: ./.github/workflows/agent-visual-verify.lock.yml
463
+ with:
464
+ pr-number: ${{ needs.subject.outputs.pr }}
465
+ linked-issue: ${{ needs.subject.outputs.issue }}
466
+ continue-on-error: true
452
467
  - name: Merge approved pull request
453
468
  if: needs.validate_output.outputs.outcome == 'auto-merge'
454
469
  env:
@@ -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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plainconceptsplatform/workflows",
3
- "version": "0.23.0",
3
+ "version": "0.24.1",
4
4
  "description": "Install and update Platform GitHub agentic workflows.",
5
5
  "keywords": [
6
6
  "github-actions",