---
title: "AGENTS.md for Playwright: Rules for Stable AI Tests in CI"
description: "Write AGENTS.md rules for Playwright from your CI failures: locators, waits, isolation, a done command that matches CI, and checks that enforce them."
canonical: "https://currents.dev/posts/agents-md-for-playwright-rules-for-stable-ai-tests-in-ci"
updated: "2026-11-05"
---

# AGENTS.md for Playwright: Rules for Stable AI Tests in CI

Published 2026-11-05 by Walter Galvão.

Canonical URL: https://currents.dev/posts/agents-md-for-playwright-rules-for-stable-ai-tests-in-ci

Write AGENTS.md rules for Playwright from your CI failures: locators, waits, isolation, a done command that matches CI, and checks that enforce them.

---


> **TL;DR**
>
> Write a short AGENTS.md where each rule comes from a failure your suite has had in CI and points at the check that enforces it, and where done means passing the same checks CI runs. Every rule a tool can enforce should also live in config, lint or a PR check.
>
> 1. Start from the tests Playwright has flagged as flaky and from the failures that blocked a merge.
> 2. Give the agent the exact command CI runs, flag for flag. Pin `--project` to the project CI runs and `--workers` to the worker count CI uses.
> 3. Never call `page.waitForTimeout`, and use no raw CSS or XPath selectors. Name every record a test creates from `testInfo.testId`.

A coding agent writes a Playwright spec, runs it once, watches it pass and reports the task done. Then CI runs the same spec alongside the rest of the suite, on another shard, with a different worker count, and the spec fails. The rerun passes. Nobody can tell from the report whether the app broke or the test did.

The agent did what it knew. Left without guidance, agents write Playwright tests from generic patterns: `waitForTimeout` calls, brittle CSS selectors and retries that hide real bugs. An agent's idea of done is the one check it ran, a single green pass on a quiet laptop. CI asks harder questions. Does the test pass next to tests it has never met? Does it pass when `fullyParallel: true` spreads its file's tests across shards? Does it pass in a pipeline that fails the run when any test is marked flaky?

A generic style list in AGENTS.md doesn't close that gap. The agent can't tell which lines matter most, and it has no way to check whether it followed any of them.

What does close it is a short AGENTS.md where each rule comes from a failure your suite has had in CI, each rule points at the check that enforces it, and done means passing the same checks CI runs.

## AGENTS.md vs Test Agents

