Autonomous QA testing for web applications. Analyzes your source code, identifies user personas, tests through a real browser, tracks assertion coverage across runs, and crystallizes passing tests to Playwright for CI.
/qa
That's it. One command. Claude reads your code, finds vulnerabilities and bugs, figures out who your users are, simulates their journeys through your app, and produces a full report with actionable fixes — each issue in its own Jira-ready file.
flowchart TD
A["/qa"] --> B["Route Discovery"]
B --> C["Code Analysis\n(security + functional)"]
C --> P["Persona Identification\n(roles, guards, UI)"]
P --> D{"Crystallized\nPlaywright exists?"}
D -- "Yes" --> E["Run Playwright\n(fast CI path)"]
D -- "No" --> F["Chrome Extension\n+ dynamic scenarios\n+ persona journeys"]
E -- "Pass" --> BL["Baseline Diff\n(coverage drift)"]
E -- "Fail" --> G["Fallback to Chrome"]
F -- "Pass" --> H["Crystallize to\nPlaywright"]
H --> BL
G -- "Pass" --> I["Re-crystallize"]
G -- "Fail" --> BL
I --> BL
BL --> R["Report\n(enriched bugs + personas\n+ coverage trend\n+ Jira-ready issue files)"]
| Phase | What happens |
|---|---|
| 0. Baseline Load | Loads assertion baseline from previous runs to track coverage drift |
| 0b. Run Directory | Creates timestamped run folder (qa-reports/runs/2026-03-21_14-30-00/) to preserve history |
| 1. Route Discovery | Scans your Next.js project for all page and API routes |
| 2. Code Analysis + Personas | Static analysis for security/functional issues, identifies user persona types from code, generates 3 persona instances per type |
| 3. Browser Testing | Playwright-first for crystallized flows, Chrome extension for new/failed flows |
| 4. Persona Simulation | Executes persona journeys — methodical admins, impatient editors, confused visitors |
| 5. Crystallization | Converts passing Chrome tests to Playwright scripts with dynamic data-aware assertions |
| 6. Baseline Update | Diffs assertions against baseline, detects regression coverage drift |
| 7. Reporting | Generates enriched bug reports, persona insights, coverage trends, individual issue files |
| 8. Finalize | Updates run metadata, writes latest-run.json pointer |
/plugin marketplace add EvolvingAgentsLabs/qa
/plugin install qa-plugin@EvolvingAgentsLabs-qa
/reload-plugins
- Claude in Chrome extension (v1.0.36+)
- Google Chrome or Microsoft Edge
- A running dev server
- (Optional)
@playwright/testfor CI
# 1. Start your dev server
npm run dev
# 2. Launch Claude Code with Chrome
claude --chrome
# 3. Run QA
/qa/qa
Runs everything: code analysis, persona detection, all standard flows (navigation, forms, auth, responsive), all persona journeys, crystallization, and baseline tracking. Every run is preserved in a timestamped folder.
Example output:
QA Testing Complete
===================
Overall Status: FAIL
Pass Rate: 91.2%
Run #3 | Coverage Score: 100.0%
Code Analysis:
Security: 2 findings (1 high, 1 medium)
Functional: 3 findings (1 high, 2 low)
Dynamic E2E scenarios: 3 generated, 2 passed, 1 failed
Flows:
Navigation: PASS (8/8 steps) [crystallized]
Forms: FAIL (6/7 steps — email validation missing) [chrome]
Auth: PASS (5/5 steps) [crystallized]
Responsive: PASS (9/9 steps) [crystallized]
Personas (3 types, 9 instances):
admin: 2/3 passed — Jake Torres (impatient) failed on user creation form
editor: 3/3 passed
visitor: 3/3 passed
Key insight: Impatient personas expose missing client-side validation
Assertion Baseline:
Tracked assertions: 47 (+5 added, -0 dropped, ~0 changed)
Coverage score: 100.0% (no drift)
Crystallization:
Playwright scripts: qa-reports/playwright/ (4 flows)
Dynamic data: 3 assertions use pattern matchers (uuid, timestamp)
Run in CI: npx playwright test qa-reports/playwright/
Reports:
Run Directory: qa-reports/runs/2026-03-21_14-30-00/
Summary: qa-reports/runs/2026-03-21_14-30-00/summary.md
Issue Index: qa-reports/runs/2026-03-21_14-30-00/issues/INDEX.md
Issue Files: qa-reports/runs/2026-03-21_14-30-00/issues/ (8 files)
Baseline: qa-reports/baseline.json (persistent)
Critical Issues:
1. [SEC-001] XSS via dangerouslySetInnerHTML in CommentSection.tsx:42
2. [E2E-forms-003] Email field accepts invalid format on /contact
3. [PERSONA-admin-2-003] User creation submits without required fields
/qa navigation # Just page load tests
/qa forms # Just form tests
/qa auth # Just auth flow tests
/qa responsive # Just responsive layout tests
Example — testing only forms:
/qa forms
QA Testing Complete
===================
Overall Status: FAIL
Pass Rate: 85.7%
Flows:
Forms: FAIL (6/7 steps + 1 dynamic scenario)
Step 5 FAIL: Email input accepts "not-an-email" without validation
Dynamic SEC-001 PASS: XSS payload escaped correctly in comment field
Assertion Baseline:
Partial run — only forms flow was tested
Forms assertions: 12 (+0 added, -0 dropped)
/qa personas # All persona types
/qa persona-admin # Only admin persona instances
Example:
/qa personas
Persona Testing
================
3 types identified (role-based detection: UserRole enum + middleware guards)
admin-1 Maria Chen (methodical): PASS (4/4 steps)
-> Dashboard loaded, user list visible, created user, settings saved
admin-2 Jake Torres (impatient): FAIL (2/4 steps)
-> Clicked "Create User" before page loaded — form submitted empty
-> Key finding: No client-side validation on admin user creation form
admin-3 Priya Sharma (exploratory): PASS (6/6 steps)
-> Tested URL manipulation /admin/users/99999 — got proper 404
-> Tried SQL-like input in search — properly escaped
editor-1 Sam Wilson (cautious): PASS (5/5 steps)
-> Read all tooltips, checked confirmation on delete — dialog present
editor-2 Alex Rivera (power_user): PASS (5/5 steps)
-> Navigated via direct URLs, used Tab/Enter for forms — all worked
editor-3 Jordan Lee (confused): FAIL (3/5 steps)
-> Went to /settings instead of /posts — no breadcrumb to navigate back
-> Used browser back 3 times — landed on blank state page
visitor-1 Casey Smith (methodical): PASS (4/4 steps)
visitor-2 Dana Park (impatient): PASS (4/4 steps)
visitor-3 Riley Jones (exploratory): PASS (5/5 steps)
Cross-Persona Insights:
- Impatient personas fail on forms without client-side validation (2 instances)
- Confused personas get lost when breadcrumbs are missing (1 instance)
- All exploratory personas successfully handled — error pages work well
After the first /qa run crystallizes tests, you can run them headlessly:
npx playwright test qa-reports/playwright/If a Playwright test fails in CI (e.g., after a UI change), run /qa locally — Claude re-tests with Chrome and re-crystallizes with updated selectors.
The plugin tracks every assertion across runs. On run #5:
Assertion Baseline Updated
==========================
Run #5 | Assertions: 47 (+2 new, -3 dropped, ~1 changed)
Coverage Score: 93.6% (drift detected!)
DROPPED ASSERTIONS (regressions):
- forms:step-3:element_visible:button[type=submit] — Submit button check
- forms:step-5:input_validation:email — Email validation check
- auth:step-7:redirect:/dashboard — Post-login redirect check
These checks were previously passing but are no longer being tested.
This answers the question every QA tool misses: "what did we stop checking for?"
| Category | Examples |
|---|---|
| XSS | dangerouslySetInnerHTML without sanitization, innerHTML assignments |
| Injection | Raw SQL with string interpolation, unsanitized exec() calls |
| Auth bypass | API routes without auth checks, missing middleware coverage |
| Secrets | Hardcoded API keys, AWS credentials, database passwords in source |
| Null refs | Unguarded .map() on undefined, destructuring without defaults |
| Race conditions | Stale closures in setState, missing AbortController in useEffect |
| Missing validation | Form submission without checks, API routes without schema validation |
| Flow | What it checks |
|---|---|
| Navigation | Every page loads, no console errors, 404 page works, internal links work |
| Forms | Empty submission shows validation, required fields enforced, valid submit works |
| Auth | Login/signup pages render, validation works, wrong credentials handled, protected routes redirect |
| Responsive | Pages render at mobile/tablet/desktop, hamburger menu works at mobile |
| Dynamic scenarios | Code analysis findings verified in browser (XSS payloads escaped, null refs don't crash) |
| What it catches | Example |
|---|---|
| Missing validation | Impatient user submits form before client-side validation runs |
| Dead-end states | Confused user navigates to wrong section, no way back |
| Permission gaps | Admin can't access feature their role should allow |
| Onboarding failures | First-time visitor can't figure out what to do |
| Keyboard inaccessibility | Power user can't tab through form fields |
| Error recovery | Exploratory user enters garbage data, app crashes instead of showing error |
The plugin detects dynamic values (UUIDs, timestamps, JWTs, session tokens) and uses pattern matchers instead of exact values. This means crystallized Playwright tests don't break when IDs change between runs.
// Without dynamic data handling (breaks on next run):
await expect(page.getByTestId('order-id')).toHaveText('Order #550e8400-e29b-41d4-a716-446655440000');
// With dynamic data handling (stable across runs):
// [DYNAMIC: uuid_v4] Order ID generated per-creation
await expect(page.getByTestId('order-id')).toHaveText(/Order #[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/);Detected patterns: UUID v4, ISO timestamps, Unix timestamps, JWT tokens, hex tokens, MongoDB ObjectIDs, numeric IDs in URLs, relative times ("5 minutes ago"), and more.
Every run gets its own timestamped folder. Persistent files (baseline, Playwright scripts) stay at the root so they accumulate across runs.
qa-reports/
├── baseline.json # PERSISTENT — assertion baseline across runs
├── crystallization.json # PERSISTENT — crystallization state
├── latest-run.json # Points to the most recent run
├── playwright/ # PERSISTENT — crystallized Playwright scripts for CI
│ ├── playwright.config.ts
│ ├── navigation.spec.ts
│ ├── forms.spec.ts
│ └── ...
└── runs/
├── 2026-03-21_14-30-00/ # Each run is preserved
│ ├── run-metadata.json # Run context (timestamp, args, duration, status)
│ ├── routes.json
│ ├── code-analysis.json
│ ├── personas.json
│ ├── summary.md
│ ├── screenshots/
│ ├── flows/
│ │ ├── code-analysis.md
│ │ ├── personas.md
│ │ ├── navigation.md
│ │ └── ...
│ └── issues/ # Jira-ready individual issue files
│ ├── INDEX.md # Sortable table with quick filters
│ ├── SEC-001.md
│ ├── E2E-forms-002.md
│ └── ...
└── 2026-03-20_09-15-00/ # Previous runs are preserved
└── ...
Every issue gets its own standalone markdown file designed for Jira import and team sharing:
---
id: SEC-001
title: "Unsanitized HTML rendering in blog comments"
severity: high
category: xss
source: code-analysis
location: "components/CommentSection.tsx:42"
status: open
e2e_verified: confirmed
run: "2026-03-21_14-30-00"
run_number: 6
---
# SEC-001 — Unsanitized HTML rendering in blog comments
**Severity**: High
**Source**: Code Analysis (confirmed via E2E)
**Location**: `components/CommentSection.tsx:42`
## Steps to Reproduce
1. Navigate to /blog/test-post
2. Enter `<img src=x onerror=alert(1)>` in the comment textarea
3. Click Submit
4. Observe: payload executes as HTML
## Evidence
- **Code Snippet**: `dangerouslySetInnerHTML={{ __html: comment.body }}`
- **Screenshot**: [XSS payload rendered](../screenshots/forms-xss-sec001-001.png)
## Technical Analysis
User-submitted comment body is rendered as raw HTML without sanitization.
## Remediation
import DOMPurify from 'dompurify'
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment.body) }} />
## Verification Steps
1. Re-run `/qa` — SEC-001 should no longer appear
2. Submit `<script>alert(1)</script>` and verify it renders as textThe INDEX.md provides summary-by-severity, summary-by-source tables, and quick filter sections (critical issues, unverified high-severity, E2E-confirmed code issues).
Every issue — code analysis, E2E failure, or persona finding — uses a 5-section format:
- Steps to Reproduce — How to trigger the issue
- Evidence — Code snippet, screenshot, or console log
- Technical Analysis — Root cause explanation
- Remediation — Specific fix with before/after code
- Verification Steps — How to confirm the fix
The plugin scans your code for signals that reveal user types:
| Signal (strongest first) | What it looks for | Example |
|---|---|---|
| Role definitions | Enums, types, permission checks | enum UserRole { ADMIN, EDITOR, VIEWER } |
| Route guards | Middleware, auth HOCs, redirects | requireRole('admin') in middleware.ts |
| UI conditionals | Components rendered by role/plan | {isAdmin && <AdminPanel />} |
| Data models | User schema with role fields | Prisma User model with role enum |
| Navigation | Sidebar items filtered by role | Admin nav vs user nav |
| API permissions | Different behavior per role | Rate limiting tiers, data scoping |
If no role system is found (public site), it falls back to universal personas: first-time visitor, returning user, power user.
Each persona type gets 3 instances with different behavioral profiles:
admin-1: Maria Chen (methodical) — follows every step carefully
admin-2: Jake Torres (impatient) — clicks before pages load
admin-3: Priya Sharma (exploratory) — tries to break things
The 6 profiles: methodical, impatient, exploratory, confused, power_user, cautious. Each profile changes HOW the persona interacts — impatient personas submit forms before validation, confused personas navigate to wrong sections, exploratory personas enter emoji and SQL fragments.
The plugin fingerprints every assertion and tracks them in qa-reports/baseline.json across runs. Each baseline history entry includes a run_dir linking back to the specific run for full traceability. This enables:
- Added assertions: New checks you started making (coverage growth)
- Dropped assertions: Checks that disappeared between runs (coverage drift)
- Coverage score:
current / peak * 100— drops when you stop checking things - Trend history: Last 50 runs for spotting gradual coverage erosion
- Run traceability: Every baseline change links to the specific run that caused it
Coverage Trend (Last 5 Runs)
| Run | Date | Assertions | Added | Dropped | Score |
|-----|--------|------------|-------|---------|--------|
| #5 | Mar 21 | 47 | +2 | -0 | 100.0% |
| #4 | Mar 20 | 45 | +3 | -0 | 100.0% |
| #3 | Mar 19 | 42 | +0 | -2 | 95.5% |
| #2 | Mar 18 | 44 | +10 | -0 | 100.0% |
| #1 | Mar 17 | 34 | +34 | -0 | 100.0% |
Copy system_files/TestFlowTemplate.md to skills/test-your-flow.md and customize. Each skill is a markdown file with step-by-step instructions that Claude executes through the Chrome extension.
- Claude Code with
--chromeflag - Claude in Chrome extension v1.0.36+
- Google Chrome or Microsoft Edge
- A running dev server for the target application
- (Optional)
@playwright/testfor running crystallized scripts in CI
