Preview Spec
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.md is a thin index.
Detailed procedures live in rules/*.md and templates/*.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's aw-tester specs, already run locally at Phase 4. Same grammar: lift its ## Spec N: blocks verbatim.
- A
/fix-bug reproduction artifact for a UI or visual bug — an e2e-testing flow or a repro/<id>.md checklist. Adapt its steps into the grammar.
Both sources are gitignored, local-only files. This works because author runs in the same worktree that wrote them, and it copies their content into the committed PR body — the durable artifact run later reads. The gitignored file is never committed; only its lifted content reaches GitHub. See rules/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-status plus the relevant files), then write one ## Spec N: block per user-visible behavior the diff changes, in aw-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 as failed (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, including run.
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 local specs.md directly (no PR, no extraction). Absent → report no spec and stop.
- Resolve the preview URL per
rules/preview-url-resolution.md. A --url <preview-url> argument overrides resolution (required with a local specs-path, and required on the mcp path). Any inconclusive: … outcome from that file is terminal — report it and stop, without a pass or a fail. Its two commonest are inconclusive: no access path for deployment lookup (pass --url) (no lookup was possible) and inconclusive: preview not deployed (the lookup ran and found nothing).
- Materialize an ephemeral
specs.md and an aw-target.yml overlay (base_url = resolved URL) under .agent/{branch}/.preview-spec/, reading auth and fixtures from a committed .claude/aw-targets/preview.yml when one exists.
- Select the driver and run per
--driver (see Drivers and rules/runner.md § Step 4). auto invokes aw-tester-chrome in-session when the Chrome extension is connected; when Chrome is unavailable or a Chrome run returns fallback: playwright, it asks the user before running the aw-tester sub-agent rather than falling back silently. A forced --driver chrome/playwright never 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 time when 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).
1---2name: preview-spec3description: Makes a UI pull request autonomously verifiable. `author` generates a step-by-step UI verification spec for the PR's visual change and injects it into the PR description as a collapsed, machine-findable block (delegated to by `create-pr` on UI diffs; also runnable standalone). `run` extracts that block, resolves the PR's live preview deployment URL via the GitHub deployments API, and runs the spec against it with Playwright by dispatching the `aw-tester` agent, then reports a pass/fail verdict. A two-way LoreKit memory loop connects them: the runner records navigation quirks it hits, and the author reads those lessons so future specs start correct from the outset. Web only — the spec grammar and Playwright runner are `aw-tester`'s; this skill owns the PR-embedding, URL resolution, and the authoring loop. Triggers on "write a preview spec", "add a UI verification spec", "verify this PR's preview", "run the preview spec", "test the preview deployment", "/preview-spec".4license: MIT5---67# Preview Spec89Attach an executable UI verification spec to a pull request, then run it against the live preview deployment.1011A reviewer verifies a UI change by clicking through the preview.12`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.1314> **This `SKILL.md` is a thin index.**15> Detailed procedures live in [`rules/*.md`](./rules) and [`templates/*.md`](./templates).16> Each operation loads only what it needs.1718## What this skill reuses1920This skill owns three things and reuses the rest.2122| Concern | Owner |23| --- | --- |24| The spec grammar (`WHEN/THEN/AND`, the locator mini-grammar, `url:`, `network:`) | `aw-tester` — [`specs.md.template`](../../workflow/autonomous-workflow/templates/specs.md.template). This skill references it and never forks it. |25| The spec-run contract (locator ladder, auth semantics, verdict schema) | [`spec-run-contract.md`](../../workflow/autonomous-workflow/rules/spec-run-contract.md) — the engine-agnostic contract both runners implement. |26| The runners + the compact verdict | Two, one contract: [`aw-tester`](../../workflow/autonomous-workflow/templates/aw-tester.agent.md) (Playwright sub-agent) and [`aw-tester-chrome`](../../workflow/autonomous-workflow/aw-tester-chrome/SKILL.md) (in-session Chrome). `run --driver` picks one. |27| The browser context (`base_url`, auth, fixtures) | `aw-target.yml` — [`aw-target.yml.template`](../../workflow/autonomous-workflow/templates/aw-target.yml.template). |28| The two-way lessons loop | `aw-tester-lessons` (locator friction, existing) + `preview-spec-lessons` (navigation / spec-quality friction, new). See [`rules/memory.md`](./rules/memory.md). |29| **Embedding the spec in the PR body** (marker + collapsed block, ceiling exemption) | this skill — [`rules/spec-format.md`](./rules/spec-format.md). |30| **Resolving the PR's preview URL** (GitHub deployments API) | this skill — [`rules/preview-url-resolution.md`](./rules/preview-url-resolution.md). |31| **The author + run orchestration** | this skill — this file + [`rules/runner.md`](./rules/runner.md). |3233## Operations3435Parse `$ARGUMENTS`. The first token selects the operation.3637| Operation | Trigger | What it does |38| --- | --- | --- |39| `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. |40| `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. |4142If 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.4344### Drivers4546`run` executes the spec through one of two runners — same grammar, same verdict, different engine. Pick with `--driver`:4748| `--driver` | Runner | When |49| --- | --- | --- |50| `auto` (default) | Chrome if the extension is connected; otherwise asks before using Playwright | Everyday use — fast locally, correct everywhere. |51| `chrome` | [`aw-tester-chrome`](../../workflow/autonomous-workflow/aw-tester-chrome/SKILL.md), in-session | Force the fast see→act loop against your logged-in Chrome. |52| `playwright` | [`aw-tester`](../../workflow/autonomous-workflow/templates/aw-tester.agent.md) sub-agent | CI, remote envs, or no browser extension. |5354`author` never touches a browser and takes no `--driver`.5556## Step 0: Resolve your GitHub access path5758Both operations touch GitHub.59Resolve which path you have — `gh` CLI, `mcp__github__*` tools, or neither — per **[`agents/shared/rules/github-access.md`](../../../agents/shared/rules/github-access.md)**.60Resolve once, state the path, and use it for the whole run.61The commands below are the `gh`-path form.6263**On the `mcp` path, use these equivalents.**64Naming 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.6566| `gh`-path command | `mcp`-path equivalent |67| --- | --- |68| `gh pr view <pr> --json body` | `mcp__github__pull_request_read` with `method: "get"` |69| `gh pr edit <pr> --body <body>` | `mcp__github__update_pull_request` with `body` |70| `gh api repos/<owner>/<repo>/deployments?sha=…` | **none — see below** |7172**`author` works on both paths; `run`'s URL resolution does not.**73[`rules/preview-url-resolution.md`](./rules/preview-url-resolution.md) reads the GitHub deployments API, and no `mcp__github__*` tool exposes deployments.74So on the `mcp` path, `run` must take an explicit `--url <preview-url>` argument.75Without 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.7677This paragraph is a summary; the branch is **enforced** in [`rules/preview-url-resolution.md § The access-path precondition`](./rules/preview-url-resolution.md#the-access-path-precondition-check-this-before-step-1), which owns the resolution decision and which [`rules/runner.md § Step 2`](./rules/runner.md) treats as terminal.78It has to live there because its condition is *`run` invoked without `--url`* — an argument this step cannot see.7980## Operation `author`8182Inject one collapsed, marked UI verification spec into the PR body.83841. **Read memory first.** Load spec-authoring lessons and locator lessons per [`rules/memory.md § Read at author time`](./rules/memory.md). These tell you the app's navigation quirks and stable locators before you write a single step.852. **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`](./rules/spec-sources.md)):86 - `.agent/{branch}/specs.md` — the autonomous-workflow planner's `aw-tester` specs, already run locally at Phase 4. Same grammar: lift its `## Spec N:` blocks verbatim.87 - A `/fix-bug` reproduction artifact for a UI or visual bug — an `e2e-testing` flow or a `repro/<id>.md` checklist. Adapt its steps into the grammar.88 Both sources are gitignored, local-only files. This works because `author` runs in the same worktree that wrote them, and it copies their content into the **committed** PR body — the durable artifact `run` later reads. The gitignored file is never committed; only its lifted content reaches GitHub. See [`rules/spec-sources.md § Two artifacts, two lifetimes`](./rules/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.893. **Otherwise, write the spec from the diff.** Read the diff (`git diff <base>...HEAD --name-status` plus the relevant files), then write one `## Spec N:` block per user-visible behavior the diff changes, in `aw-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.904. **Wrap and inject** the spec in the marked collapsed block per [`rules/spec-format.md`](./rules/spec-format.md), and write it into the PR body with the body-write call for your resolved [access path](#step-0-resolve-your-github-access-path), preserving everything already there.91Writing 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.92Report it as `failed (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, including `run`.9394The 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`](./rules/spec-format.md) and in the [description contract](../../delivery/create-pr/rules/description-contract.md).9596Report: how many specs were authored, and the one-line goal of each.9798## Operation `run`99100Run the embedded spec against the live preview.101102Full procedure: **[`rules/runner.md`](./rules/runner.md)**. In outline:1031041. **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 local `specs.md` directly (no PR, no extraction). Absent → report `no spec` and stop.1052. **Resolve the preview URL** per [`rules/preview-url-resolution.md`](./rules/preview-url-resolution.md). A `--url <preview-url>` argument overrides resolution (required with a local `specs-path`, and required on the `mcp` path). Any `inconclusive: …` outcome from that file is terminal — report it and stop, without a pass or a fail. Its two commonest are `inconclusive: no access path for deployment lookup (pass --url)` (no lookup was possible) and `inconclusive: preview not deployed` (the lookup ran and found nothing).1063. **Materialize** an ephemeral `specs.md` and an `aw-target.yml` overlay (`base_url` = resolved URL) under `.agent/{branch}/.preview-spec/`, reading auth and fixtures from a committed `.claude/aw-targets/preview.yml` when one exists.1074. **Select the driver and run** per `--driver` (see [Drivers](#drivers) and [`rules/runner.md § Step 4`](./rules/runner.md)). `auto` invokes `aw-tester-chrome` in-session when the Chrome extension is connected; when Chrome is unavailable or a Chrome run returns `fallback: playwright`, it asks the user before running the `aw-tester` sub-agent rather than falling back silently. A forced `--driver chrome`/`playwright` never prompts. Mode `--all`.1085. **Report** the verdict (pass / fail / inconclusive, per spec) — identical shape from either driver.1096. **Write lessons** per [`rules/memory.md § Write at run time`](./rules/memory.md) when a spec failed for a navigation or precondition reason — not for a locator miss, which is the runner's own lesson to write.110111## Hard rules112113- **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.114- **Never weaken a spec to make it pass.** A red verdict is a finding, not a failure of this skill.115- **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.116- **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).