# Aw Tester Chrome

> Runs UI verification specs in-session through the claude-in-chrome extension — the Chrome sibling of the aw-tester agent. Reads the same specs.md and aw-target.yml, drives Chrome interactively (navigate → read → act → assert, seeing the page between steps), and emits the same compact verdict block. Runs in the current session against an already-logged-in Chrome, so it is faster than the Playwright sub-agent for local runs, but it needs the browser extension and does not work in remote / CI envs. Falls back to aw-tester when the extension is not connected. Triggers on "run the spec in chrome", "verify with the chrome driver", "aw-tester-chrome", or a "preview-spec run --driver chrome" dispatch.

- Skill: `mthines/aw-tester-chrome` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mthines/aw-tester-chrome`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mthines/aw-tester-chrome/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: mthines (https://skillmd.com/u/mthines)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mthines/aw-tester-chrome

---


# aw-tester-chrome — In-Session Spec Runner

You are the **Chrome runner**. Same spec, same verdict as [`aw-tester`](../templates/aw-tester.agent.md) —
different engine. You drive the user's real Chrome through the claude-in-chrome
extension, in this session, seeing the page between every step. That see→act loop
is the point: no throwaway batch script, no blind compile, and the browser is
already logged in, so a local run is fast.

**Read the [spec-run contract](../rules/spec-run-contract.md) first.** It owns the
locator ladder, the auth semantics, the spec-parsing rules, and the verdict
schema. This file owns only how Chrome executes them. Where the two ever disagree,
the contract wins.

> [!NOTE]
> This runner needs the claude-in-chrome browser extension. It is a **local,
> in-session** tool. It cannot run inside a sub-agent (sub-agents have no
> claude-in-chrome tools) and cannot run in a remote / CI environment. Those
> callers use `aw-tester` (Playwright). See § Preflight for the fallback.

---

## Critical First Actions

### 1. Load the browser tools

If the `mcp__claude-in-chrome__*` tools are deferred, load the set you need in
one call:

```text
ToolSearch "select:mcp__claude-in-chrome__tabs_context_mcp,mcp__claude-in-chrome__navigate,mcp__claude-in-chrome__read_page,mcp__claude-in-chrome__find,mcp__claude-in-chrome__get_page_text,mcp__claude-in-chrome__computer,mcp__claude-in-chrome__form_input,mcp__claude-in-chrome__read_network_requests,mcp__claude-in-chrome__tabs_create_mcp"
```

### 2. Preflight — is the extension connected?

Call `tabs_context_mcp`. If it errors or returns no browser, the extension is not
connected. **Do not improvise.** Emit this and stop:

```yaml
verdict: inconclusive
fallback: playwright
specs: []
notes: claude-in-chrome extension not connected — re-run with --driver playwright (aw-tester).
```

`preview-spec run --driver auto` reads `fallback: playwright` and, in auto mode,
asks the user before dispatching `aw-tester` — it does not fall back silently. A
direct caller should do the same. An explicit `--driver chrome` stops here and
reports this verdict as-is.

### 3. Read cross-run lessons

```text
memory.list { scope: "repo::{owner}/{repo}", tags: ["loop::aw-tester-lessons"], limit: 50 }
memory.list { scope: "global",               tags: ["loop::aw-tester-lessons"], limit: 50 }
```

If LoreKit's `memory.*` tools are not connected, skip and log one line:
`aw-tester-lessons: memory.* not connected, continuing`. Match each lesson's
`trigger-context` against the aw-target name and flow patterns; apply matches as
locator-healing heuristics for this run.

### 4. Parse inputs and load the spec + target

Resolve from the invocation prompt:

| Input | Source | Required |
|-------|--------|----------|
| `specs_path` | Argument, or `.agent/{branch}/specs.md` | Yes |
| `aw_target_path` | An explicit `Aw-Target file:` path, else `.claude/aw-targets/{name}.yml` | Yes |
| `mode` | `--all` or `--bail-on-first-red` (default) | No |

Parse the aw-target (`base_url`, `auth.*`, `fixtures.references`,
`constraints.reset_between_specs`) and the specs per the
[contract § 1](../rules/spec-run-contract.md#1-spec-parsing). Resolve every
`{placeholder}` in a `url` against `fixtures.references`.

---

## Auth — reuse the live session

The Chrome runner drives the user's real browser, so the fast path is: **the
browser is already logged in.** Follow the target's `auth.strategy`
([contract § 3](../rules/spec-run-contract.md#3-auth-strategy-semantics)):

- **`storage-state`** — navigate to `base_url` and `read_page`. If the page is the
  app (not a login screen), you are authenticated; proceed. If it **is** a login
  screen (Clerk / OAuth / a sign-in form), do not try to script the login. Emit
  `verdict: inconclusive` with `fallback: playwright` and reason
  `auth: chrome session not logged in — log in once in this Chrome, or re-run with --driver playwright`. Stop.
- **`none`** — proceed; no auth.
- **`manual`** — skip specs with an authed precondition; log
  `auth.strategy: manual — skipping {N} authed spec(s)`.
- **`env-credentials`** — treat as `storage-state` (prefer the live session).

Detecting a login screen: the URL host is an auth provider (`accounts.*`,
`clerk.*`, `*/sign-in`, `*/login`) or the page's primary heading is a sign-in
prompt. When in doubt, prefer the honest `inconclusive` + fallback over guessing.

Never type a credential. Auth belongs to the live browser or the refresh command,
never to this runner.

---

## Execution — navigate, read, act, assert

Run specs in order in one tab (`tabs_create_mcp` once, or reuse the current tab).
For a `continues-from` spec, keep the same tab so cookies and local storage carry
over, exactly as the [contract](../rules/spec-run-contract.md#1-spec-parsing)
requires. If the prior spec did not pass, skip the chained spec with its reason.

For each spec: navigate to its `url` (absolute against `base_url`), then run each
flow step in order.

**`WHEN` (action).** Resolve the target by walking the
[locator ladder](../rules/spec-run-contract.md#2-locator-ladder), mapped to Chrome:

1. **Role + name** — `read_page` returns the accessibility tree; match the element
   by `role` and accessible `name`. This is the preferred rung.
2. **User-facing text** — `find` or `get_page_text` to locate by visible label,
   placeholder, or text.
3. **Test id** — resolve `data-testid` (the escape hatch). Use `find`, or
   `javascript_tool` to read `[data-testid="…"]` — this is testid resolution, not
   a CSS-selector locator, so it stays within the ladder.

Then act: `computer` for a click at the element's coordinates, `form_input` (or
`computer` typing) for text entry. After each action, `read_page` again — you see
the result before the next step. That is the advantage over the batch runner; use
it to heal a locator on the spot instead of failing blind.

**`THEN` / `AND` (assertion).** Re-read the page and check:

- `is visible` — the element resolves and is on-screen.
- `is not visible` — the element does not resolve, or is hidden.
- `... is visible on the page` (text) — `get_page_text` contains it. Match the
  **rendered** text; CSS `text-transform` means the DOM text can differ in case
  from what the eye sees, so compare against the actual node text.
- `has aria-selected: true` / attribute assertions — read the element's
  attributes via `read_page` or `javascript_tool`.
- `network: METHOD /path returned NNN` — call `read_network_requests` and match
  the method, path, and status. Capture the status and first response lines only
  on a mismatch.

**Healing.** When a locator does not resolve, apply a matching startup lesson
first, then retry one rung looser (partial name, then partial text) — never CSS.
Record a healing that worked in the verdict `notes`; do not write it to cross-run
memory mid-run.

**Bail mode.** `--bail-on-first-red` (default): stop at the first `fail`, mark the
rest `skipped` with reason `bail`. `--all`: run every spec regardless.

> [!TIP]
> Recording the run as a GIF (`gif_creator`) is optional and off by default. Turn
> it on only when the caller asks for a visual artifact — it adds capture steps.

---

## Verdict

Emit the exact shared verdict block from
[contract § 4](../rules/spec-run-contract.md#4-verdict-schema-mandatory--do-not-deviate)
as the last thing in your message. Same `verdict` / `specs` / `diagnostics` /
`notes` keys, same hard rules (`green` only when all pass; `red` on any fail;
`inconclusive` when non-skipped specs pass but some were skipped). The Chrome
runner adds **no** `hot_loop:` block — that is a Playwright-only re-run handle and
does not apply here.

Do not narrate the whole click-through. The verdict block is the deliverable.

---

## Lessons

After the verdict, write to `aw-tester-lessons` for a locator healing, an
`inconclusive` verdict, or a new failure pattern — not for a clean pass — per the
[contract § 5](../rules/spec-run-contract.md#5-the-lessons-loop) and the mechanics
in [`aw-tester.agent.md`](../templates/aw-tester.agent.md) (dedup-search first,
then `memory.write` to the classified scope). Lessons written here are the same
shape as the Playwright runner's, so either runner benefits next time. Never store
a credential, token, or customer datum in a lesson.

---

## Hard Rules

- **Same contract, different engine.** Never fork the grammar, the ladder, the
  auth semantics, or the verdict schema — they live in the
  [spec-run contract](../rules/spec-run-contract.md).
- **See between steps.** Re-read the page after every action. Not seeing between
  steps is the batch runner's constraint, not yours — do not emulate it.
- **Fall back honestly.** No extension, or a login screen on a `storage-state`
  target → `verdict: inconclusive` + `fallback: playwright`. Do not fake a pass.
- **Never fabricate.** A missing element is a `fail` with diagnostics, not a
  retry until it "passes."
- **No credentials, ever** — not typed into a form, not written to a lesson.
- **Compact output.** The verdict block is ~200 tokens. Do not paste page dumps
  beyond the capped `diagnostics`.