Two different things in a Playwright repo now carry the word "agents", and mixing them up puts your rules in the wrong file. AGENTS.md holds agent instructions for the AI agents that work on a repository, and you can keep one or more of them anywhere in the repo. [Playwright's Test Agents](https://playwright.dev/docs/test-agents) are a separate feature: Playwright ships three of them out of the box, [the planner, the generator and the healer](/posts/9-strategies-to-get-the-most-out-of-playwright-test-agents).

You add the Test Agent definitions to a project with the `init-agents` command, and the docs say to regenerate them whenever Playwright is updated so they pick up new tools and instructions. Each definition carries one agent's own instructions. AGENTS.md carries your suite's rules, and those should hold whether a spec comes from the generator, the healer or a general coding agent working a ticket.

<AntiPattern title="Rules in agent definitions" id="anti-pattern-rules-in-agent-definitions">

The generated definitions sit in the repo and read like instructions, so the obvious move is to add "prefer roles" or "never sleep" to them. Those files are Playwright's, and the docs tell you to regenerate them after every upgrade. A rule that lives in a file you regenerate is a rule you have to remember to put back.

<AntiPattern.Fix>

Keep project rules in AGENTS.md, and treat the definitions as generated output you refresh on every upgrade:

```bash
# Add the planner, generator and healer definitions next to your AGENTS.md
npx playwright init-agents
# After each Playwright upgrade, regenerate them
npm install -D @playwright/test@latest
npx playwright init-agents
```

The example inside a definition matters too. A user reported that Playwright's generator agent definition contains invalid TypeScript in its example ([#42743](https://github.com/microsoft/playwright/issues/42743)). The reporter noted that it is the one concrete example of a generated spec in the file, so it is exactly the thing a model pattern-matches when producing a new test. The issue was closed as completed.

</AntiPattern.Fix>

</AntiPattern>

## Rules from CI failures

Your CI history already holds the rules you need. Start from [the tests Playwright has flagged as flaky](/posts/playwright-flaky-tests-detect-them-in-ci-with-retries), the ones that failed on the first run but passed on a retry, and from the failures that blocked a merge. For each one, ask what the agent would have needed to know to avoid it.

[Currents Flaky Tests](https://docs.currents.dev/dashboard/tests/flaky-tests) marks a test flaky when it has retries enabled and doesn't pass on its first attempt, and its Test Explorer shows the tests with the highest flakiness rate, so that list is where your first rules come from.

Write every rule in three parts: the rule, the CI failure it prevents and the check that enforces it. The failure gives the agent a reason, which is what it needs when it meets a case the rule doesn't name. The check tells the agent, and you, when the rule was broken.

<AntiPattern title="A copied style template" id="anti-pattern-a-copied-style-template">

A team pastes a template of good advice into AGENTS.md and moves on:

```
<!-- Anti-pattern: nothing says which rule matters, and nothing shows when one is broken -->
# Testing guidelines

- Write clean, readable tests.
- Follow Playwright best practices.
- Use good selectors.
- Avoid flaky tests.
- Keep tests independent.
```

Every line is true, and none of them changes what the agent writes. "Avoid flaky tests" is a goal, not an instruction. "Use good selectors" leaves the agent to decide what good means, and nothing in CI fails when it decides wrong.

<AntiPattern.Fix>

Write one rule per failure, with its reason and its check. Here is the ban on `page.waitForTimeout` in that shape:

```
## Waits
- Rule: never call `page.waitForTimeout`. Wait with a locator action or a web assertion; both wait on their own.
- Prevents: a fixed sleep that passes at local speed and fails when CI is slower. Playwright's docs call tests that wait for time inherently flaky.
- Check: the PR job fails on any `waitForTimeout` under `tests/`, and the repeat run with retries off catches the timing guesses the grep misses.
```

</AntiPattern.Fix>

</AntiPattern>

### Cut and move

Cut any rule with no CI failure behind it. When config or lint can enforce a rule, put the rule in that tool and leave a one-line pointer in AGENTS.md. A rule the agent can read past is weaker than one that fails the build.

## Project map and commands

The first thing the agent needs is the exact command CI runs, flag for flag. Without it, the agent's green run says something about a setup nobody ships.

<AntiPattern title="'Run the tests'" id="anti-pattern-run-the-tests">

An AGENTS.md line like "run the tests before you finish" leaves the agent to type plain `npx playwright test`. By default, that command runs every project, and it sets workers to 50% of the machine's logical CPU cores. Playwright recommends one worker in CI, for stability and reproducibility. If your CI pins one project and one worker, the agent's pass came from a different run than CI's.

<AntiPattern.Fix>

Give the agent the exact CI command, plus a map of where things go. Pin `--project` to the project CI runs, and pin `--workers` to the worker count CI uses:

```
## Project map and commands
- Specs live in `tests/<feature>/<behavior>.spec.ts`, one user behavior per file.
- Reference spec: copy the imports, structure and naming of `tests/orders/cancel-order.spec.ts`.
- One `test.describe` per feature. Test titles say what the user does: "guest cancels an unpaid order".
- Put a short `// Step:` comment above each user step.
- CI command, exactly as written: `npx playwright test --project=chromium --workers=1`
- Lint and types: `npx eslint tests` and `npx tsc --noEmit`
```

List commands and files, not explanations. The agent needs the command it should type, not a tour of the repo. One reference spec beats a paragraph of conventions, because the agent can copy a real file's imports and naming instead of guessing them from a description.

</AntiPattern.Fix>

</AntiPattern>

## Locator and wait rules

The selector and the wait are the two lines that decide whether an agent's spec passes once or passes every time.

To make tests resilient, Playwright recommends prioritizing user-facing attributes and explicit contracts such as `page.getByRole()`. Turn that into an order the agent follows: role first, then label or visible text, then a test ID, and no raw CSS or XPath selectors at all.

Waits get a flat ban. The [docs for `page.waitForTimeout`](https://playwright.dev/docs/api/class-page) say never to wait for a timeout in production, because tests that wait for time are inherently flaky, and to use Locator actions and web assertions, which wait automatically. Extend the ban to any wait that guesses at timing instead of waiting for something the user can see.

<AntiPattern title="Sleep after a CSS click" id="anti-pattern-sleep-after-a-css-click">

The agent's spec passed on its machine, so the sleep looks harmless:

```ts
import { test, expect } from '@playwright/test';

test('guest places an order', async ({ page }) => {
  // Anti-pattern: the selector is tied to markup, and the sleep guesses how long the order takes
  await page.goto('/checkout');
  await page.locator('#checkout > div.actions > button.btn-primary').click();
  await page.waitForTimeout(3000);
  expect(await page.locator('.confirmation h2').textContent()).toContain('Order placed');
});
```

The three-second sleep is a guess about how fast the app answers. The guess holds at local speed and fails when CI is slower, and the selector breaks the next time someone restyles the button.

<AntiPattern.Fix>

Find the button by its role and name, and let a web assertion do the waiting:

```ts
import { test, expect } from '@playwright/test';

test('guest places an order', async ({ page }) => {
  await page.goto('/checkout');
  await page.getByRole('button', { name: 'Place order' }).click();
  // The web assertion waits for the heading on its own
  await expect(page.getByRole('heading', { name: /Order \d+ placed/ })).toBeVisible();
});
```

</AntiPattern.Fix>

</AntiPattern>

### Lint for missing awaits

Lint can catch a missing `await` before an asynchronous call to the Playwright API. Playwright recommends the `@typescript-eslint/no-floating-promises` ESLint rule to make sure no `await` is missing before an asynchronous call to the Playwright API. Turn it on, and lint flags a forgotten `await`.

## Isolation and test data

Agents write tests the way a story reads: create the order, then cancel it. That story breaks in CI. Parallel tests run in separate worker processes and can't share state or global variables, and each test runs its relevant hooks, `beforeAll` and `afterAll` included, just for itself. A test that leaks state through a module-level variable, or depends on another test's side effects, works when tests run in order and breaks when they run in parallel or in a different order.

<AntiPattern title="Shared record between tests" id="anti-pattern-shared-record-between-tests">

The agent creates an order in one test and reuses its ID in the next:

```ts
import { test, expect } from '@playwright/test';

// Anti-pattern: the cancel test reads an ID the create test wrote. In another worker or another order, orderId is undefined.
let orderId: string;

test('user creates an order', async ({ page }) => {
  await page.goto('/orders/new');
  await page.getByRole('button', { name: 'Create order' }).click();
  orderId = (await page.getByTestId('order-id').textContent()) ?? '';
});

test('user cancels the order', async ({ page }) => {
  await page.goto(`/orders/${orderId}`);
  await page.getByRole('button', { name: 'Cancel order' }).click();
  await expect(page.getByRole('status')).toHaveText('Cancelled');
});
```

Run the file alone, in order, and both tests pass. With `fullyParallel` on, all tests in all files run in parallel, and parallel tests run in separate worker processes that can't share state or global variables. `--shuffle` also shuffles individual tests in parallel mode.

<AntiPattern.Fix>

Parallel tests that create or edit the same record race with each other, and [Playwright's docs](https://playwright.dev/docs/test-parallel) recommend deriving a unique identifier from `testInfo.testId` to avoid collisions. Make every test create its own data:

```ts
import { test, expect } from '@playwright/test';

test('user cancels an order', async ({ page, request }, testInfo) => {
  // A reference derived from the test ID can't collide with another test's order
  const response = await request.post('/api/orders', {
    data: { reference: `order-${testInfo.testId}` },
  });
  expect(response.ok()).toBeTruthy();
  const { id } = await response.json();

  await page.goto(`/orders/${id}`);
  await page.getByRole('button', { name: 'Cancel order' }).click();
  await expect(page.getByRole('status')).toHaveText('Cancelled');
});
```

</AntiPattern.Fix>

</AntiPattern>

### No serial escape hatch

Serial mode looks like a quick way to force the order back. Forbid it in AGENTS.md. Playwright doesn't recommend serial mode and says it is usually better to make tests isolated, so they can run independently. A test that needs its sibling has a bug, and serial mode only hides it.

## A definition of done

Done has to be a command the agent runs, not a judgment it makes. Write it into AGENTS.md as commands, and make each one a check CI would also fail.

<AntiPattern title="Checking with retries on" id="anti-pattern-checking-with-retries-on">

The agent runs the new spec under the CI config, retries and all:

```bash
# Anti-pattern: with retries on, a test that fails and then passes on a retry still exits green
npx playwright test tests/orders/cancel-order.spec.ts --project=chromium --retries=2
```

`--fail-on-flaky-tests` is off by default, so unless the config sets `failOnFlakyTests`, a spec that fails and then passes on a retry exits green under this command, and the agent reports it done.

<AntiPattern.Fix>

Make every repetition count, randomize the order and split the spec across shards:

```
## Definition of done
For every new or changed spec, run all three commands yourself, non-interactively, and paste each command and its result into the PR. Any failure means the spec is not done.

1. `npx playwright test <spec> --project=chromium --workers=1 --repeat-each=10 --retries=0 --fully-parallel --shuffle`
2. `npx playwright test <spec> --project=chromium --workers=1 --retries=0 --fully-parallel --shard=1/2`
3. `npx playwright test <spec> --project=chromium --workers=1 --retries=0 --fully-parallel --shard=2/2`

Never add retries, sleeps or skips to make these pass.
```

Each flag closes one escape route. `--repeat-each` runs each test N times, and `--retries=0` means no retries, so no repetition can pass on a second try. The 10 is a starting point, not a threshold. `--fully-parallel` runs all tests in parallel. In the config, `fullyParallel` runs all tests in all files in parallel. In that mode, `--shuffle` randomizes individual tests as well as files, which helps to find tests that depend on each other.

The shard commands catch what one process can hide. Playwright can only shard tests that can run in parallel, which by default means whole files. With `--fully-parallel`, every test can run in parallel, so the two [shard](https://playwright.dev/docs/test-sharding) commands split the spec's own tests between two separate runs. The docs recommend `fullyParallel: true` for a balanced distribution across shards, especially in CI.

Leave `--fail-on-flaky-tests` out of this list. The flag fails the run if any test is flagged as flaky. Playwright flags a test as flaky when it failed on the first run but passed when retried, so with `--retries=0` no test is ever flagged. The flag belongs in the CI config, where retries are on.

Pasting the command and its result turns "I ran the tests" into something a reviewer can check at a glance.

</AntiPattern.Fix>

</AntiPattern>

## Enforcing rules in CI

AGENTS.md is context, and an agent can read past any line of it. Every rule a tool can enforce should also live in config, lint or a PR check.

<AntiPattern title="Reviewers as the safety net" id="anti-pattern-reviewers-as-the-safety-net">

An agent's PR arrives with a test marked `test.only()` or a group marked `test.describe.only()`. A reviewer reading a diff can miss it, and the agent will make the same mistake in its next PR.

<AntiPattern.Fix>

Start with `playwright.config.ts`. `forbidOnly` makes the run exit with an error if any test or group is marked `test.only()` or `test.describe.only()`. `failOnFlakyTests` makes the run exit with an error when any test is marked flaky, and `--fail-on-flaky-tests` is its CLI form. The docs call both settings useful on CI.

```ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  // Every test can run on its own, so shards split by test, not by file
  fullyParallel: true,
  // A leftover test.only fails the run on CI
  forbidOnly: !!process.env.CI,
  // Retries on CI give failOnFlakyTests something to catch
  retries: process.env.CI ? 2 : 0,
  failOnFlakyTests: !!process.env.CI,
  // One worker on CI, as Playwright recommends
  workers: process.env.CI ? 1 : undefined,
  use: {
    baseURL: process.env.BASE_URL,
  },
  projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
});
```

Keep retries on in CI. A test is only flaky when it fails first and passes on a retry, so without retries `failOnFlakyTests` never fires.

The PR job runs what the config can't: ESLint with `@typescript-eslint/no-floating-promises` turned on in your ESLint config, and `tsc --noEmit`, which [Playwright recommends](https://playwright.dev/docs/best-practices) running on CI to check that functions are called with the right signature. It also greps for fixed waits and runs the first done command on every changed spec:

```yaml
name: Spec checks
on: pull_request

