test-exploratory — naive-user discovery, two identities
Degree of freedom: MIXED — wander and charters [HIGH freedom]; driver,
sessions, and triage [LOW freedom — run exactly]. Driver is
playwright-cli, never Playwright MCP. Three isolated sessions — never
share storage.
You drive the live non-prod app like a slightly careless user. The job is
discovery, not verification: find the bugs nobody scripted. The sharpest
bugs live in the guest vs logged-in diff. Report only — do not fix in this
pass. Pair with protocol-browser-anti-stall. Hand real bugs to
workflow-feedback-to-closure. Lock a fix later with test-playwright.
This is not test-qa (story/CRUD), not test-red-team (hostile
matrix), not test-playwright (this-diff PDCA + fix-as-you-go).
This skill vs neighbors
| Skill |
Owns |
| test-exploratory (this) |
Unscripted wander + junk/nav-abuse + guest vs authed diff table |
test-qa |
Story-driven CRUD / smoke — explicitly not a monkey test |
test-red-team |
Adversarial feature×dimension matrix (security/data/perf) |
test-playwright |
Session diff + blast radius; fixes inline |
audit-auth-flows |
Static route×gate matrix (code). This skill is the live probe |
audit-ux-journeys |
Story completability / IA — not junk submit + double-click |
audit-accessibility |
WCAG / axe only |
How to reason (before a finding)
- Observe — captured console/network event + identity + snapshot ref
- Interpret — guest-in-protected, guest-only throw, leakage, or expected deny?
- Classify — Real bug / UX defect / Flaky / Out of scope (Phase 3 table)
- Severity — blast radius: auth/money first
Worked example
Observe: guest session goto /settings/billing → 200 + "Update card".
Authed same route works. Console clean.
Interpret: protected billing UI reachable without a session.
Classify: Real bug — guest-in-protected (live complement to audit-auth-flows).
Severity: critical.
Finding: guest | billing | guest-in-protected | critical | repro: -s=explore-guest goto /settings/billing
Phase 0 — Safety, driver, isolation [LOW freedom — sessions exact]
Non-prod only. You will submit forms and mash destructive controls. If
only prod exists: read-only navigation and say so.
Driver is playwright-cli, never Playwright MCP:
PW="npx --yes @playwright/cli@latest"
$PW -s=explore-guest open --headed http://localhost:3000
Read protocol-browser-anti-stall first (Rule 0: headed, one action, no
*.spec.ts). Use three isolated sessions — never share storage:
| Session |
Storage |
Purpose |
-s=explore-guest |
default in-memory (no --profile) |
anonymous |
-s=explore-authed |
state-load or dedicated --profile under ~/.playwright-cli-profiles/ |
seeded test account |
-s=explore-post-logout |
fresh in-memory |
prove logout actually cleared state |
See protocol-browser-anti-stall/references/playwright-session-coordination.md.
Instrument before the first click. After open, capture console,
network/requests, and status ≥400. Judge findings against captured
events, not guesses.
Accessibility tree first. Click/fill by snapshot refs (e12). Refs
die on the next page change — re-snapshot after every navigation
(Playwright Agent CLI snapshots).
Use --depth or a scoped snapshot on dense pages. Screenshot is a
secondary modality for layout/grouping (web.dev 2026), not the driver.
Vision/coordinates only for canvas or unnamed icons
(vision mode).
SBTM charters — copy references/charter-template.md. Time-box each
identity (30–45 min). Stop when the box ends; record leftovers.
Phase 1 — Map, then wander (per identity) [HIGH freedom]
- Discover routes from links + router/sitemap. Build a per-identity
list (some routes exist only when logged in).
- Exercise the a11y tree on each page: every button/link/tab/menu/modal;
forms three ways (valid / empty-required / junk — huge strings, emoji,
<script>, SQL-ish, negatives, wrong types, whitespace); double-submit;
dismiss modals three ways (X, Escape, click-outside).
- Abuse navigation — back/forward after mutations, refresh mid-flow,
deep-link inner routes, Back after logout (on the post-logout session).
- One mobile-width pass on key pages. Layout breakage →
audit-responsive. Empty/error chrome → audit-ui-states. Do not re-audit.
- Short monkey burst on auth, checkout, and mutating forms only (confused
click sequences / races). Skip marketing pages.
Prioritize blast radius: auth, money, account mutation, then the rest.
Phase 2 — Guest vs logged-in diff (the deliverable) [HIGH freedom]
Run Phase 1 fully as GUEST, then fully as LOGGED-IN, then
POST-LOGOUT. Compare. File these in a dedicated table — nothing else in
the pack produces it:
- Guest reaches protected things — route/API/UI that should require auth
(live complement to
audit-auth-flows).
- Guest-only breakage — 500/404/throw for guests, works when authed
(null-user). Auth-looking UI that renders for guests then errors on click.
- Login/logout transitions — redirect loops, broken return-to, session
not cleared.
- State leakage — cookies,
localStorage, sessionStorage (Playwright
storageState does not persist sessionStorage — inspect it explicitly),
bfcache/Back showing private data after logout.
- Divergent errors — same action, different failure per identity.
Phase 3 — Triage (this IS the work) [LOW freedom — T4; do not skip]
Classify every captured event before it becomes a finding:
| Class |
Meaning |
Action |
| Real bug |
Reproducible: uncaught exception, 500, dead control, raw error, guest-in-protected, hang, data-loss on double-submit |
File with repro + identity + evidence |
| UX defect |
Works but wrong (silent fail, no loading/error) |
Route to audit-ui-states / audit-ux-journeys |
| Flaky |
Failed once |
Re-run once before filing. Do not file flakes |
| Out of scope |
WCAG, pixel polish, load |
Hand off; do not duplicate |
Severity by blast radius (auth/money first). Do not generate a Playwright
spec dump and call the app "covered" — that is shallow comprehensive coverage.
Self-critique (same table, before output): every row has identity +
evidence; flakes were re-run once; WCAG/pixel/load were handed off, not
filed here; guest never used --profile.
Definition of Done
Output format
- Run summary — target, charters, routes per identity, skips
- Findings table — id | identity (guest/authed/both) | area | type | severity | repro | evidence
- Guest-vs-logged-in diff — protected-reachable, guest-only breakage, transitions, leakage
- Console/network — recurring errors, ≥400, hangs
- Handoffs —
audit-responsive · audit-ui-states · audit-auth-flows · each bug → workflow-feedback-to-closure → later test-playwright
Present the report. Do not fix in this pass.
1---2name: test-exploratory3description: Exploratory ("monkey") QA of a live app as GUEST then LOGGED-IN, then diff. Headed playwright-cli, junk inputs, nav abuse. Use when "monkey test the app", "exploratory QA", "wander like a confused user", or "guest vs logged in". Story CRUD → test-qa. Hostile sweep → test-red-team. This-diff PDCA → test-playwright.4license: MIT5---67# test-exploratory — naive-user discovery, two identities89**Degree of freedom: MIXED** — wander and charters `[HIGH freedom]`; driver,10sessions, and triage `[LOW freedom — run exactly]`. Driver is11**playwright-cli**, never Playwright MCP. **Three isolated sessions** — never12share storage.1314You drive the **live non-prod app** like a slightly careless user. The job is15**discovery, not verification**: find the bugs nobody scripted. The sharpest16bugs live in the **guest vs logged-in diff**. Report only — do not fix in this17pass. Pair with `protocol-browser-anti-stall`. Hand real bugs to18`workflow-feedback-to-closure`. Lock a fix later with `test-playwright`.1920This is **not** `test-qa` (story/CRUD), **not** `test-red-team` (hostile21matrix), **not** `test-playwright` (this-diff PDCA + fix-as-you-go).2223## This skill vs neighbors2425| Skill | Owns |26|:------|:-----|27| **test-exploratory** (this) | Unscripted wander + junk/nav-abuse + **guest vs authed diff table** |28| `test-qa` | Story-driven CRUD / smoke — explicitly not a monkey test |29| `test-red-team` | Adversarial feature×dimension matrix (security/data/perf) |30| `test-playwright` | Session diff + blast radius; **fixes** inline |31| `audit-auth-flows` | Static route×gate matrix (code). This skill is the **live probe** |32| `audit-ux-journeys` | Story completability / IA — not junk submit + double-click |33| `audit-accessibility` | WCAG / axe only |3435## How to reason (before a finding)36371. **Observe** — captured console/network event + identity + snapshot ref382. **Interpret** — guest-in-protected, guest-only throw, leakage, or expected deny?393. **Classify** — Real bug / UX defect / Flaky / Out of scope (Phase 3 table)404. **Severity** — blast radius: auth/money first4142## Worked example4344> **Observe:** guest session `goto /settings/billing` → 200 + "Update card".45> Authed same route works. Console clean.46> **Interpret:** protected billing UI reachable without a session.47> **Classify:** Real bug — guest-in-protected (live complement to `audit-auth-flows`).48> **Severity:** critical.49> **Finding:** guest | billing | guest-in-protected | critical | repro: `-s=explore-guest` goto `/settings/billing`5051## Phase 0 — Safety, driver, isolation [LOW freedom — sessions exact]5253- **Non-prod only.** You will submit forms and mash destructive controls. If54 only prod exists: read-only navigation and say so.55- **Driver is playwright-cli**, never Playwright MCP:5657 ```bash58 PW="npx --yes @playwright/cli@latest"59 $PW -s=explore-guest open --headed http://localhost:300060 ```6162 Read `protocol-browser-anti-stall` first (Rule 0: headed, one action, no63 `*.spec.ts`). Use **three isolated sessions** — never share storage:6465 | Session | Storage | Purpose |66 |:--------|:--------|:--------|67 | `-s=explore-guest` | default in-memory (no `--profile`) | anonymous |68 | `-s=explore-authed` | `state-load` or dedicated `--profile` under `~/.playwright-cli-profiles/` | seeded test account |69 | `-s=explore-post-logout` | fresh in-memory | prove logout actually cleared state |7071 See `protocol-browser-anti-stall/references/playwright-session-coordination.md`.72- **Instrument before the first click.** After `open`, capture `console`,73 network/`requests`, and status ≥400. Judge findings against **captured74 events**, not guesses.75- **Accessibility tree first.** Click/fill by snapshot **refs** (`e12`). Refs76 die on the next page change — **re-snapshot after every navigation**77 ([Playwright Agent CLI snapshots](https://playwright.dev/agent-cli/snapshots)).78 Use `--depth` or a scoped snapshot on dense pages. Screenshot is a79 **secondary** modality for layout/grouping (web.dev 2026), not the driver.80 Vision/coordinates only for canvas or unnamed icons81 ([vision mode](https://playwright.dev/agent-cli/vision-mode)).82- **SBTM charters** — copy `references/charter-template.md`. Time-box each83 identity (30–45 min). Stop when the box ends; record leftovers.8485## Phase 1 — Map, then wander (per identity) [HIGH freedom]86871. **Discover routes** from links + router/sitemap. Build a **per-identity**88 list (some routes exist only when logged in).892. **Exercise the a11y tree** on each page: every button/link/tab/menu/modal;90 forms three ways (**valid / empty-required / junk** — huge strings, emoji,91 `<script>`, SQL-ish, negatives, wrong types, whitespace); **double-submit**;92 dismiss modals three ways (X, Escape, click-outside).933. **Abuse navigation** — back/forward after mutations, refresh mid-flow,94 deep-link inner routes, Back after logout (on the post-logout session).954. **One mobile-width pass** on key pages. Layout breakage →96 `audit-responsive`. Empty/error chrome → `audit-ui-states`. Do not re-audit.975. **Short monkey burst** on auth, checkout, and mutating forms only (confused98 click sequences / races). Skip marketing pages.99100Prioritize blast radius: auth, money, account mutation, then the rest.101102## Phase 2 — Guest vs logged-in diff (the deliverable) [HIGH freedom]103104Run Phase 1 fully as **GUEST**, then fully as **LOGGED-IN**, then105**POST-LOGOUT**. Compare. File these in a dedicated table — nothing else in106the pack produces it:107108- **Guest reaches protected things** — route/API/UI that should require auth109 (live complement to `audit-auth-flows`).110- **Guest-only breakage** — 500/404/throw for guests, works when authed111 (null-user). Auth-looking UI that renders for guests then errors on click.112- **Login/logout transitions** — redirect loops, broken return-to, session113 not cleared.114- **State leakage** — cookies, `localStorage`, **`sessionStorage`** (Playwright115 `storageState` does **not** persist sessionStorage — inspect it explicitly),116 bfcache/Back showing private data after logout.117- **Divergent errors** — same action, different failure per identity.118119## Phase 3 — Triage (this IS the work) [LOW freedom — T4; do not skip]120121Classify every captured event before it becomes a finding:122123| Class | Meaning | Action |124|:------|:--------|:-------|125| **Real bug** | Reproducible: uncaught exception, 500, dead control, raw error, guest-in-protected, hang, data-loss on double-submit | File with repro + identity + evidence |126| **UX defect** | Works but wrong (silent fail, no loading/error) | Route to `audit-ui-states` / `audit-ux-journeys` |127| **Flaky** | Failed once | **Re-run once** before filing. Do not file flakes |128| **Out of scope** | WCAG, pixel polish, load | Hand off; do not duplicate |129130Severity by blast radius (auth/money first). Do not generate a Playwright131spec dump and call the app "covered" — that is shallow comprehensive coverage.132133**Self-critique (same table, before output):** every row has identity +134evidence; flakes were re-run once; WCAG/pixel/load were handed off, not135filed here; guest never used `--profile`.136137## Definition of Done138139- [ ] Non-prod (or read-only + said so)140- [ ] Three isolated CLI sessions; guest never used `--profile`141- [ ] Listeners attached before interaction; traces/screenshots per identity142- [ ] Charters filled; leftovers listed143- [ ] Route lists per identity; high-blast-radius monkey pass done144- [ ] Guest + authed + post-logout runs145- [ ] Guest-vs-authed **diff table** produced146- [ ] Events triaged; flakes re-run; each real bug has repro + identity + evidence147- [ ] Honest skip list (MFA, paid flows, external redirects)148149## Output format1501511. **Run summary** — target, charters, routes per identity, skips1522. **Findings table** — id | identity (guest/authed/both) | area | type | severity | repro | evidence1533. **Guest-vs-logged-in diff** — protected-reachable, guest-only breakage, transitions, leakage1544. **Console/network** — recurring errors, ≥400, hangs1555. **Handoffs** — `audit-responsive` · `audit-ui-states` · `audit-auth-flows` · each bug → `workflow-feedback-to-closure` → later `test-playwright`156157Present the report. Do not fix in this pass.