Quick Route
| Situation |
Go to |
| Stand up a goal-driven run from scratch |
Discovery + references/setup.md |
| Decide agentic vs scripted for a given flow |
Fit: Intent-Driven vs Scripted |
| Agent passes one run, fails the next |
Determinism |
| "How does it click without screenshots?" |
Interaction Model |
| Runs are slow / burning tokens |
Cost and Latency |
| Agent reports false success |
Success Assertion (the Oracle) |
| Flow is stable — make it permanent |
Graduation → references/graduation-and-ci.md |
| Block a merge on the goal |
CI Gating → references/graduation-and-ci.md |
| Canvas / no accessibility tree |
Canvas Fallback → references/graduation-and-ci.md |
Discovery Questions
First, check .agents/qa-project-context.md in the project root and skip anything it already
answers (stack, environments, seed/reset tooling, model access).
- Which flow, and how often does its UI change? Fast-changing/experimental UI favors
intent-driven; a stable critical path (login) favors scripted. This decides the whole approach.
- Is there a seeded fixture and a way to reset state? Determinism is impossible without
seeded data and a per-run reset. If neither exists, that is step zero.
- Can you deep-link past auth to a seeded entry point? Re-driving login every run is the
biggest avoidable cost; a seeded entry URL scopes the goal and cuts steps.
- What is the unambiguous success oracle? Specific account text, a
/dashboard URL, an
order number — plus a forbidden state. "No error" is not an oracle.
- Does the target render to canvas / WebGL? No accessibility tree means snapshot-first
won't work; plan the vision fallback or instrument the canvas with ARIA.
- Which model and budget? Pin a model id and a step budget up front; tier cheap steps to
Haiku 4.5 / Sonnet 4.6 and reserve Opus 4.8 for genuinely ambiguous flows.
Core Principles
Intent, not instructions — but only where churn earns it. The agent reads a goal and
finds its own path through the accessibility tree, so it survives a moved button or renamed
class that would break a selector. That resilience costs 2-5x the time and money of a
scripted run, so spend it on fast-changing UI and hard-to-locate flows, not on stable
critical paths.
An agent run is untrustworthy until it is deterministic. Same goal, same seeded app
must produce the same verdict. That requires temperature 0, a pinned model id, seeded data
with a reset, a bounded step budget, and an explicit pass/fail assertion. Without these you
have a coin flip, not a test.
The oracle lives outside the agent. Never let the LLM self-grade "looks good." Success
is a checkable assertion against the final browser_snapshot — specific expected text, a
URL, AND a forbidden-state negative check — evaluated by your harness, not the model.
Accessibility tree first, pixels last. browser_snapshot returns roles, refs, and
accessible names (~200-400 tokens) and is deterministic and cheap. Screenshots, pixel
coordinates, vision, and OCR are a scoped last resort for canvas only, never the default.
Graduation is the goal, not perpetual agent runs. Once a flow is stable, promote the
run to a durable scripted tests/*.spec.ts with role-based locators. An agent that has
been green for two weeks should become a fast, free regression test — keep the agent for
exploration, not for guarding a settled path.
Fit: Intent-Driven vs Scripted
The decision is per-flow, not per-project. Run risk-based-testing first if you need the
risk map; this table is the routing rule once you have it.
| Flow characteristic |
Use |
Why |
| Stable, high-frequency critical path (login, payment) |
Scripted + pinned (playwright-automation) |
Runs every PR; must be fast, free, and deterministic. No upside to re-exploring it. |
| Fast-changing / experimental UI (a dashboard that churns weekly, a redesign in flight) |
Agentic / intent-driven |
Selectors would break constantly; a goal survives layout churn. |
| Hard-to-locate flow you can't reliably select |
Agentic |
The agent finds the control by role/name instead of you reverse-engineering a selector. |
| Exploratory smoke / "does the happy path still work at all" |
Agentic |
One NL goal covers a lot of ground without a maintained script. |
| Anything in CI that must never falsely pass |
Scripted, OR agentic with a hard oracle |
Non-determinism is a false-pass risk you must actively cap. |
The rule, stated plainly: keep stable critical paths scripted and pinned; point
intent-driven agents at fast-changing UI and exploratory smoke. Do not move everything to the
agent — it is slower, costlier, and non-deterministic, and not every test should be agentic.
The Interaction Model (accessibility-tree-first)
Playwright MCP is not computer-use with screenshots and pixel coordinates. It is
accessibility-tree-first:
browser_navigate to the seeded entry URL.
browser_snapshot returns the accessibility tree — each interactive element as a
role, a stable ref, and its accessible name (from ARIA/labels). ~200-400 tokens.
- The agent picks an element by
ref and calls browser_click or browser_type.
browser_wait_for waits on text appearing/disappearing — never a fixed sleep.
- Re-
browser_snapshot after the DOM changes; assert against that tree.
Why not screenshots: the snapshot is token-efficient (thousands of tokens cheaper than an
image), deterministic (text refs, not fuzzy pixel matching), and needs no vision model or
OCR. Feeding screenshots as the primary input makes the run slower, pricier, and flakier.
browser_take_screenshot is for human evidence only, never as the assertion input.
See references/setup.md for the MCP registration, the full tool table, and the goal prompt.
Determinism: making a run trustworthy in CI
A run that passes once and fails the next with no app change is not yet a test. The fix is
never "just retry" or bumping temperature for "smarter" exploration — that adds variance. Pin
the variables instead:
| Lever |
Setting |
| Model |
Pinned model id (e.g. claude-haiku-4-5-20251001), never latest |
| Sampling |
temperature 0 — no creative wandering in CI |
| Data |
Seeded fixture + reset/seed the database before every run |
| Scope |
Bounded step budget (maxSteps), e.g. 18 — exceeding it FAILS, never auto-retries |
| Oracle |
Explicit pass/fail verdict asserted against the snapshot |
| Evidence |
Assert on the accessibility tree, never a screenshot diff |
Avoid: temperature: 0.7 or 1 for exploration, retry-until-pass loops,
waitForTimeout sleeps, and screenshot-based assertions. Each one hides flakiness rather than
removing it. Full harness config in references/setup.md.
Success Assertion: the Oracle (where agents fail silently)
This is the sharpest failure mode: the agent reports success while stuck on the login page,
because "page loaded / no error / looks good" was accepted as success and the LLM was allowed
to self-grade. Force an explicit oracle the harness checks — never the agent.
For the goal "sign in as an existing user and confirm the dashboard shows the right account name":
SUCCESS (all must hold — assert against the final browser_snapshot):
- URL matches /dashboard
- Snapshot contains the specific expected account name text, e.g. "Acme Corp — Jane R."
NEGATIVE / forbidden state (fail fast if any is true):
- Still on a URL matching /login → FAIL
- Snapshot contains role="alert" with "invalid credentials" → FAIL
VERDICT: harness emits {"passed": true|false}; the LLM does not decide.
The positive checks (specific account name + /dashboard URL) prove where it landed; the
negative check (must NOT be on the login page) is what kills the false pass. "No error,"
"didn't crash," "screenshot looks correct," and "trust the agent" are not success criteria.
Cost and Latency
Agent runs are 2-5x slower and pricier than scripted tests — a step is an LLM round-trip, the
dominant cost. Cut spend without losing coverage by going smaller, not bigger:
- Step budget — keep
maxSteps low and enforced; fewer round-trips, less drift.
- Model tiering — Haiku 4.5 / Sonnet 4.6 for cheap navigation steps; reserve Opus 4.8 for
genuinely ambiguous exploration. Don't run the biggest model on every step.
- Prompt caching — cache the static system prompt, tool schemas, and goal; they repeat
every run.
- Scope via a seeded entry point — one narrow goal per run, deep-linked past login instead
of re-driving it each time.
- Snapshot over screenshots — the a11y snapshot is ~200-400 tokens; a full-page screenshot
is thousands. Default to snapshot.
Backwards moves to reject: "use a bigger model / Opus 4.8 for every step," "raise the step
limit," "screenshot every step," and running with no budget at all. See references/setup.md.
Graduation and CI Gating
Promote a stabilized goal into a durable scripted test, and gate merges on the verdict. Both
are detailed in references/graduation-and-ci.md; the essentials:
- Graduate with Playwright Test Agents (planner / generator / healer, shipped in
Playwright v1.56.0).
npx playwright init-agents --loop=claude. The planner writes a
Markdown test plan to specs/<flow>.md; the generator turns it into tests/<flow>.spec.ts
with role-based locators (getByRole, getByLabel, getByText) verified against the live
DOM; the healer repairs broken locators. This is the promotion path — not "keep running it
as an agent," not recorded clicks, not page.locator('xpath=...'), not data-testid-only.
- Gate CI so a failed goal exits non-zero and emits a machine-readable verdict
(
{"passed": true|false} in result.json); the GitHub Actions job parses the boolean and
exit 1s on false. State is seeded/ephemeral and reset per run, with a step budget and a
timeout cap. Never continue-on-error: true, never "always exit 0," never a prose verdict a
human reads.
- Canvas with no accessibility tree: prefer instrumenting the canvas with ARIA; as a scoped
last resort enable
--caps=vision to unlock browser_mouse_click_xy for that flow only.
browser_snapshot will not work on a raw canvas, but don't make coordinates the default and
don't abandon agentic testing.
Migrating a brittle script to a goal (honest tradeoffs)
Converting an 80-line script that re-types login and walks 6 hardcoded steps into a single NL
goal with an explicit success assertion is a real win for a churning flow — but state
the downsides honestly:
- Non-determinism / false-pass risk — the run could pass falsely; that's why the hard
oracle and the negative check are non-negotiable.
- Cost/latency — 2-5x slower; bound it with a step budget and a seeded entry point.
- Not every test should be agentic — keep stable paths scripted, and plan to graduate
this one back to a scripted test once it stabilizes.
Reject the over-promise: it is not "strictly better with no downsides," do not "migrate
everything," and never drop the assertions to make it pass.
Anti-Patterns
1. Reflexively writing a scripted Playwright test
"Browser test" pattern-matches to codegen, so the default is page.goto / page.locator /
await expect(page...) / hunting data-testid. That misses the entire point. A goal-driven
agent reads NL intent and explores via browser_snapshot — no pre-written selectors.
2. "Use the agent for everything"
Over-selling the new toy. The agent is 2-5x slower and non-deterministic. Stable critical paths
(login) stay scripted and pinned; intent-driven wins on fast-changing UI. Never "always use the
agent" or "agents replace all scripted tests."
3. Fixing flakiness with retries or higher temperature
"Just retry" and bumping temperature for "smarter" exploration both add variance. The real
levers are temperature 0, a pinned model, seeded data, a bounded step budget, and an explicit
verdict.
4. Assuming computer-use = screenshots + pixel coordinates
Playwright MCP is accessibility-tree-first. Defaulting to vision, screenshots, OCR, or
mouse_click_xy is slower, costlier, and flakier. Pixels are a canvas-only last resort.
5. "No error = success" (the false pass)
Accepting "page loaded / didn't crash / looks good" and letting the LLM self-grade is exactly
why the agent reports success while stuck on login. Require specific expected text, a URL, and
a forbidden-state negative check, evaluated by the harness.
6. Bigger model / more steps to go faster
Backwards. Opus 4.8 on every step and raising maxSteps raise cost and latency without buying
reliability. Smaller models, tighter budgets, caching, and tighter scope are the fix.
7. Running an agent forever instead of graduating
A goal green for two weeks on a now-stable flow should become a scripted tests/*.spec.ts via
Playwright Test Agents. "Keep running it as an agent," recording clicks, and xpath locators are
all wrong promotions.
8. Prose verdict in CI
Returning a paragraph for a human to read, or continue-on-error: true, lets a failed goal
merge. The run must exit non-zero on failure with a machine-readable boolean.
Done When
- Goal prompt exists as natural-language intent (no
page.locator / page.goto /
data-testid in the goal) with a START seeded entry URL.
- An explicit success oracle is defined: specific expected text AND a URL check AND a
forbidden-state negative check, asserted against
browser_snapshot — not a screenshot.
- Run config pins a model id, sets
temperature: 0, a maxSteps budget, and a seed; no
waitForTimeout, no retry-until-pass.
- Interaction is snapshot-first:
browser_navigate / browser_snapshot /
browser_click / browser_type / browser_wait_for; screenshots used only for evidence.
- The runner emits
result.json with {"passed": true|false} and exit 1s on false; the CI
job gates the merge on the boolean (no continue-on-error, no always exit 0).
- CI seeds/resets ephemeral state per run and enforces a step budget and a timeout cap.
- A graduation trigger is recorded (e.g. "green for 2 weeks → run
init-agents, generate
tests/<flow>.spec.ts with getByRole locators").
- If any target is canvas/WebGL, the vision fallback (
--caps=vision +
browser_mouse_click_xy) is scoped to that flow only, or the canvas is instrumented with ARIA.
Related Skills
- playwright-automation — Writing and maintaining deterministic scripted Playwright tests
and Page Objects. Go there to author the durable test; this skill graduates an agent run into one.
- ai-system-testing — Testing your product's OWN LLM/AI features (prompt regression, model
output quality). This skill tests any app using an agent; it does not test your AI feature.
- exploratory-testing — Human SBTM exploration and bug hunting. The agentic smoke goal is
the automated cousin; use exploratory-testing for charter-driven manual sessions.
- test-reliability — Self-healing locators and quarantine for scripted flaky tests at
runtime. Complements the determinism levers here once a test has graduated.
- qa-project-context — The universal dependency; supplies stack, environments, seed/reset
tooling, and model access that every question above depends on.
Reference Files (in references/)
- setup.md — Playwright MCP registration (
.mcp.json), the snapshot tool table, the
natural-language goal prompt with success/negative assertions, the determinism harness
config (pinned model, temperature 0, maxSteps, seed, prompt cache), and cost/latency levers.
- graduation-and-ci.md — Playwright Test Agents promotion pipeline (planner →
specs/*.md,
generator → tests/*.spec.ts with role-based locators, healer), the GitHub Actions gating
workflow with a machine-readable boolean verdict, and the canvas --caps=vision fallback.
1---
2name: agentic-browser-testing
3description: >-
4 Goal-driven E2E testing where a browser agent (Playwright MCP / computer-use) reads a
5 natural-language goal and explores the app via the accessibility tree to assert outcomes —
6 no pre-written script. Covers when intent-driven beats scripted, making agent runs
7 deterministic (pinned model, temperature 0, seeded data, bounded steps, explicit success
8 assertion, snapshot-not-pixel), cost/latency control, the accessibility-tree-first
9 interaction model, CI gating, and graduating a stable run into a scripted Playwright test.
10 Use when: "agentic browser test," "goal-driven browser test," "let an agent explore the app,"
11 "natural-language E2E," "browser agent smoke test," "Playwright MCP test."
12 Not for: Writing/maintaining deterministic scripted Playwright tests — that is
13 playwright-automation. Testing your product's OWN LLM features — that is ai-system-testing.
14 Related: playwright-automation, ai-system-testing, exploratory-testing, test-reliability, qa-project-context.
15license: MIT
16metadata:
17 author: kindlmann
18 version: "1.0"
19 category: ai-qa
20---
21
22<objective>
23A scripted Playwright test breaks the moment a button moves or a class renames; writing one
24for a dashboard that changes weekly is a maintenance treadmill. This skill stands up a
25goal-driven browser agent instead: it reads a natural-language goal, explores the app via the
26accessibility tree (Playwright MCP `browser_snapshot`), and asserts the outcome against an
27explicit oracle. The failure mode it prevents is the one that makes teams distrust agents — an
28agent that reports "success" while stuck on the login page because nothing forced it to prove
29where it landed. You leave with a deterministic, CI-gated agent run and a graduation path to a
30durable scripted test once the flow stabilizes.
31</objective>
32
33## Quick Route
34
35| Situation | Go to |
36|-----------|-------|
37| Stand up a goal-driven run from scratch | Discovery + `references/setup.md` |
38| Decide agentic vs scripted for a given flow | Fit: Intent-Driven vs Scripted |
39| Agent passes one run, fails the next | Determinism |
40| "How does it click without screenshots?" | Interaction Model |
41| Runs are slow / burning tokens | Cost and Latency |
42| Agent reports false success | Success Assertion (the Oracle) |
43| Flow is stable — make it permanent | Graduation → `references/graduation-and-ci.md` |
44| Block a merge on the goal | CI Gating → `references/graduation-and-ci.md` |
45| Canvas / no accessibility tree | Canvas Fallback → `references/graduation-and-ci.md` |
46
47## Discovery Questions
48
49First, check `.agents/qa-project-context.md` in the project root and skip anything it already
50answers (stack, environments, seed/reset tooling, model access).
51
521. **Which flow, and how often does its UI change?** Fast-changing/experimental UI favors
53 intent-driven; a stable critical path (login) favors scripted. This decides the whole approach.
542. **Is there a seeded fixture and a way to reset state?** Determinism is impossible without
55 seeded data and a per-run reset. If neither exists, that is step zero.
563. **Can you deep-link past auth to a seeded entry point?** Re-driving login every run is the
57 biggest avoidable cost; a seeded entry URL scopes the goal and cuts steps.
584. **What is the unambiguous success oracle?** Specific account text, a `/dashboard` URL, an
59 order number — plus a forbidden state. "No error" is not an oracle.
605. **Does the target render to canvas / WebGL?** No accessibility tree means snapshot-first
61 won't work; plan the vision fallback or instrument the canvas with ARIA.
626. **Which model and budget?** Pin a model id and a step budget up front; tier cheap steps to
63 Haiku 4.5 / Sonnet 4.6 and reserve Opus 4.8 for genuinely ambiguous flows.
64
65---
66
67## Core Principles
68
691. **Intent, not instructions — but only where churn earns it.** The agent reads a goal and
70 finds its own path through the accessibility tree, so it survives a moved button or renamed
71 class that would break a selector. That resilience costs 2-5x the time and money of a
72 scripted run, so spend it on fast-changing UI and hard-to-locate flows, not on stable
73 critical paths.
74
752. **An agent run is untrustworthy until it is deterministic.** Same goal, same seeded app
76 must produce the same verdict. That requires temperature 0, a pinned model id, seeded data
77 with a reset, a bounded step budget, and an explicit pass/fail assertion. Without these you
78 have a coin flip, not a test.
79
803. **The oracle lives outside the agent.** Never let the LLM self-grade "looks good." Success
81 is a checkable assertion against the final `browser_snapshot` — specific expected text, a
82 URL, AND a forbidden-state negative check — evaluated by your harness, not the model.
83
844. **Accessibility tree first, pixels last.** `browser_snapshot` returns roles, refs, and
85 accessible names (~200-400 tokens) and is deterministic and cheap. Screenshots, pixel
86 coordinates, vision, and OCR are a scoped last resort for canvas only, never the default.
87
885. **Graduation is the goal, not perpetual agent runs.** Once a flow is stable, promote the
89 run to a durable scripted `tests/*.spec.ts` with role-based locators. An agent that has
90 been green for two weeks should become a fast, free regression test — keep the agent for
91 exploration, not for guarding a settled path.
92
93---
94
95## Fit: Intent-Driven vs Scripted
96
97The decision is per-flow, not per-project. Run `risk-based-testing` first if you need the
98risk map; this table is the routing rule once you have it.
99
100| Flow characteristic | Use | Why |
101|---------------------|-----|-----|
102| Stable, high-frequency critical path (login, payment) | **Scripted + pinned** (`playwright-automation`) | Runs every PR; must be fast, free, and deterministic. No upside to re-exploring it. |
103| Fast-changing / experimental UI (a dashboard that churns weekly, a redesign in flight) | **Agentic / intent-driven** | Selectors would break constantly; a goal survives layout churn. |
104| Hard-to-locate flow you can't reliably select | **Agentic** | The agent finds the control by role/name instead of you reverse-engineering a selector. |
105| Exploratory smoke / "does the happy path still work at all" | **Agentic** | One NL goal covers a lot of ground without a maintained script. |
106| Anything in CI that must never falsely pass | Scripted, OR agentic **with a hard oracle** | Non-determinism is a false-pass risk you must actively cap. |
107
108**The rule, stated plainly:** keep stable critical paths scripted and pinned; point
109intent-driven agents at fast-changing UI and exploratory smoke. Do not move everything to the
110agent — it is slower, costlier, and non-deterministic, and not every test should be agentic.
111
112---
113
114## The Interaction Model (accessibility-tree-first)
115
116Playwright MCP is **not** computer-use with screenshots and pixel coordinates. It is
117accessibility-tree-first:
118
1191. `browser_navigate` to the seeded entry URL.
1202. `browser_snapshot` returns the **accessibility tree** — each interactive element as a
121 `role`, a stable `ref`, and its `accessible name` (from ARIA/labels). ~200-400 tokens.
1223. The agent picks an element by `ref` and calls `browser_click` or `browser_type`.
1234. `browser_wait_for` waits on text appearing/disappearing — never a fixed sleep.
1245. Re-`browser_snapshot` after the DOM changes; assert against that tree.
125
126Why not screenshots: the snapshot is **token-efficient** (thousands of tokens cheaper than an
127image), **deterministic** (text refs, not fuzzy pixel matching), and needs no vision model or
128OCR. Feeding screenshots as the primary input makes the run slower, pricier, and flakier.
129`browser_take_screenshot` is for human evidence only, never as the assertion input.
130
131See `references/setup.md` for the MCP registration, the full tool table, and the goal prompt.
132
133---
134
135## Determinism: making a run trustworthy in CI
136
137A run that passes once and fails the next with no app change is not yet a test. The fix is
138never "just retry" or bumping temperature for "smarter" exploration — that adds variance. Pin
139the variables instead:
140
141| Lever | Setting |
142|-------|---------|
143| Model | **Pinned model id** (e.g. `claude-haiku-4-5-20251001`), never `latest` |
144| Sampling | **temperature 0** — no creative wandering in CI |
145| Data | **Seeded fixture + reset/seed the database** before every run |
146| Scope | **Bounded step budget** (`maxSteps`), e.g. 18 — exceeding it FAILS, never auto-retries |
147| Oracle | **Explicit pass/fail verdict** asserted against the snapshot |
148| Evidence | Assert on the **accessibility tree**, never a screenshot diff |
149
150Avoid: `temperature: 0.7` or `1` for exploration, retry-until-pass loops,
151`waitForTimeout` sleeps, and screenshot-based assertions. Each one hides flakiness rather than
152removing it. Full harness config in `references/setup.md`.
153
154---
155
156## Success Assertion: the Oracle (where agents fail silently)
157
158This is the sharpest failure mode: the agent reports success while stuck on the login page,
159because "page loaded / no error / looks good" was accepted as success and the LLM was allowed
160to self-grade. Force an explicit oracle the harness checks — never the agent.
161
162For the goal *"sign in as an existing user and confirm the dashboard shows the right account name"*:
163
164```text
165SUCCESS (all must hold — assert against the final browser_snapshot):
166 - URL matches /dashboard
167 - Snapshot contains the specific expected account name text, e.g. "Acme Corp — Jane R."
168NEGATIVE / forbidden state (fail fast if any is true):
169 - Still on a URL matching /login → FAIL
170 - Snapshot contains role="alert" with "invalid credentials" → FAIL
171VERDICT: harness emits {"passed": true|false}; the LLM does not decide.
172```
173
174The positive checks (specific account name + `/dashboard` URL) prove where it landed; the
175**negative check** (must NOT be on the login page) is what kills the false pass. "No error,"
176"didn't crash," "screenshot looks correct," and "trust the agent" are not success criteria.
177
178---
179
180## Cost and Latency
181
182Agent runs are 2-5x slower and pricier than scripted tests — a step is an LLM round-trip, the
183dominant cost. Cut spend without losing coverage by going *smaller*, not *bigger*:
184
185- **Step budget** — keep `maxSteps` low and enforced; fewer round-trips, less drift.
186- **Model tiering** — Haiku 4.5 / Sonnet 4.6 for cheap navigation steps; reserve Opus 4.8 for
187 genuinely ambiguous exploration. Don't run the biggest model on every step.
188- **Prompt caching** — cache the static system prompt, tool schemas, and goal; they repeat
189 every run.
190- **Scope via a seeded entry point** — one narrow goal per run, deep-linked past login instead
191 of re-driving it each time.
192- **Snapshot over screenshots** — the a11y snapshot is ~200-400 tokens; a full-page screenshot
193 is thousands. Default to snapshot.
194
195Backwards moves to reject: "use a bigger model / Opus 4.8 for every step," "raise the step
196limit," "screenshot every step," and running with no budget at all. See `references/setup.md`.
197
198---
199
200## Graduation and CI Gating
201
202Promote a stabilized goal into a durable scripted test, and gate merges on the verdict. Both
203are detailed in `references/graduation-and-ci.md`; the essentials:
204
205- **Graduate** with **Playwright Test Agents** (planner / generator / healer, shipped in
206 Playwright **v1.56.0**). `npx playwright init-agents --loop=claude`. The **planner** writes a
207 Markdown test plan to `specs/<flow>.md`; the **generator** turns it into `tests/<flow>.spec.ts`
208 with **role-based locators** (`getByRole`, `getByLabel`, `getByText`) verified against the live
209 DOM; the **healer** repairs broken locators. This is the promotion path — not "keep running it
210 as an agent," not recorded clicks, not `page.locator('xpath=...')`, not data-testid-only.
211- **Gate CI** so a failed goal exits **non-zero** and emits a **machine-readable** verdict
212 (`{"passed": true|false}` in `result.json`); the GitHub Actions job parses the boolean and
213 `exit 1`s on false. State is seeded/ephemeral and reset per run, with a step budget and a
214 timeout cap. Never `continue-on-error: true`, never "always exit 0," never a prose verdict a
215 human reads.
216- **Canvas with no accessibility tree:** prefer instrumenting the canvas with ARIA; as a scoped
217 last resort enable `--caps=vision` to unlock `browser_mouse_click_xy` for that flow only.
218 `browser_snapshot` will not work on a raw canvas, but don't make coordinates the default and
219 don't abandon agentic testing.
220
221---
222
223## Migrating a brittle script to a goal (honest tradeoffs)
224
225Converting an 80-line script that re-types login and walks 6 hardcoded steps into a single NL
226**goal** with an **explicit success assertion** is a real win for a churning flow — but state
227the downsides honestly:
228
229- **Non-determinism / false-pass risk** — the run could pass falsely; that's why the hard
230 oracle and the negative check are non-negotiable.
231- **Cost/latency** — 2-5x slower; bound it with a **step budget** and a seeded entry point.
232- **Not every test should be agentic** — keep stable paths scripted, and plan to **graduate**
233 this one back to a scripted test once it stabilizes.
234
235Reject the over-promise: it is **not** "strictly better with no downsides," do **not** "migrate
236everything," and never drop the assertions to make it pass.
237
238---
239
240## Anti-Patterns
241
242### 1. Reflexively writing a scripted Playwright test
243"Browser test" pattern-matches to codegen, so the default is `page.goto` / `page.locator` /
244`await expect(page...)` / hunting `data-testid`. That misses the entire point. A goal-driven
245agent reads NL intent and explores via `browser_snapshot` — no pre-written selectors.
246
247### 2. "Use the agent for everything"
248Over-selling the new toy. The agent is 2-5x slower and non-deterministic. Stable critical paths
249(login) stay scripted and pinned; intent-driven wins on fast-changing UI. Never "always use the
250agent" or "agents replace all scripted tests."
251
252### 3. Fixing flakiness with retries or higher temperature
253"Just retry" and bumping temperature for "smarter" exploration both add variance. The real
254levers are temperature 0, a pinned model, seeded data, a bounded step budget, and an explicit
255verdict.
256
257### 4. Assuming computer-use = screenshots + pixel coordinates
258Playwright MCP is accessibility-tree-first. Defaulting to vision, screenshots, OCR, or
259`mouse_click_xy` is slower, costlier, and flakier. Pixels are a canvas-only last resort.
260
261### 5. "No error = success" (the false pass)
262Accepting "page loaded / didn't crash / looks good" and letting the LLM self-grade is exactly
263why the agent reports success while stuck on login. Require specific expected text, a URL, and
264a forbidden-state negative check, evaluated by the harness.
265
266### 6. Bigger model / more steps to go faster
267Backwards. Opus 4.8 on every step and raising `maxSteps` raise cost and latency without buying
268reliability. Smaller models, tighter budgets, caching, and tighter scope are the fix.
269
270### 7. Running an agent forever instead of graduating
271A goal green for two weeks on a now-stable flow should become a scripted `tests/*.spec.ts` via
272Playwright Test Agents. "Keep running it as an agent," recording clicks, and xpath locators are
273all wrong promotions.
274
275### 8. Prose verdict in CI
276Returning a paragraph for a human to read, or `continue-on-error: true`, lets a failed goal
277merge. The run must exit non-zero on failure with a machine-readable boolean.
278
279---
280
281## Done When
282
283- Goal prompt exists as natural-language intent (no `page.locator` / `page.goto` /
284 `data-testid` in the goal) with a START seeded entry URL.
285- An explicit success oracle is defined: specific expected text AND a URL check AND a
286 forbidden-state negative check, asserted against `browser_snapshot` — not a screenshot.
287- Run config pins a model id, sets `temperature: 0`, a `maxSteps` budget, and a seed; no
288 `waitForTimeout`, no retry-until-pass.
289- Interaction is snapshot-first: `browser_navigate` / `browser_snapshot` /
290 `browser_click` / `browser_type` / `browser_wait_for`; screenshots used only for evidence.
291- The runner emits `result.json` with `{"passed": true|false}` and `exit 1`s on false; the CI
292 job gates the merge on the boolean (no `continue-on-error`, no `always exit 0`).
293- CI seeds/resets ephemeral state per run and enforces a step budget and a timeout cap.
294- A graduation trigger is recorded (e.g. "green for 2 weeks → run `init-agents`, generate
295 `tests/<flow>.spec.ts` with `getByRole` locators").
296- If any target is canvas/WebGL, the vision fallback (`--caps=vision` +
297 `browser_mouse_click_xy`) is scoped to that flow only, or the canvas is instrumented with ARIA.
298
299---
300
301## Related Skills
302
303- **playwright-automation** — Writing and maintaining deterministic scripted Playwright tests
304 and Page Objects. Go there to author the durable test; this skill graduates an agent run into one.
305- **ai-system-testing** — Testing your product's OWN LLM/AI features (prompt regression, model
306 output quality). This skill tests any app *using* an agent; it does not test your AI feature.
307- **exploratory-testing** — Human SBTM exploration and bug hunting. The agentic smoke goal is
308 the automated cousin; use exploratory-testing for charter-driven manual sessions.
309- **test-reliability** — Self-healing locators and quarantine for *scripted* flaky tests at
310 runtime. Complements the determinism levers here once a test has graduated.
311- **qa-project-context** — The universal dependency; supplies stack, environments, seed/reset
312 tooling, and model access that every question above depends on.
313
314## Reference Files (in `references/`)
315
316- **setup.md** — Playwright MCP registration (`.mcp.json`), the snapshot tool table, the
317 natural-language goal prompt with success/negative assertions, the determinism harness
318 config (pinned model, temperature 0, maxSteps, seed, prompt cache), and cost/latency levers.
319- **graduation-and-ci.md** — Playwright Test Agents promotion pipeline (planner → `specs/*.md`,
320 generator → `tests/*.spec.ts` with role-based locators, healer), the GitHub Actions gating
321 workflow with a machine-readable boolean verdict, and the canvas `--caps=vision` fallback.