Attach an executable UI verification spec to a pull request, then run it against the live preview deployment.
A reviewer verifies a UI change by clicking through the preview.
preview-spec turns that click-through into an artifact an agent can follow: a short spec in the PR description, run against the deployed preview by aw-tester, reporting pass or fail.
This
SKILL.mdis a thin index. Detailed procedures live inrules/*.mdandtemplates/*.md. Each operation loads only what it needs.
What this skill reuses
This skill owns three things and reuses the rest.
| Concern | Owner |
|---|---|
The spec grammar (WHEN/THEN/AND, the locator mini-grammar, url:, network:) |
aw-tester — specs.md.template. This skill references it and never forks it. |
| The spec-run contract (locator ladder, auth semantics, verdict schema) | spec-run-contract.md — the engine-agnostic contract both runners implement. |
| The runners + the compact verdict | Two, one contract: aw-tester (Playwright sub-agent) and aw-tester-chrome (in-session Chrome). run --driver picks one. |
The browser context (base_url, auth, fixtures) |
aw-target.yml — aw-target.yml.template. |
| The two-way lessons loop | aw-tester-lessons (locator friction, existing) + preview-spec-lessons (navigation / spec-quality friction, new). See rules/memory.md. |
| Embedding the spec in the PR body (marker + collapsed block, ceiling exemption) | this skill — rules/spec-format.md. |
| Resolving the PR's preview URL (GitHub deployments API) | this skill — rules/preview-url-resolution.md. |
| The author + run orchestration | this skill — this file + rules/runner.md. |
Operations
Parse $ARGUMENTS. The first token selects the operation.
| Operation | Trigger | What it does |
|---|---|---|
author |
first token author, or delegated from create-pr |
Seed the spec from an existing source (the aw planner's specs.md, a /fix-bug repro) or generate it from the diff, then inject the marked collapsed block into the PR body. Reads memory first. |
run |
first token run |
Extract the block from the PR (or read a local specs.md path), resolve the preview URL, run the spec via the selected driver, report the verdict, write lessons. |
If no operation token is present, default to author when a diff or branch context is in scope, and run when only a PR reference is given.
Drivers
run executes the spec through one of two runners — same grammar, same verdict, different engine. Pick with --driver:
--driver |
Runner | When |
|---|---|---|
auto (default) |
Chrome if the extension is connected; otherwise asks before using Playwright | Everyday use — fast locally, correct everywhere. |
chrome |
aw-tester-chrome, in-session |
Force the fast see→act loop against your logged-in Chrome. |
playwright |
aw-tester sub-agent |
CI, remote envs, or no browser extension. |
author never touches a browser and takes no --driver.
Step 0: Resolve your GitHub access path
Both operations touch GitHub.
Resolve which path you have — gh CLI, mcp__github__* tools, or neither — per agents/shared/rules/github-access.md.
Resolve once, state the path, and use it for the whole run.
The commands below are the gh-path form.
On the mcp path, use these equivalents.
Naming them here is load-bearing: the gh-path form above is not a mapping, and a reader who has to invent one writes nothing to the PR.
gh-path command |
mcp-path equivalent |
|---|---|
gh pr view <pr> --json body |
mcp__github__pull_request_read with method: "get" |
gh pr edit <pr> --body <body> |
mcp__github__update_pull_request with body |
gh api repos/<owner>/<repo>/deployments?sha=… |
none — see below |
author works on both paths; run's URL resolution does not.
rules/preview-url-resolution.md reads the GitHub deployments API, and no mcp__github__* tool exposes deployments.
So on the mcp path, run must take an explicit --url <preview-url> argument.
Without one, report inconclusive: no access path for deployment lookup (pass --url) and stop — never report inconclusive: preview not deployed, which claims a fact about the deployment that was never checked.
This paragraph is a summary; the branch is enforced in rules/preview-url-resolution.md § The access-path precondition, which owns the resolution decision and which rules/runner.md § Step 2 treats as terminal.
It has to live there because its condition is run invoked without --url — an argument this step cannot see.
Operation author
Inject one collapsed, marked UI verification spec into the PR body.
- Read memory first. Load spec-authoring lessons and locator lessons per
rules/memory.md § Read at author time. These tell you the app's navigation quirks and stable locators before you write a single step. - Reuse an existing spec source when present. Before writing anything, check for a spec artifact the surrounding flow already produced, in priority order (full contract:
rules/spec-sources.md):.agent/{branch}/specs.md— the autonomous-workflow planner'saw-testerspecs, already run locally at Phase 4. Same grammar: lift its## Spec N:blocks verbatim.- A
/fix-bugreproduction artifact for a UI or visual bug — ane2e-testingflow or arepro/<id>.mdchecklist. Adapt its steps into the grammar. Both sources are gitignored, local-only files. This works becauseauthorruns in the same worktree that wrote them, and it copies their content into the committed PR body — the durable artifactrunlater reads. The gitignored file is never committed; only its lifted content reaches GitHub. Seerules/spec-sources.md § Two artifacts, two lifetimes. When a source is found, seed the block from it and skip step 3, so the PR block matches what was verified locally rather than a second, divergent description of the same behavior.
- Otherwise, write the spec from the diff. Read the diff (
git diff <base>...HEAD --name-statusplus the relevant files), then write one## Spec N:block per user-visible behavior the diff changes, inaw-tester's grammar. Prefer role-and-name locators; use{testid: …}only as an escape hatch. Keep it to the behaviors a reviewer would actually click through — 1 to 3 specs, not an exhaustive suite. - Wrap and inject the spec in the marked collapsed block per
rules/spec-format.md, and write it into the PR body with the body-write call for your resolved access path, preserving everything already there. Writing the block into the PR body is this operation's only deliverable, so a run that could not perform that write has not authored a spec. Report it asfailed (no GitHub access path)rather than reporting the specs you drafted — a drafted spec that never reached the PR is indistinguishable from none to every later reader, includingrun.
The block is exempt from the create-pr description length ceiling and is preserved verbatim by review-loop's body refresh — both rules live in rules/spec-format.md and in the description contract.
Report: how many specs were authored, and the one-line goal of each.
Operation run
Run the embedded spec against the live preview.
Full procedure: rules/runner.md. In outline:
- Get the spec. Extract it from the PR body between the
<!-- preview-spec:v1 -->markers — the committed PR body is the only source that works on any checkout and in any later session. As a shortcut for a local author→run loop,run <specs-path>reads a localspecs.mddirectly (no PR, no extraction). Absent → reportno specand stop. - Resolve the preview URL per
rules/preview-url-resolution.md. A--url <preview-url>argument overrides resolution (required with a localspecs-path, and required on themcppath). Anyinconclusive: …outcome from that file is terminal — report it and stop, without a pass or a fail. Its two commonest areinconclusive: no access path for deployment lookup (pass --url)(no lookup was possible) andinconclusive: preview not deployed(the lookup ran and found nothing). - Materialize an ephemeral
specs.mdand anaw-target.ymloverlay (base_url= resolved URL) under.agent/{branch}/.preview-spec/, reading auth and fixtures from a committed.claude/aw-targets/preview.ymlwhen one exists. - Select the driver and run per
--driver(see Drivers andrules/runner.md § Step 4).autoinvokesaw-tester-chromein-session when the Chrome extension is connected; when Chrome is unavailable or a Chrome run returnsfallback: playwright, it asks the user before running theaw-testersub-agent rather than falling back silently. A forced--driver chrome/playwrightnever prompts. Mode--all. - Report the verdict (pass / fail / inconclusive, per spec) — identical shape from either driver.
- Write lessons per
rules/memory.md § Write at run timewhen a spec failed for a navigation or precondition reason — not for a locator miss, which is the runner's own lesson to write.
Hard rules
- Never fork the spec grammar. It is
aw-tester's single source of truth. If a step cannot be expressed in it, say so — do not invent syntax. - Never weaken a spec to make it pass. A red verdict is a finding, not a failure of this skill.
- Never store a secret in the spec, the target file, or a lesson. Preview-auth credentials live in the committed
preview.yml's refresh command or in the environment, never in the PR body — the spec is public. - The runner reports; it does not fix. Applying a fix for a failing spec is the author's job (a better spec) or the PR author's (a code change).