# QA

> Use when testing UI changes for visual correctness, interactions, console errors, and edge cases. Performs browser-based QA and returns a detailed issue list for planning fixes.

- Skill: `jackneil/qa-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add jackneil/qa-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jackneil/qa-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: jackneil (https://skillmd.com/u/jackneil)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jackneil/qa-2

---


First, check if a repo-scoped version exists in the current project:
1. If `.claude/skills/qa/SKILL.md` exists (Glob) → read and follow it instead of this file.
2. If `.claude/commands/qa.md` exists (Glob) → read and follow it instead (legacy `/jacked-setup` output).
Otherwise follow the engine below.

This skill is the quick, focused QA pass (single agent) — the engine below. Its thorough sibling is `/ux` (`~/.claude/skills/ux/SKILL.md`): a parallel multi-agent review testing 6 UX aspects across multiple pages. Decision guide:
- Changed button styling or a single component? → follow the engine below (`/qa`)
- Changed layout, interactions, AND multiple pages? → switch to `/ux`
- Want faster repeat QA runs? → `/jacked-setup qa` pre-configures browser tool and framework checks

Both `/qa` and `/ux` are **read-only detection tools** — they return a detailed issue list but do NOT fix code (the one exception is an explicit opt-in follow-on `/qa` can offer at the end: turning a verified journey into a regression test, only with your approval). After receiving findings, use `superpowers:writing-plans` to build a fix plan from the issues. **Write the plan as HTML, not Markdown** (jacked's preferred format for human-consumption artifacts — see `~/.claude/jacked-reference.md` § Artifact Format Preference). Copy `~/.claude/jacked-templates/plan-template.html` and save to `docs/superpowers/plans/{YYYY-MM-DD}-qa-fixes.html`. Tell the sub-skill explicitly: "Output as HTML using the jacked template — not Markdown." Let the user iterate, then execute with `/dcr` verification.

<!-- ENGINE -->

> **Tip:** MCP-based browser tools (Playwright MCP, Claude-in-Chrome) require no bash approval and work instantly. If using `agent-browser`, pre-approve it once by adding `Bash(npx agent-browser:*)` to your permission allowlist.

You are a QA engineer testing UI changes from the current coding session. Follow these steps systematically.

## Config Override

If this command was invoked via a local config wrapper (you see a `## Repo Config` section earlier in the prompt), use that config to skip detection:
- **Browser Tool** specified? → Skip Step 1, use the declared tool directly (fall back to detection if it's unavailable)
- **Stack** declared? → Skip tech stack inference in Step 2
- **Dev Server Port** specified? → Use it in Step 4 URL detection (still check `lsof` as fallback)
- **`## Dev Servers`** table present (multi-server / monorepo repo)? → In Step 4, bring up ALL listed servers (a frontend pointed at a dead backend fails silently) before testing — this supersedes a single `Dev Server Port`
- **Component Paths** may be per-app (e.g. ``apps/desktop/src/...` (desktop)``) when the repo has `apps/*/` — treat each app's paths as scoping hints for the app that owns the changed file
- **Credential Hints** listed? → Use those variable names in Step 5 credential search
- **Framework-Specific Checks** listed? → Add them to Step 6 QA pass (in addition to standard checks)

If the config overlay date is more than 90 days old, mention: "Your `/qa` config is over 90 days old — consider running `/jacked-setup qa` to refresh it."

If no `## Repo Config` section is present, run all detection steps normally.

## Step 1: Detect Browser Tools

Check which browser automation tools are available. Prefer MCP tools first — they require no bash permissions or approval prompts.

**Option A — Chrome DevTools MCP (preferred)**: Try calling `mcp__chrome-devtools__list_pages`. If it works, use Chrome DevTools MCP tools for all browser interaction:
- `mcp__chrome-devtools__navigate_page` → open pages
- `mcp__chrome-devtools__take_snapshot` → accessibility tree (preferred for element detection)
- `mcp__chrome-devtools__take_screenshot` → visual screenshot
- `mcp__chrome-devtools__click` → click element by ref from snapshot
- `mcp__chrome-devtools__fill` → fill input fields
- `mcp__chrome-devtools__evaluate_script` → run JavaScript on page
- `mcp__chrome-devtools__emulate` → change viewport size (mobile/tablet testing)
- `mcp__chrome-devtools__list_console_messages` → check for JS errors
- `mcp__chrome-devtools__list_network_requests` → check for failed requests

**If Chrome DevTools MCP fails** (tool call errors, connection refused, or no pages returned): Tell the user:
```
Chrome DevTools MCP is not responding. To fix:

1. Chrome version: You need Chrome 144 or newer.
   Check yours at chrome://version — update if needed.

2. Enable remote debugging (pick one):
   a) In Chrome: go to chrome://inspect/#remote-debugging and enable it
   b) Or launch Chrome with: --remote-debugging-port=9222
      macOS:  /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
      Linux:  google-chrome --remote-debugging-port=9222

3. If not installed: run `jacked install` (includes Chrome DevTools MCP setup)
   or manually: claude mcp add -s user chrome-devtools -- npx chrome-devtools-mcp@latest --autoConnect
```
Then continue to Option B as fallback.

**Option B — Playwright MCP**: Try using `mcp__plugin_playwright_playwright__browser_snapshot`. If it works, use Playwright tools for all browser interaction. Note to user:
> Using Playwright MCP (Chrome DevTools MCP is preferred — see above). Playwright opens separate browser windows.

**Option C — Claude-in-Chrome**: Try using `mcp__claude-in-chrome__tabs_context_mcp`. If it works, use Claude-in-Chrome tools for all browser interaction.

**Option D — agent-browser CLI**: Run `npx agent-browser --version` via Bash. If it succeeds, use agent-browser for all browser interaction via Bash tool calls (e.g., `npx agent-browser open <url>`, `npx agent-browser snapshot`, `npx agent-browser screenshot <path>`, `npx agent-browser click <ref>`, `npx agent-browser type <ref> <text>`, `npx agent-browser eval <js>`). This reuses your existing browser session — no new windows.
> Note: `npx` requires an approval prompt unless pre-approved. Add `Bash(npx agent-browser:*)` to your permission allowlist to avoid repeated prompts.

**If none are available**: Tell the user:
```
No browser tools detected. Recommended setup:

  jacked install    (configures Chrome DevTools MCP automatically)

Or install manually:
  claude mcp add -s user chrome-devtools -- npx chrome-devtools-mcp@latest --autoConnect
  (requires Chrome 144+ with remote debugging enabled — see chrome://inspect/#remote-debugging)

Alternatives:
- Playwright MCP: Add to .mcp.json with --headless flag
- Claude-in-Chrome: Install the Chrome extension from https://chromewebstore.google.com
- agent-browser: npm i -g agent-browser (requires npx pre-approval)
```
Then stop.

## Step 2: Identify What Changed

Run `git diff --name-only HEAD` to see what files changed. Filter for UI-relevant files:
- `.js`, `.jsx`, `.ts`, `.tsx`, `.css`, `.scss`, `.less`, `.html`
- `.vue`, `.svelte`, `.erb`, `.jinja`, `.jinja2`

Ignore files in `node_modules/`, `dist/`, `build/`, `__pycache__/`, and test files (`*.test.*`, `*.spec.*`).

Summarize what UI areas were likely affected (e.g., "Login form styling", "Dashboard data table", "Navigation component").

If no UI files changed, tell the user and ask if they still want to proceed.

## Step 3: Check for Cross-Page Impact

After identifying changed files, check if any are shared infrastructure — global CSS, router/navigation, state management, API client, layout components, shared utilities, or WebSocket/event bus files. Signals: files in paths like `shared/`, `common/`, `utils/`, `lib/`, `helpers/`, `layouts/`, `hooks/`, `services/`, `core/` or named `app.*`, `main.*`, `router.*`, `store.*`, `state.*`, `theme.*`, `websocket.*`. Do NOT flag `index.*` (module re-exports), `*.stories.*`, `*.module.css`, or `*.d.ts`.

If shared files changed: note "Shared infrastructure changed — will spot-check additional pages after primary QA pass." Use file paths and conversation context to identify which other pages the shared code likely affects. If the change is truly global (e.g., global CSS, router), check 2-3 representative pages from different areas of the app.

After the main QA pass on the primary page (Run QA Pass step), perform a concrete spot-check on each flagged page:
1. Navigate to the page
2. Take one snapshot (accessibility tree)
3. Check for: page loads without error, no blank screens, no visibly broken layout, no console errors
4. Move on — anything deeper is `/ux` territory

If no shared files changed, skip the spot-check entirely.

## Step 4: Determine App URL

**Isolate → PROVE it → only THEN test hard. Fail closed.** This pass clicks, fills forms, and
may submit/save/delete. **First get an isolated copy** (best available): (1) a **PR / preview /
ephemeral deploy** — check `gh pr checks` / the PR's "View deployment" links; (2) **spin it up
locally** — dev server + a local DB with seed/fixture data (`docker compose up`, `manage.py
runserver`, `npm run dev`/`pnpm dev`, a `seed`/`migrate` command, `.env.local`); (3) a disposable
staging. **Then, BEFORE the first write, you are READ-ONLY until you affirmatively confirm ALL
of:** (a) **host** is `localhost`/`127.0.0.1`/the EXACT preview URL — never the prod domain; (b)
**DB** — the running PROCESS is on a local/throwaway DB, read from the live process (`ps eww
<pid>`, `/proc/<pid>/environ`) or an app endpoint, NOT a dotfile (a preview/remote URL alone does
NOT prove the DB — if you can't read its env, stay read-only); (c) **outbound side-effects** —
email/payment/webhook/third-party integrations are sandboxed or disabled (a local DB won't stop a
real charge or email blast); (d) **you started it** — a server you merely found listening isn't
proof. ANY doubt → it's production, stay read-only. **Only once ALL pass: go after the edge and
destructive paths freely** — that's the payoff of isolating. Forced onto production? Stay
READ-ONLY and tell the user the interactive checks need an isolated instance. **This gate governs
EVERY write in this command** — login, form submits, create/edit/delete, and any
replay/iterate-until-green re-run; you are READ-ONLY at each such step until ALL of (a)–(d) pass.

**If `$ARGUMENTS` contains a URL**: treat it as the target only — it is STILL subject to the full
(a)–(d) gate above before any mutating interaction (a `localhost` argument clears only check (a),
never the process-DB / outbound / you-started checks).

**Otherwise**, detect the dev server(s) in this order:
1. **`## Dev Servers` table declared** (multi-server / monorepo)? → ensure EVERY listed server is up before testing — a frontend whose backend API is dead is the #1 real "looks broken" failure. Start any that aren't listening (honoring each app's "server may be user-managed, don't kill it" note), and respect the (a)–(d) isolation gate for any server you start. In a monorepo, map each changed file to the app that owns it (nearest `apps/*/` boundary) so you test on the right server.
2. **Single `Dev Server Port` set** (config or context)? → use it.
3. Check conversation context for recently mentioned URLs (e.g., `http://localhost:3000`)
4. Run `lsof -i -P -sTCP:LISTEN | grep -E ':(1420|1421|3000|3001|4000|4173|4200|4321|5000|5173|5174|6006|8000|8001|8080|8765|8888) '` to find common dev server ports

If a server is found, use it. If multiple are found, ask the user which one. If none found, ask the user for the URL.

## Step 5: Check for Login Credentials

If the app requires authentication to access the areas being tested, search for credentials in `.env` files before asking the user.

**Find the repo root**:
```bash
git rev-parse --show-toplevel 2>/dev/null || pwd
```

**Scan env files** in priority order — run each grep separately (all are auto-approved, stop at first file with results):
```bash
grep -iE "^[A-Z_]*(EMAIL|PASSWORD|USERNAME|LOGIN)[A-Z_]*=" .env.local
grep -iE "^[A-Z_]*(EMAIL|PASSWORD|USERNAME|LOGIN)[A-Z_]*=" .env.development
grep -iE "^[A-Z_]*(EMAIL|PASSWORD|USERNAME|LOGIN)[A-Z_]*=" .env.test
grep -iE "^[A-Z_]*(EMAIL|PASSWORD|USERNAME|LOGIN)[A-Z_]*=" .env
```
Run from the repo root. **Skip any variable whose name starts with `DB_`, `DATABASE_`, `POSTGRES_`, `REDIS_`, `MONGO_`, `S3_`, or `AWS_`** — those are infrastructure credentials, not app login credentials.

**Announce what was found** (variable names only, never values):
- ✓ Found: `TEST_USER_EMAIL` + `TEST_USER_PASSWORD` in `.env.local` — "Using these for login."
- ✗ Not found: "No login credentials found in env files." → Ask the user for credentials.

**If login with found credentials fails:** Warn the user ("Credentials from `.env.local` were rejected") and ask for correct credentials. Do not retry silently.

**Security note:** If credentials were found in `.env.local`, `.env.development`, or `.env.test` in a repo you just cloned, verify this is an expected dev credentials file before using it.

## Step 6: Run QA Pass

**Screenshot setup** (agent-browser and Playwright only — Chrome does not support file-based screenshots):
```bash
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
rm -rf "$REPO_ROOT/tmp/qa_screenshots"
mkdir -p "$REPO_ROOT/tmp/qa_screenshots"
```
Save all screenshots to `$REPO_ROOT/tmp/qa_screenshots/<descriptive-name>.png`.
*Add `tmp/` to your project's `.gitignore` if it isn't already there.*

### State Hygiene

Start each UI area from a known state so cross-test contamination doesn't manufacture false bugs (a half-filled form, a leftover session, a record mutated by an earlier check):
- Begin from a fresh navigation, or — where the tool supports it — an isolated/incognito context (Chrome DevTools MCP: `mcp__chrome-devtools__new_page` with `isolatedContext` partitions cookies/storage; Playwright: a fresh browser context).
- Don't mutate real or shared records when a throwaway path exists — use a seeded/test entity, not production data.
- After the pass, restore state: log out, clear any half-filled form, and close or delete entities you created.

> **Warning — live session:** With Chrome DevTools MCP `--autoConnect` you are driving the user's REAL signed-in Chrome, not a sandbox. Do not leave it mid-flow, on a destructive confirmation, or logged into something it wasn't before — leave it where you found it.

Navigate to the app URL. For each UI area affected by the changes:

### Visual Check
- Take a snapshot (accessibility tree preferred over screenshot for actionability)
- Look for broken layouts, missing elements, overlapping content
- Check that text is readable and properly aligned
- Verify colors, spacing, and visual hierarchy look correct

### Interactive Testing
- Click buttons and links — do they respond correctly?
- Fill out forms — do inputs accept text, show validation?
- Test navigation — do page transitions work?
- Check dropdowns, modals, toggles, and other interactive elements

### Console Errors
- Check the browser console for JavaScript errors
- Per-tool calls: Chrome DevTools MCP `mcp__chrome-devtools__list_console_messages` (drill into one with `mcp__chrome-devtools__get_console_message`); Playwright `mcp__plugin_playwright_playwright__browser_console_messages`; Claude-in-Chrome `mcp__claude-in-chrome__read_console_messages`
- Flag any errors, especially new ones related to the changed code

### Network Errors
A failed API call is a leading cause of "the UI looks broken" and is invisible to a visual + console pass — check it explicitly. After exercising each UI area, list the network requests and flag anything tied to the changed code that didn't succeed:
- Non-2xx/3xx responses (4xx/5xx), failed fetches, CORS errors, and pending/hung requests that never resolve
- Per-tool calls: Chrome DevTools MCP `mcp__chrome-devtools__list_network_requests` (drill into one with `mcp__chrome-devtools__get_network_request`); Playwright `mcp__plugin_playwright_playwright__browser_network_requests`; agent-browser `npx agent-browser eval "performance.getEntriesByType('resource')"` (or its network-log command if available). Note: `PerformanceResourceTiming` exposes no HTTP status code, so the agent-browser `eval` path cannot read response status — use its dedicated network-log command if available, otherwise capture the request URL/duration and infer failures from `transferSize === 0`/hung timing rather than a status code.
- For each failure, record the **URL**, the **status code** (when the tool exposes it — the agent-browser `eval` path above does not, so omit it there), and whether it **correlates with a visible UI defect** (empty state, a spinner that never resolves, an error toast, missing data). Surface it even when the page otherwise renders.

### Edge Cases
- Empty states: What happens with no data?
- Long text: Does overflow handling work?
- Special characters: Do inputs handle `<script>`, quotes, unicode?
- Rapid interactions: Double-clicking, fast navigation

### Responsive (if applicable)
- Resize the browser to mobile width (375px) and check layout
- Resize to tablet width (768px) and check layout

### Accessibility Lens (if available)

Check if an accessibility specialist lens is installed:

```bash
ls ~/.claude/lenses/accessibility.md .claude/lenses/accessibility.md 2>/dev/null | head -1
```

If found, read it and incorporate its "What to check" items into your testing checklist. These are **additive** — they don't replace your existing QA checks. Focus on items that can be verified visually or via browser DevTools:

- Color contrast (use DevTools accessibility panel or Lighthouse)
- Keyboard navigation (tab through the page, verify focus indicators)
- Semantic HTML (inspect elements — buttons should be `<button>`, not `<div>`)
- Form labels (each input has a visible, associated `<label>`)
- Focus management after interactions (modal open/close, route changes)

Skip items that require specialized tooling (screen reader testing, automated WCAG scanners) unless the user specifically requests them.

### Performance & Lighthouse (Chrome DevTools MCP only, opt-in)

Run this **only** when Chrome DevTools MCP is the active tool AND the change plausibly affects performance or a11y — new/large images or fonts, bigger bundles, render-blocking resources, a new heavy component, or markup/contrast changes. Skip it for quick single-component checks so they stay fast.

- **Lighthouse:** run `mcp__chrome-devtools__lighthouse_audit` (mode=`navigation`, device matching the viewport you're testing) on the **primary changed page** for **accessibility / SEO / best-practices**. Note: Lighthouse here **EXCLUDES performance** — use the trace below for Core Web Vitals.
- **Performance trace (optional):** navigate to the target URL first, then `mcp__chrome-devtools__performance_start_trace` — by default it reloads the page and auto-stops once the page settles, so no manual reload or separate `performance_stop_trace` call is needed (pass `reload=false`/`autoStop=false` only if you want to drive an interaction yourself, then call `mcp__chrome-devtools__performance_stop_trace`). Then `mcp__chrome-devtools__performance_analyze_insight` to read **LCP / INP / CLS** and render-blocking resources.
- **Keep it token-cheap:** direct the heavy report to disk — set the tool's `outputDirPath` to `$REPO_ROOT/tmp/qa_screenshots/` (run `mkdir -p "$REPO_ROOT/tmp/qa_screenshots"` first if you skipped the screenshot setup), or write to a tmp path. Pull only the scores + top failing audits into the QA report; **never inline the raw JSON**.

## Step 7: Report Findings

Present a structured report:

```
## QA Report

### Summary
- [Area 1]: PASS / FAIL
- [Area 2]: PASS / FAIL

### Issues Found
1. **[Severity: HIGH/MEDIUM/LOW]** Description
   - Steps to reproduce
   - Expected behavior
   - Actual behavior
   - [Screenshot if available]

### Console Errors
- [List any JS errors found, or "None"]

### Network Errors
- [List any failed/4xx/5xx/CORS/hung requests — URL + status + linked UI defect, or "None"]

### Suggestions
- [Optional improvements noticed during testing]
```

If everything passes, say so clearly. If issues are found, prioritize them by severity.

## Step 8: (Optional) Convert a Verified Journey to a Regression Test

**Strictly opt-in.** The QA pass itself stays read-only (detect + report) — this only runs if the user explicitly asks for it after seeing the report. Offer it once:

> Want me to lock this in? I can turn a verified PASS journey (or a reproduced bug) into a runnable regression test so it can't silently come back.

If the user says yes:
1. **Pick the journey** — a clean PASS path worth protecting, or the exact reproduction steps for a confirmed bug.
2. **Replay** the steps through the active browser tool to confirm they're accurate.
3. **Emit a spec** using the repo's existing e2e framework (`@playwright/test` if none is established), written into the repo's existing tests directory — match the current layout and naming (e.g. `e2e/`, `tests/e2e/`, `playwright/`).
4. **Run it and iterate until green.** For a bug, write the test to fail against current behavior first, so it goes green only once the fix lands.

If the user declines, skip straight to Cleanup.

## Step 9: Cleanup

Remove the screenshot directory after presenting the report:
```bash
rm -rf "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/tmp/qa_screenshots"
```

This command is **read-only** — the QA pass detects and reports issues but does NOT fix them. The single exception is Step 8, which writes a regression test only on explicit user approval (an opt-in follow-on, never part of the detection pass). The detailed issue list is returned to the parent caller (Claude Code), which should then use `superpowers:writing-plans` to build a fix plan from the findings, let the user iterate on it, and execute with `/dcr` verification.

> **Tip:** Run `/jacked-setup qa` to generate a repo-specific config that skips browser detection, bakes in your tech stack, and adds framework-specific QA checks.

