A pass you can defend.
qa runs your web frontend in a real browser, in the states your users actually meet: empty, denied, 390px wide, dark, stuck behind a menu. It hands your coding agent the evidence, one fix at a time, and every pass states exactly what it covered.
Routes from Next.js, React Router, TanStack Router, Remix, SvelteKit, Astro and Nuxt. Rules for React and TypeScript. Browser checks on any URL.
- Routes
- 2/2
- Unmeasured
- 7
- Exit
- 1
$ curl -fsSL https://qakraken.com/install.sh | sh $ qa setup
$ npx --yes --allow-remote=root --package=https://qakraken.com/qa.tgz qa setup$ bunx --bun --package=https://qakraken.com/qa.tgz qa setupDownload the archive for your platform, then check it and unpack it.
| Platform | Archive | Size | sha256 |
|---|---|---|---|
| darwin-arm64 | qa-1.0.0-darwin-arm64.tar.gz | 43.3 MB | 5d445192d15fa6b7b5e04b0d0a559c24470c64c568928df2ed13a7d488181ba0 |
| darwin-x64 | qa-1.0.0-darwin-x64.tar.gz | 46.7 MB | 0a91eb801a5ca21c1cf529c121f0a8ded32b1a1ddd8af592128e7bf23c6dca37 |
| linux-arm64 | qa-1.0.0-linux-arm64.tar.gz | 47.9 MB | d92264e1fe239deda0047c0efe1cf33357b34de7030ebccd41e022e7f0c0c72a |
| linux-x64 | qa-1.0.0-linux-x64.tar.gz | 47.9 MB | a89cf1cf461a39491bdb91d094719c3f4eac90c3b378b41880eb5e25d58e5bf7 |
$ echo "<sha256> <archive>" | shasum -a 256 -c $ tar -xzf <archive> && mkdir -p ~/.local/bin && mv qa-1.0.0-*/qa ~/.local/bin/qa $ qa setup
Manifest: manifest.json. On macOS a file saved by a browser is quarantined; run xattr -d com.apple.quarantine ~/.local/bin/qa. A curl download is not.
Install qa on this machine, then verify one app repo. 1. Run: curl -fsSL https://qakraken.com/install.sh | sh It downloads one executable, checks its sha256 against the manifest, and installs it to ~/.local/bin/qa. No sudo. If ~/.local/bin is not on PATH, add it. 2. Run: qa setup It installs the browser qa drives. This is the only step that needs Node. 3. From the app repo: qa init <repo> then: qa verify <repo> --preflight Report each exit code. 0 is clean within the measured scope, 1 is findings, 2 is could not run and is never a pass. Do not edit the app's source to make a check pass.
~/.local/bin/qa- Routes
- 2/2
- Personas
- 1/1
- Modes
- 3 390·1440·D
- Depth
- 11/12
- Findings
- 46
- Unmeasured
- 7 items
- Verdict
- exit 1 · gating findings
Green CI. Broken states.
Your tests walk the happy path the app was built against. Your users meet everything else. qa draws the difference, marks each finding, and says which parts it could not reach.
- 1
A menu that never closes
A modal menu stays open with
pointer-events: noneon the body, and every control after it becomes unreachable. qa dismisses it, records it once, and moves on. - 2
Overflow at 390px
The invoices table pushes past the viewport on a phone. It is measured at the width, not inferred from CSS.
- 3
Denied renders blank
A persona without access gets an empty panel instead of a denial. Signed-in personas run through your own auth launcher, so credentials never enter the evidence.
- 4
What it could not reach
Unmeasured is drawn hatched, never painted green. It goes into the denominator, not around it.
What it reads.
qa is built for React and TypeScript front ends. The browser half runs against any URL. Each part below says what it covers.
Found from your source
Next.js app and pages routers, React Router, TanStack Router, Remix, SvelteKit, Astro, Nuxt and Vue Router, Expo Router. Vite apps through their router. Any other app: declare the routes in a file.
23 rules, JS and TS
Read .js, .jsx, .ts and .tsx. Missing empty, error and loading states, stale closures, unsafe HTML, env leaks, double submit, missing error boundaries.
TanStack Query and SWR
Which queries ignore their error or loading state, and which writes leave another page stale after they succeed.
Real Chromium
Playwright per route, width, colour scheme and persona. Works behind a service worker. Sign-in goes through a launcher you provide.
Results export as SARIF, GitHub annotations and JUnit. A local MCP server hands the same run to any MCP client.
Not just pixels.
Most UI testing stops at the screenshot. qa follows a failure to where it starts: the build that is actually served, the contract between client and server, the request that never fired, the cache that went stale, the permission the client assumed. 135 failure classes in eleven layers. Only three of those layers are what you see.
Backend edges, judged per endpoint.
For every route (method, path, input schema, permission, writes), Jev screens the handler for five failure shapes:
- Null or empty input reaches logic unchecked
- Ownership or tenant not checked before a read or write
- A write that is not idempotent on retry
- A partial failure that leaves inconsistent state
- An error path that leaks internals or returns 200
Then code checks exactly what code can: the frontend gate against the backend permission, and the schema's nullability against the model's. Every proven lead gets a test that fails on today's code.
After a write, every surface that renders the data must agree →
The instrument measures.
Your agent fixes.
qa never edits your source. It produces evidence and one brief at a time, and your coding agent owns the commit. Rerun the same command and the exit code tells you whether the fix held.
Detect
Static rules over source, Playwright probes in a real browser, per persona and mode. Jev screens every surface for what rules can’t see.
23 rules · probes · JevReduce
A Rust kernel folds every observation into one deterministic graph.
nodes.jsonl · edges.jsonlBrief
Findings are ranked into lanes. Your agent gets one brief with its repro and evidence.
.verify/lanes/<class>/brief.mdFix
Claude Code, Codex or Cursor edits and commits. qa waits for the source change.
your agent · your commitRecheck
Same command, same scope. A ratchet catches any regression.
exit 1 → 0One graph. Every observation.
The kernel folds every finding, route, component, query, endpoint and piece of evidence into one graph of typed nodes and edges. Hover or focus a node to read what it is tied to. Click or press Enter to hold it.
Hand it to your agent.
Paste this into Claude Code or Codex, inside the app you want checked. It says what qa is, which skill to read, and the first commands to run.
- Paste the prompt into your agent.
- It runs the static pass first, no browser needed.
- With the dev server up, it runs one measured session and reads the brief.
- It fixes the cause, commits, and reruns the same command.
qa checks a web frontend by running it. It runs on this machine and never edits source. Exit codes: 0 clean within the measured scope, 1 findings or a regression, 2 could not run (never a pass). Read the frontend-verify skill (SKILL.md) first. If the qa command is not on PATH, stop and tell me. From the app's directory, start with the static run, no browser needed: qa verify . qa show . Then, with the dev server running: Use frontend-verify in this host worktree. Run `qa run <repo> --base URL --once`, read the generated `.verify/lanes/<class>/brief.md`, own the source fixes and commits, then rerun the same command to recheck. Replace <repo> with this app's path and URL with the dev server address. Report the exit code and what was measured.