jobs:
  spec-checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - name: Lint
        run: npx eslint tests
      - name: Type check
        run: npx tsc --noEmit
      - name: No fixed waits
        run: |
          if git grep -n "waitForTimeout" -- tests; then
            echo "page.waitForTimeout is banned in tests (see AGENTS.md)"
            exit 1
          fi
      - run: npx playwright install --with-deps chromium
      - name: Repeat changed specs with retries off
        run: |
          CHANGED=$(git diff --name-only --diff-filter=AM "origin/${{ github.base_ref }}...HEAD" -- 'tests/*.spec.ts')
          if [ -z "$CHANGED" ]; then
            echo "No changed specs"
            exit 0
          fi
          npx playwright test $CHANGED --project=chromium --workers=1 --repeat-each=10 --retries=0 --fully-parallel --shuffle
```

In the diff, `github.base_ref` is the base ref, or target branch, of the pull request in a workflow run.

</AntiPattern.Fix>

</AntiPattern>

## Rules for fixing failures

A failing test gives an agent two ways out: [fix the cause](/posts/claude-code-playwright-mcp-fix-tests-that-fail-in-ci), or make the red go away. Without rules, the second one is faster.

Make the agent name the root cause before it edits anything: a selector that no longer matches, a timing guess, a test-data collision, or a real change in the app. Each cause has its own fix. A stale selector moves to `page.getByRole()`, and a timing guess becomes a web assertion. A collision gets a record named from `testInfo.testId`.

Some fixes are forbidden outright: adding `page.waitForTimeout`, raising retries and skipping the test. When retries are enabled, Playwright retries a failing test until it passes or reaches the maximum, so a test that failed on the first run but passed when retried is categorized as flaky. A skip hides the bug entirely.

The last cause needs its own rule. When the app's behavior changed, the agent leaves the test failing and reports a possible regression instead of rewriting the assertion to match. A request for a Test Agent that reports real regressions and leaves the test alone was closed as not planned ([#43675](https://github.com/microsoft/playwright/issues/43675)). A maintainer replied that you should just ask your agent to classify things directly. So the rule has to live in your AGENTS.md.

<AntiPattern title="Patching until green" id="anti-pattern-patching-until-green">

A coding agent, or the healer, is told to make a failing test pass, and nothing in the repo says how. The quickest patches are a sleep, a higher retry count or a skip, and each one turns the run green while the regression ships.

<AntiPattern.Fix>

Give the agent a procedure that starts by reproducing the failure and ends with the definition of done:

```
## Fixing a failing test
1. Reproduce first: `npx playwright test <spec> --project=chromium --workers=1 --repeat-each=10 --retries=0`
2. Name the root cause before editing: selector, timing, test-data collision or a change in the app.
3. Forbidden fixes: adding `page.waitForTimeout`, raising retries, skipping the test.
4. If the app's behavior changed, don't touch the assertion. Leave the test failing and report a possible regression in the PR.
5. After the fix, pass every command in the definition of done.
```

Reproducing with retries off shows the agent is fixing the failure CI saw, and the done commands show the fix holds.

</AntiPattern.Fix>

</AntiPattern>

## Agent gotchas in CI

The done command only helps if the agent runs tests the way CI does, and a few defaults pull the two apart.

### Scripts the agent skips

Don't assume the agent launches tests through your npm scripts. A reporter asked for Playwright's agents and MCP tools to support npm script based execution, because they could not handle a project's custom command line arguments ([#38375](https://github.com/microsoft/playwright/issues/38375)). The request was closed as not planned. A maintainer replied that MCP uses the environment passed from whatever launches it. Put every setting a test depends on in `playwright.config.ts` or the environment, never only in a script's arguments.

### Siblings in one file

By default, test files run in parallel, but the tests inside a single file run in order in the same worker process. A test that leaks state through a module-level variable or depends on another test's side effects works when tests run in order, but breaks the moment they run in parallel or in a different order. With `fullyParallel: true`, Playwright also spreads a file's tests [across shards](/posts/optimizing-test-runtime-playwright-sharding-vs-workers). That is why every done command carries `--fully-parallel`.

### Laptop defaults

`--project` runs only tests from the specified list of projects and defaults to running all projects, and the `--workers` default is 50% of logical CPU cores. [Playwright's CI guide](https://playwright.dev/docs/ci) recommends setting workers to 1 in CI environments. The done commands pin both `--project` and `--workers`, so the agent's local run uses the same projects and worker count as CI.

## The complete AGENTS.md

The finished file has five short blocks. Keep it that short: every line traces to a CI failure or points at the check that enforces it, and a line that does neither is a line to cut.

```
# AGENTS.md

