Playwright In Sandbox
This is the primary Playwright skill for sandbox browser verification and deterministic end-to-end coverage.
Use it in two explicit modes:
- Interactive Sandbox Mode as final browser verification after a task's implementation is in a plausibly correct state.
- Deterministic E2E Mode before finishing a task when the changed flow should be protected by durable regression coverage.
This skill is intentionally generic. It should work for:
- task-level screenshot-driven verification after an agent has implemented UI work and needs browser proof
- formal Playwright E2E authoring or rewrite work in downstream application repos
- task flows where a final browser verification should happen before the task is considered complete
Do not use this skill for backend-only work, one-off page operations that do not justify browser automation, or broad failure storms before you understand the workflow inventory and root causes.
Core Workflow
- Write a brief QA inventory before touching the browser.
- Decide the mode first: Interactive Sandbox Mode or Deterministic E2E Mode.
- Start or confirm the app in a persistent session.
- Implement the change and get the functionality into a plausibly correct state before using Playwright as signoff.
- Run the changed flow interactively and inspect screenshots as evidence, not just DOM state.
- Record the contracts you learned:
- route-ready signals
- modal open and close signals
- action-enabled conditions
- save-complete signals
- durable
data-testid or semantic selectors
- If the flow is bug-fix, workflow, regression-critical, or meaningfully changed, graduate it into deterministic E2E coverage.
- If interactive proof shows the product behavior is wrong, fix the product code or the data contract. Do not make a bad behavior look green by weakening the test.
- Before finishing the task, ensure the changed flow has both:
- successful interactive proof
- durable E2E coverage or an explicit rationale why it stays interactive-only
Common Rules
These rules apply to both Interactive Sandbox Mode and Deterministic E2E Mode.
- Interactive verification is a post-change signoff step. Do not treat it as random mid-task poking while the implementation is still half-built.
- If the browser proves the product behavior is wrong, fix the functionality or the underlying data contract. Do not invent clever ways to make the test green around a bug.
- Use the repo's canonical E2E database contract strictly. If the repo standard is
e2e.db, use e2e.db. If e2e.db is missing and the repo expects one, create or provision e2e.db and keep using that contract. Do not silently fall back to the normal application database.
- Prefer querying the canonical E2E database or seeded business data to derive expected values, statuses, assignments, and aggregates. When the repo uses
better-sqlite3, it is acceptable to inspect the DB directly to confirm the real expected value before asserting the UI.
- Prefer selectors in this order:
- explicit
id, data-*, data-testid, or other owned semantic contracts
- accessible role plus stable accessible name
- label/control association
- stable URL, pathname, or query contract
- text-only selectors only when the text itself is the product contract
- CSS, XPath, or DOM-order selectors only for deliberate structure checks
- Remove stale screenshots, traces, and temporary artifacts from failed or superseded runs before signoff.
- If the repo has a maintained full-suite run, nightly QA run, or automated health check, keep visibility on whether it actually ran and whether it stayed green. Targeted checks do not replace suite health forever.
- Some migrated or one-off client applications may temporarily need broader migration-verification coverage than a typical greenfield app. That is allowed, but the quality bar stays the same: deterministic selectors, owned data, real user contracts, and no fake greens.
Mode Selection
Use Interactive Sandbox Mode when
- the implementation is already in a plausibly correct state
- you need final browser proof that the changed flow really works for a user
- you need screenshot evidence to judge whether the UI is actually correct
- you need to learn or confirm readiness gates, modal behavior, or durable selectors before writing or updating automated coverage
Use Deterministic E2E Mode when
- the change fixes a bug
- the task creates or materially changes a user workflow
- the flow is business-critical or likely to regress
- legacy Playwright coverage is being rewritten, consolidated, or retired
- the task should not be considered complete without regression protection
Stay in Interactive Mode only when
- the change is exploratory or temporary
- the flow is not durable enough yet to encode as regression coverage
- the task does not meaningfully change a maintained workflow
- a migrated or one-off app needs a temporary verification pass that is not yet stable enough to convert into durable E2E coverage
If you choose not to graduate to committed E2E coverage, be explicit about why.
Shared Environment Contract
- Prefer
127.0.0.1 over localhost unless the repo defines something else explicitly.
- Use the repo's explicit server contract first. If the repo does not define one,
4444 is the common sandbox default.
- In sandbox environments, Playwright browsers may live under
/ms-playwright; do not assume the default cache path.
- In sandbox environments, launch Chromium explicitly in headless mode:
chromium.launch({ headless: true }).
- Confirm the Playwright browser path when there is any doubt about the runtime payload:
echo "$PLAYWRIGHT_BROWSERS_PATH"
ls -al /ms-playwright
- Before
page.goto(...), verify the target port is actually listening and the app responds.
- For standard runs, use the repo's canonical E2E database contract. If the repo standard is
e2e.db, always use e2e.db.
- If the repo expects
e2e.db and it is missing, create or provision e2e.db before running tests.
- Only use alternate DB names or paths when the repo explicitly supports isolated validation lanes and you are intentionally isolating worker runs.
- Keep interactive artifacts separate from committed regression assets.
- scratch scripts and screenshots belong in temp or dedicated artifact folders
- committed regression coverage belongs in
tests/e2e/ or the repo's formal test location
- Remove stale screenshots, traces, and temporary artifacts from failed or superseded runs before signoff.
- When running multiple rewrite or validation lanes in parallel, isolate runtime resources:
- port
- database or seed state
- output folder
- screenshots and traces
Interactive Sandbox Mode
Use this mode to prove a changed user flow works right now after the implementation is done enough to verify.
Interactive mode is not permission to poke until something happens to pass once. Use it after implementing the change and after you believe the functionality should work, then use the browser as final visual and functional verification of the real user flow.
QA Inventory
Build the inventory from three sources:
- the user's requested requirements
- the user-visible behavior you implemented or changed
- the claims you expect to make in the final response
Anything that appears in any of those three sources must map to at least one QA check before signoff.
List:
- the user-visible claims you intend to sign off on
- every meaningful control, mode switch, or implemented interactive behavior
- the state changes or view changes each control can cause
- at least two exploratory or off-happy-path probes
Desktop Verification Script
Set TARGET_URL to the app you are debugging. Prefer 127.0.0.1 over localhost.
import { chromium } from "playwright";
const TARGET_URL = "http://127.0.0.1:4444";
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1600, height: 900 },
});
const page = await context.newPage();
try {
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded:", await page.title());
// Add the task-specific interactions and assertions here.
await page.screenshot({ path: "playwright-desktop.png", type: "png" });
} finally {
await context.close().catch(() => {});
await browser.close().catch(() => {});
}
Mobile Verification Script
Use a separate mobile script when the task affects responsive layout or touch behavior.
import { chromium } from "playwright";
const TARGET_URL = "http://127.0.0.1:4444";
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true,
});
const page = await context.newPage();
try {
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded mobile:", await page.title());
// Add the task-specific interactions and assertions here.
await page.screenshot({ path: "playwright-mobile.png", type: "png" });
} finally {
await context.close().catch(() => {});
await browser.close().catch(() => {});
}
Iteration Model
- Use one standalone Node.js verification script per focused flow.
- After code changes, rerun the verification script from a clean process instead of trying to preserve state across runs.
- Keep each script narrow: one changed flow, its main assertions, and its screenshot artifacts.
- If desktop and mobile both matter, run separate scripts or separate invocations.
- Screenshot review is part of the contract. Do not sign off from DOM assertions alone.
Interactive Checklists
Session Loop
- write the QA inventory
- make the code change
- run the Playwright verification script for the current flow as final browser verification
- rerun functional QA with real user input
- rerun visual QA separately
- capture final artifacts only after the UI is in the state you are actually evaluating
- record the selectors and readiness gates you would trust in formal E2E
Functional QA
- Use real user controls for signoff: keyboard, mouse, click, touch, or equivalent Playwright input APIs.
- Verify at least one end-to-end critical flow.
- Confirm the visible result of that flow, not just internal state.
- Work through the shared QA inventory rather than ad hoc spot checks.
- Cover every obvious visible control at least once before signoff, not only the happy path.
- After the scripted checks pass, do a short exploratory pass using normal input.
page.evaluate(...) may inspect or stage state, but it does not count as signoff input.
Visual QA
- Treat visual QA as separate from functional QA.
- Verify each visible claim explicitly in the state where it matters.
- Inspect the initial viewport before scrolling.
- Inspect the densest realistic state you can reach during QA.
- Look for clipping, overflow, distortion, weak contrast, broken layering, awkward motion, and stale overlays.
- If the UI only "works" because a hidden blocker was not noticed, the flow is not ready for signoff.
Interactive Signoff
- the functional path passed with normal user input
- the visual pass covered the whole relevant interface
- the viewport-fit checks passed for the intended initial view
- the final screenshots match the claims being signed off on
- the exploratory pass is called out in the final response
- the durable selectors and readiness gates are written down for E2E authoring
- stale screenshots or temporary artifacts from failed iterations are removed
Deterministic E2E Mode
Use this mode when you are authoring or rewriting real Playwright regression coverage.
The job is to protect real functionality. If the app behavior is wrong, fix the app. Do not preserve a bug by adjusting the test around it.
Workflow
- Start from what interactive validation already proved.
- Translate the learned UI contracts into durable automated checks.
- Keep each spec focused on one workflow family or one coherent user journey.
- Make each test own or explicitly receive its setup.
- Prefer canonical seeded data or database-backed expectations for values, statuses, and aggregates instead of magic literals the test does not own.
- Validate the changed flow before deleting or consolidating legacy coverage.
- If rewriting a brittle suite, map every removed scenario to one of:
- retained
- consolidated with justification
- intentionally obsolete with rationale
- quarantined with explicit explanation
Selector Hierarchy
Do not build durable tests on incidental text, generic combobox patterns, visual styling, or unstable overlay-sensitive DOM structure if a better contract exists.
Text-only selectors are a last resort unless the text itself is the true product contract.
Waiting and Readiness Rules
- Do not default to
waitForLoadState("networkidle").
- For navigation, wait on URL change plus a page-ready sentinel, or response contract plus a ready sentinel.
- For dialogs, wait on explicit open-state before interaction and close-state before the next dependent step.
- For saves, wait on an observable save-complete contract.
- For async controls, assert what makes the control enabled before clicking it.
- For flows with overlays, drawers, or command palettes, prove they are closed before interacting with the next surface.
- If an interaction fails because another surface still owns focus or pointer events, fix the readiness model instead of layering retries forever.
Navigation and Assertion Rules
- Assert behavior, not implementation.
- Prefer user-visible outcomes over internal implementation details.
- When numeric values, statuses, roster assignments, or aggregates come from seeded business data, derive the expected value from the test-owned setup or canonical DB contract first, then assert the UI matches it.
- Use text assertions only when the text itself is the contract.
- Avoid broad page-level text matching when the real contract lives in a specific card, section, dialog, or row.
- Do not keep multiple redundant assertions for the same workflow outcome just to make a spec feel thorough.
- Do not make a failing test green by broadening selectors, weakening expectations, or accepting the current bug unless that weaker contract is the real intended product behavior.
Hard Bans
- default
networkidle as the primary readiness strategy
page.evaluate(...) to assert behavior the UI can expose directly
- giant omnibus specs spanning multiple workflows
- generic selectors like
button[role="combobox"] as the primary contract
- silently reusing leftover state from prior tests
- deleting or consolidating legacy tests before the workflow mapping is explicit
Failure Convergence Protocol
When many tests fail, do not patch them one by one by default.
- Cluster failures by root cause first.
- auth or bootstrap
- stale server or shared port contention
- modal or overlay state
- selector contract gaps
- readiness gaps
- data or setup nondeterminism
- obsolete workflow assumptions
- Fix shared contracts before individual tests.
- If the spec shape is wrong, stop patching and rewrite that workflow family.
- If a spec keeps re-breaking because it mixes too many workflows, retire the omnibus and split it.
Rewrite Governance
- Coverage loss must be explicit, never accidental.
- Every removed legacy assertion must have a mapped replacement or a written obsolescence rationale.
- Prefer meaningful consolidation over duplicative green checks, but do not silently reduce user-journey coverage.
- Leave quarantined
fixme coverage only when:
- the behavior is genuinely blocked
- the quarantine is explicit
- the rest of the family can still move forward safely
- Keep formal E2E assets in committed test directories and exploratory scripts or screenshots out of those directories.
Parallel Rewrite Validation
Parallel rewrite work is allowed, but only when ownership and runtime isolation are real.
- Parallelize by workflow family or disjoint file ownership.
- Do not let multiple workers edit the same omnibus spec or shared helper without explicit ownership.
- If workers validate in parallel, isolate:
- port
- database or seed path
- output folder
- screenshots, traces, and artifacts
- If runtime isolation is not available, serialize Playwright validation even if rewrite coding stays parallel.
Full-Suite Freshness
- Keep targeted validation fast, but do not let the maintained full-suite contract rot.
- If the repo or automation stack supports daily, nightly, or pre-release full-suite runs, treat that as part of quality visibility.
- If automated agents are expected to keep QA green, there should be observable evidence that they ran, what they ran, and whether they stayed green.
- A passing targeted spec is not a substitute for maintaining the broader suite over time.
Dev Server
For local web debugging, keep the app running in a persistent TTY session. Do not rely on one-shot background commands from a short-lived shell.
Use the repo's documented startup flow first. If there is no explicit contract, the common sandbox pattern is:
pnpm run build
PORT=4444 pnpm run start
Before page.goto(...), verify the target port is listening and the app responds.
After interactive verification is complete, stop the server process you started so the sandbox stays clean for the rest of the task.
Common Failure Modes
- The browser flow passes only because an overlay or modal blocker was never actually closed.
- A test proves the DOM changed but does not prove the user-visible flow works.
- A spec uses the wrong roster, seed data, or identity assumptions and ends up "testing" fake state.
- A test is made green by adapting to the current bug instead of fixing the app or data contract.
- A test hard-codes display text, counts, or values that should have been derived from the test-owned DB or seed state.
- A worker rewrites files in parallel but validation still shares one fixed port and one mutable database.
- A suite looks green because duplicate tests were kept rather than properly consolidated.
- A large omnibus spec keeps hiding unique coverage because nobody mapped which extracted spec now owns each workflow.
Signoff Expectations
- Interactive proof exists for the changed flow.
- If the flow matters for regression, durable E2E coverage exists too.
- The browser evidence matches the claims being made in the final response.
- The selectors, waits, and assertions are tied to real UI contracts.
- Any skipped, quarantined, consolidated, or retired coverage is called out explicitly.
1---2name: playwright-in-sandbox3description: Primary Playwright governance skill for sandbox browser verification and deterministic end-to-end authoring or rewrite work.4---5
6# Playwright In Sandbox
7
8This is the primary Playwright skill for sandbox browser verification and deterministic end-to-end coverage.
9
10Use it in two explicit modes:
11
121. **Interactive Sandbox Mode** as final browser verification after a task's implementation is in a plausibly correct state.
132. **Deterministic E2E Mode** before finishing a task when the changed flow should be protected by durable regression coverage.
14
15This skill is intentionally generic. It should work for:
16
17- task-level screenshot-driven verification after an agent has implemented UI work and needs browser proof
18- formal Playwright E2E authoring or rewrite work in downstream application repos
19- task flows where a final browser verification should happen before the task is considered complete
20
21Do not use this skill for backend-only work, one-off page operations that do not justify browser automation, or broad failure storms before you understand the workflow inventory and root causes.
22
23## Core Workflow
24
251. Write a brief QA inventory before touching the browser.
262. Decide the mode first: Interactive Sandbox Mode or Deterministic E2E Mode.
273. Start or confirm the app in a persistent session.
284. Implement the change and get the functionality into a plausibly correct state before using Playwright as signoff.
295. Run the changed flow interactively and inspect screenshots as evidence, not just DOM state.
306. Record the contracts you learned:
31 - route-ready signals
32 - modal open and close signals
33 - action-enabled conditions
34 - save-complete signals
35 - durable `data-testid` or semantic selectors
367. If the flow is bug-fix, workflow, regression-critical, or meaningfully changed, graduate it into deterministic E2E coverage.
378. If interactive proof shows the product behavior is wrong, fix the product code or the data contract. Do not make a bad behavior look green by weakening the test.
389. Before finishing the task, ensure the changed flow has both:
39 - successful interactive proof
40 - durable E2E coverage or an explicit rationale why it stays interactive-only
41
42## Common Rules
43
44These rules apply to both Interactive Sandbox Mode and Deterministic E2E Mode.
45
46- Interactive verification is a post-change signoff step. Do not treat it as random mid-task poking while the implementation is still half-built.
47- If the browser proves the product behavior is wrong, fix the functionality or the underlying data contract. Do not invent clever ways to make the test green around a bug.
48- Use the repo's canonical E2E database contract strictly. If the repo standard is `e2e.db`, use `e2e.db`. If `e2e.db` is missing and the repo expects one, create or provision `e2e.db` and keep using that contract. Do not silently fall back to the normal application database.
49- Prefer querying the canonical E2E database or seeded business data to derive expected values, statuses, assignments, and aggregates. When the repo uses `better-sqlite3`, it is acceptable to inspect the DB directly to confirm the real expected value before asserting the UI.
50- Prefer selectors in this order:
51 1. explicit `id`, `data-*`, `data-testid`, or other owned semantic contracts
52 2. accessible role plus stable accessible name
53 3. label/control association
54 4. stable URL, pathname, or query contract
55 5. text-only selectors only when the text itself is the product contract
56 6. CSS, XPath, or DOM-order selectors only for deliberate structure checks
57- Remove stale screenshots, traces, and temporary artifacts from failed or superseded runs before signoff.
58- If the repo has a maintained full-suite run, nightly QA run, or automated health check, keep visibility on whether it actually ran and whether it stayed green. Targeted checks do not replace suite health forever.
59- Some migrated or one-off client applications may temporarily need broader migration-verification coverage than a typical greenfield app. That is allowed, but the quality bar stays the same: deterministic selectors, owned data, real user contracts, and no fake greens.
60
61## Mode Selection
62
63### Use Interactive Sandbox Mode when
64
65- the implementation is already in a plausibly correct state
66- you need final browser proof that the changed flow really works for a user
67- you need screenshot evidence to judge whether the UI is actually correct
68- you need to learn or confirm readiness gates, modal behavior, or durable selectors before writing or updating automated coverage
69
70### Use Deterministic E2E Mode when
71
72- the change fixes a bug
73- the task creates or materially changes a user workflow
74- the flow is business-critical or likely to regress
75- legacy Playwright coverage is being rewritten, consolidated, or retired
76- the task should not be considered complete without regression protection
77
78### Stay in Interactive Mode only when
79
80- the change is exploratory or temporary
81- the flow is not durable enough yet to encode as regression coverage
82- the task does not meaningfully change a maintained workflow
83- a migrated or one-off app needs a temporary verification pass that is not yet stable enough to convert into durable E2E coverage
84
85If you choose not to graduate to committed E2E coverage, be explicit about why.
86
87## Shared Environment Contract
88
89- Prefer `127.0.0.1` over `localhost` unless the repo defines something else explicitly.
90- Use the repo's explicit server contract first. If the repo does not define one, `4444` is the common sandbox default.
91- In sandbox environments, Playwright browsers may live under `/ms-playwright`; do not assume the default cache path.
92- In sandbox environments, launch Chromium explicitly in headless mode: `chromium.launch({ headless: true })`.
93- Confirm the Playwright browser path when there is any doubt about the runtime payload:
94
95```bash
96echo "$PLAYWRIGHT_BROWSERS_PATH"
97ls -al /ms-playwright
98```
99
100- Before `page.goto(...)`, verify the target port is actually listening and the app responds.
101- For standard runs, use the repo's canonical E2E database contract. If the repo standard is `e2e.db`, always use `e2e.db`.
102- If the repo expects `e2e.db` and it is missing, create or provision `e2e.db` before running tests.
103- Only use alternate DB names or paths when the repo explicitly supports isolated validation lanes and you are intentionally isolating worker runs.
104- Keep interactive artifacts separate from committed regression assets.
105 - scratch scripts and screenshots belong in temp or dedicated artifact folders
106 - committed regression coverage belongs in `tests/e2e/` or the repo's formal test location
107- Remove stale screenshots, traces, and temporary artifacts from failed or superseded runs before signoff.
108- When running multiple rewrite or validation lanes in parallel, isolate runtime resources:
109 - port
110 - database or seed state
111 - output folder
112 - screenshots and traces
113
114## Interactive Sandbox Mode
115
116Use this mode to prove a changed user flow works right now after the implementation is done enough to verify.
117
118Interactive mode is not permission to poke until something happens to pass once. Use it after implementing the change and after you believe the functionality should work, then use the browser as final visual and functional verification of the real user flow.
119
120### QA Inventory
121
122Build the inventory from three sources:
123
124- the user's requested requirements
125- the user-visible behavior you implemented or changed
126- the claims you expect to make in the final response
127
128Anything that appears in any of those three sources must map to at least one QA check before signoff.
129
130List:
131
132- the user-visible claims you intend to sign off on
133- every meaningful control, mode switch, or implemented interactive behavior
134- the state changes or view changes each control can cause
135- at least two exploratory or off-happy-path probes
136
137### Desktop Verification Script
138
139Set `TARGET_URL` to the app you are debugging. Prefer `127.0.0.1` over `localhost`.
140
141```javascript
142import { chromium } from "playwright";
143
144const TARGET_URL = "http://127.0.0.1:4444";
145const browser = await chromium.launch({ headless: true });
146const context = await browser.newContext({
147 viewport: { width: 1600, height: 900 },
148});
149const page = await context.newPage();
150
151try {
152 await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
153 console.log("Loaded:", await page.title());
154
155 // Add the task-specific interactions and assertions here.
156
157 await page.screenshot({ path: "playwright-desktop.png", type: "png" });
158} finally {
159 await context.close().catch(() => {});
160 await browser.close().catch(() => {});
161}
162```
163
164### Mobile Verification Script
165
166Use a separate mobile script when the task affects responsive layout or touch behavior.
167
168```javascript
169import { chromium } from "playwright";
170
171const TARGET_URL = "http://127.0.0.1:4444";
172const browser = await chromium.launch({ headless: true });
173const context = await browser.newContext({
174 viewport: { width: 390, height: 844 },
175 isMobile: true,
176 hasTouch: true,
177});
178const page = await context.newPage();
179
180try {
181 await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
182 console.log("Loaded mobile:", await page.title());
183
184 // Add the task-specific interactions and assertions here.
185
186 await page.screenshot({ path: "playwright-mobile.png", type: "png" });
187} finally {
188 await context.close().catch(() => {});
189 await browser.close().catch(() => {});
190}
191```
192
193### Iteration Model
194
195- Use one standalone Node.js verification script per focused flow.
196- After code changes, rerun the verification script from a clean process instead of trying to preserve state across runs.
197- Keep each script narrow: one changed flow, its main assertions, and its screenshot artifacts.
198- If desktop and mobile both matter, run separate scripts or separate invocations.
199- Screenshot review is part of the contract. Do not sign off from DOM assertions alone.
200
201### Interactive Checklists
202
203#### Session Loop
204
205- write the QA inventory
206- make the code change
207- run the Playwright verification script for the current flow as final browser verification
208- rerun functional QA with real user input
209- rerun visual QA separately
210- capture final artifacts only after the UI is in the state you are actually evaluating
211- record the selectors and readiness gates you would trust in formal E2E
212
213#### Functional QA
214
215- Use real user controls for signoff: keyboard, mouse, click, touch, or equivalent Playwright input APIs.
216- Verify at least one end-to-end critical flow.
217- Confirm the visible result of that flow, not just internal state.
218- Work through the shared QA inventory rather than ad hoc spot checks.
219- Cover every obvious visible control at least once before signoff, not only the happy path.
220- After the scripted checks pass, do a short exploratory pass using normal input.
221- `page.evaluate(...)` may inspect or stage state, but it does not count as signoff input.
222
223#### Visual QA
224
225- Treat visual QA as separate from functional QA.
226- Verify each visible claim explicitly in the state where it matters.
227- Inspect the initial viewport before scrolling.
228- Inspect the densest realistic state you can reach during QA.
229- Look for clipping, overflow, distortion, weak contrast, broken layering, awkward motion, and stale overlays.
230- If the UI only "works" because a hidden blocker was not noticed, the flow is not ready for signoff.
231
232#### Interactive Signoff
233
234- the functional path passed with normal user input
235- the visual pass covered the whole relevant interface
236- the viewport-fit checks passed for the intended initial view
237- the final screenshots match the claims being signed off on
238- the exploratory pass is called out in the final response
239- the durable selectors and readiness gates are written down for E2E authoring
240- stale screenshots or temporary artifacts from failed iterations are removed
241
242## Deterministic E2E Mode
243
244Use this mode when you are authoring or rewriting real Playwright regression coverage.
245
246The job is to protect real functionality. If the app behavior is wrong, fix the app. Do not preserve a bug by adjusting the test around it.
247
248### Workflow
249
2501. Start from what interactive validation already proved.
2512. Translate the learned UI contracts into durable automated checks.
2523. Keep each spec focused on one workflow family or one coherent user journey.
2534. Make each test own or explicitly receive its setup.
2545. Prefer canonical seeded data or database-backed expectations for values, statuses, and aggregates instead of magic literals the test does not own.
2556. Validate the changed flow before deleting or consolidating legacy coverage.
2567. If rewriting a brittle suite, map every removed scenario to one of:
257 - retained
258 - consolidated with justification
259 - intentionally obsolete with rationale
260 - quarantined with explicit explanation
261
262### Selector Hierarchy
263
264Do not build durable tests on incidental text, generic combobox patterns, visual styling, or unstable overlay-sensitive DOM structure if a better contract exists.
265Text-only selectors are a last resort unless the text itself is the true product contract.
266
267### Waiting and Readiness Rules
268
269- Do not default to `waitForLoadState("networkidle")`.
270- For navigation, wait on URL change plus a page-ready sentinel, or response contract plus a ready sentinel.
271- For dialogs, wait on explicit open-state before interaction and close-state before the next dependent step.
272- For saves, wait on an observable save-complete contract.
273- For async controls, assert what makes the control enabled before clicking it.
274- For flows with overlays, drawers, or command palettes, prove they are closed before interacting with the next surface.
275- If an interaction fails because another surface still owns focus or pointer events, fix the readiness model instead of layering retries forever.
276
277### Navigation and Assertion Rules
278
279- Assert behavior, not implementation.
280- Prefer user-visible outcomes over internal implementation details.
281- When numeric values, statuses, roster assignments, or aggregates come from seeded business data, derive the expected value from the test-owned setup or canonical DB contract first, then assert the UI matches it.
282- Use text assertions only when the text itself is the contract.
283- Avoid broad page-level text matching when the real contract lives in a specific card, section, dialog, or row.
284- Do not keep multiple redundant assertions for the same workflow outcome just to make a spec feel thorough.
285- Do not make a failing test green by broadening selectors, weakening expectations, or accepting the current bug unless that weaker contract is the real intended product behavior.
286
287### Hard Bans
288
289- default `networkidle` as the primary readiness strategy
290- `page.evaluate(...)` to assert behavior the UI can expose directly
291- giant omnibus specs spanning multiple workflows
292- generic selectors like `button[role="combobox"]` as the primary contract
293- silently reusing leftover state from prior tests
294- deleting or consolidating legacy tests before the workflow mapping is explicit
295
296## Failure Convergence Protocol
297
298When many tests fail, do not patch them one by one by default.
299
3001. Cluster failures by root cause first.
301 - auth or bootstrap
302 - stale server or shared port contention
303 - modal or overlay state
304 - selector contract gaps
305 - readiness gaps
306 - data or setup nondeterminism
307 - obsolete workflow assumptions
3082. Fix shared contracts before individual tests.
3093. If the spec shape is wrong, stop patching and rewrite that workflow family.
3104. If a spec keeps re-breaking because it mixes too many workflows, retire the omnibus and split it.
311
312## Rewrite Governance
313
314- Coverage loss must be explicit, never accidental.
315- Every removed legacy assertion must have a mapped replacement or a written obsolescence rationale.
316- Prefer meaningful consolidation over duplicative green checks, but do not silently reduce user-journey coverage.
317- Leave quarantined `fixme` coverage only when:
318 - the behavior is genuinely blocked
319 - the quarantine is explicit
320 - the rest of the family can still move forward safely
321- Keep formal E2E assets in committed test directories and exploratory scripts or screenshots out of those directories.
322
323## Parallel Rewrite Validation
324
325Parallel rewrite work is allowed, but only when ownership and runtime isolation are real.
326
327- Parallelize by workflow family or disjoint file ownership.
328- Do not let multiple workers edit the same omnibus spec or shared helper without explicit ownership.
329- If workers validate in parallel, isolate:
330 - port
331 - database or seed path
332 - output folder
333 - screenshots, traces, and artifacts
334- If runtime isolation is not available, serialize Playwright validation even if rewrite coding stays parallel.
335
336## Full-Suite Freshness
337
338- Keep targeted validation fast, but do not let the maintained full-suite contract rot.
339- If the repo or automation stack supports daily, nightly, or pre-release full-suite runs, treat that as part of quality visibility.
340- If automated agents are expected to keep QA green, there should be observable evidence that they ran, what they ran, and whether they stayed green.
341- A passing targeted spec is not a substitute for maintaining the broader suite over time.
342
343## Dev Server
344
345For local web debugging, keep the app running in a persistent TTY session. Do not rely on one-shot background commands from a short-lived shell.
346
347Use the repo's documented startup flow first. If there is no explicit contract, the common sandbox pattern is:
348
349```bash
350pnpm run build
351PORT=4444 pnpm run start
352```
353
354Before `page.goto(...)`, verify the target port is listening and the app responds.
355
356After interactive verification is complete, stop the server process you started so the sandbox stays clean for the rest of the task.
357
358## Common Failure Modes
359
360- The browser flow passes only because an overlay or modal blocker was never actually closed.
361- A test proves the DOM changed but does not prove the user-visible flow works.
362- A spec uses the wrong roster, seed data, or identity assumptions and ends up "testing" fake state.
363- A test is made green by adapting to the current bug instead of fixing the app or data contract.
364- A test hard-codes display text, counts, or values that should have been derived from the test-owned DB or seed state.
365- A worker rewrites files in parallel but validation still shares one fixed port and one mutable database.
366- A suite looks green because duplicate tests were kept rather than properly consolidated.
367- A large omnibus spec keeps hiding unique coverage because nobody mapped which extracted spec now owns each workflow.
368
369## Signoff Expectations
370
371- Interactive proof exists for the changed flow.
372- If the flow matters for regression, durable E2E coverage exists too.
373- The browser evidence matches the claims being made in the final response.
374- The selectors, waits, and assertions are tied to real UI contracts.
375- Any skipped, quarantined, consolidated, or retired coverage is called out explicitly.