Rules for Playwright specs in this repo. Each rule names the CI failure it prevents and the check that enforces it.

## Project map and commands
- Specs: `tests/<feature>/<behavior>.spec.ts`, one user behavior per file.
- Reference spec: copy `tests/orders/cancel-order.spec.ts`.
- CI command, exactly: `npx playwright test --project=chromium --workers=1`
- Lint and types: `npx eslint tests` and `npx tsc --noEmit`

## Locators and waits
- Locate with `page.getByRole` first, then by label or visible text, then by test ID. Never raw CSS or XPath. Prevents: selectors that break when markup changes. Check: review.
- Never call `page.waitForTimeout` or guess at timing. Use locator actions and web assertions. Prevents: sleeps that pass locally and fail on slower CI runs. Check: the PR grep and the repeat run.
- Await every Playwright call. Prevents: missing awaits before Playwright API calls. Check: `@typescript-eslint/no-floating-promises`.

## Isolation and test data
- No module-level state shared between tests, and no test that depends on another test's side effects. Prevents: specs that pass in order and break in parallel. Check: the definition of done.
- Name every record a test creates from `testInfo.testId`. Prevents: parallel tests racing on one record. Check: the definition of done.
- Never use serial mode to restore an order. Make the tests independent. Prevents: one failing test skipping the rest of the file. Check: the definition of done.

## Definition of done
Run these yourself, non-interactively, for every new or changed spec. Paste each command and its result into the PR.
1. `npx playwright test <spec> --project=chromium --workers=1 --repeat-each=10 --retries=0 --fully-parallel --shuffle`
2. `npx playwright test <spec> --project=chromium --workers=1 --retries=0 --fully-parallel --shard=1/2`
3. `npx playwright test <spec> --project=chromium --workers=1 --retries=0 --fully-parallel --shard=2/2`

## Fixing a failing test
1. Reproduce with command 1 of the definition of done.
2. Name the root cause before editing: selector, timing, test-data collision or app change.
3. Never fix with `page.waitForTimeout`, more retries or a skip.
4. If the app's behavior changed, leave the test failing and report a possible regression.
5. Finish by passing the definition of done.
```

### Test the file

Don't trust a new AGENTS.md until it has caught something. Ask the agent for a spec, then plant the bug from the isolation section: a module-level variable that one test writes and the next one reads. Run the first done command. If it passes, the file isn't doing its job yet.

## Where AGENTS.md stops helping

AGENTS.md makes each new spec prove itself before it merges. It can't show you a test that fails in one CI run out of many, months later, after the app around it changed. Ten repetitions on a laptop are a sample. Only run history is a record.

The file also shouldn't grow into a Playwright textbook. [Currents' Playwright best practices skill](https://docs.currents.dev/ai/agent-skills/agent-skill-playwright-best-practices) teaches agents locators, web-first assertions, debugging flaky tests and CI setup, and Claude Code, Cursor, VS Code, Gemini CLI and other agents that read skills load it on their own when a task involves Playwright.

Let the skill carry general Playwright practice, and keep AGENTS.md for what only your suite knows: its CI command, its reference spec, its failures and the checks that catch them. A line that names none of those belongs in the skill or nowhere.

## Frequently asked questions


### What should an AGENTS.md file contain for a Playwright project?

Give the agent the exact CI command, plus a map of where things go, with --project pinned to the project CI runs and --workers pinned to the worker count CI uses. By default, plain `npx playwright test` runs every project and sets workers to 50% of the machine's logical CPU cores, and Playwright recommends one worker in CI, for stability and reproducibility. [Project map and commands](#project-map-and-commands) shows the full block.


### What is the difference between AGENTS.md and Playwright Test Agents?

AGENTS.md holds instructions for the AI agents that work on a repository, and you can keep one or more of them anywhere in the repo. Playwright's Test Agents are a separate feature: Playwright ships three of them out of the box, the planner, the generator and the healer. [AGENTS.md vs Test Agents](#agentsmd-vs-test-agents) explains why mixing them up puts your rules in the wrong file.


### When is an AI-written Playwright test done?

A spec is done when it passes the done commands with `--repeat-each=10 --retries=0 --fully-parallel --shuffle`, then with `--shard=1/2` and `--shard=2/2`. Any failure means the spec is not done. [A definition of done](#a-definition-of-done) has the agent paste each command and its result into the PR.


### Should the agent's done check use fail-on-flaky-tests?

No, leave `--fail-on-flaky-tests` out of the done commands. Playwright flags a test as flaky when it failed on the first run but passed when retried, so with `--retries=0` no test is ever flagged. As [A definition of done](#a-definition-of-done) says, the flag belongs in the CI config, where retries are on.


### How do I stop an AI agent from using waitForTimeout in Playwright tests?

Ban `page.waitForTimeout` in AGENTS.md, and have the PR job fail on any `waitForTimeout` under `tests/`. [Enforcing rules in CI](#enforcing-rules-in-ci) shows the grep step in a GitHub Actions job.


### What should an AI agent do when a Playwright test fails?

The agent should name the root cause before it edits anything: a selector that no longer matches, a timing guess, a test-data collision, or a real change in the app. When the app's behavior changed, the agent leaves the test failing and reports a possible regression, as [Rules for fixing failures](#rules-for-fixing-failures) explains.


### Can I add my own rules to the Playwright Test Agent definitions?

No, keep project rules in AGENTS.md, and treat the definitions as generated output you refresh on every upgrade. The docs say to regenerate the definitions whenever Playwright is updated, so a rule added to them is a rule you have to remember to put back, as [the anti-pattern box](#anti-pattern-rules-in-agent-definitions) explains.




---

## About the author

**Walter Galvão**, Software Engineer. Walter Galvão is a software engineer at Currents. He writes about Playwright in practice: debugging flaky tests, structuring suites for CI, and the engineering workflows that keep large test suites reliable.

LinkedIn: https://www.linkedin.com/in/waltergalvao/
