qaiq
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@qaiqscan this repo for Playwright test hygiene issues"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
QAIQ
QAIQ scores the hygiene of a Playwright test suite — waits, locators, isolation, assertions, CI config — and hands back findings and questions, not a verdict.
Quickstart
npx @rafaellabarrocas/qaiq .
Running against demo/sample-suite/ — a small Playwright suite written with
deliberate mistakes, included in this repo so the output above is reproducible:
npx @rafaellabarrocas/qaiq demo/sample-suite.
Real output, from running QAIQ on its own repository:
Hygiene Score 99/100
Wait discipline ██████████ 100
Locator quality ·········· n/a
Test isolation ██████████ 98
Assertion strength ██████████ 100
CI hygiene ██████████ 100
FINDINGS (9)
~scripts/__tests__/check-ip-boundary.test.ts:78 ISO-004 Hardcoded identity data that collides in parallel
src/core/__tests__/suppression.test.ts:25 SUP-001 Suppression without a reason
src/core/__tests__/suppression.test.ts:34 SUP-001 Suppression without a reason
src/core/__tests__/suppression.test.ts:39 SUP-001 Suppression without a reason
src/core/__tests__/suppression.test.ts:44 SUP-001 Suppression without a reason
src/core/__tests__/suppression.test.ts:61 SUP-001 Suppression without a reason
src/io/__tests__/config.test.ts:7 ISO-001 Mutable module-level state shared across tests
src/io/__tests__/discover.test.ts:8 ISO-001 Mutable module-level state shared across tests
src/surfaces/mcp/__tests__/server.test.ts:15 ISO-001 Mutable module-level state shared across tests
~ = heuristic rule, weighted at half. Confirm before acting.
NOT MEASURED — these need a human
· Whether you tested the right things (risk coverage)
· Whether your assertions match real intent (oracle correctness)
· Whether the gaps that matter are covered (test design)
· Whether this is safe to release (release judgment)
Questions for a human
? QAIQ measures how tests are written, not what they cover. Who reviewed the test design against the product's real risks?
QAIQ scores test-code hygiene only. A high score means your tests are well-written — not that your product is well-tested.The five SUP-001 findings are QAIQ correctly flagging its own test fixtures:
src/core/__tests__/suppression.test.ts deliberately embeds
qaiq-disable-next-line comments with missing or non-substantive reasons
(-- x, -- ., a 21-character run with no spaces) as literal fixture text,
to prove that FIX 1's substantive-reason bar rejects them. QAIQ's suppression
parser reads raw file text and has no notion of "this text is inside a JS
string literal argument," so it correctly reads these as real (and
correctly rejected) suppression attempts when scanning its own repository.
Locator quality reads n/a, not a perfect score, because QAIQ found zero
locator calls to judge in its own source tree — see Scoring for
why a missing denominator is never rounded up to 100.
Other ways to run it:
npx @rafaellabarrocas/qaiq . --json # machine-readable Scorecard
npx @rafaellabarrocas/qaiq . --min-score=80 # exit 1 if the headline score is below 80Exit codes:
Code | Meaning |
| Ran cleanly. (Findings can still exist — a clean run is not a clean score.) |
|
|
| Operational failure — no test files found, unreadable config, a bad flag. Distinct from |
Related MCP server: AI-SDLC-MCP-Server
What QAIQ does not do
QAIQ scores how your tests are written. It cannot tell you:
Whether you tested the right things (risk coverage)
Whether your assertions match real intent (oracle correctness)
Whether the gaps that matter are covered (test design)
Whether this is safe to release (release judgment)
Those are judgment calls. QAIQ is built to hand them to a person, not answer
them itself — which is why no output of this tool, CLI or MCP, ever returns
an approval, a pass, or a green light. There is no field named ok,
approved, ready, or safe anywhere in its schema, and there will not be
one added. Every scorecard also carries openQuestions, and that list is
never empty — even a suite that triggers zero findings still gets handed at
least one question only a person can answer. A high score means your tests
are well-written, not that your product is well-tested.
The five dimensions
QAIQ scores a suite across five dimensions. Each is scored independently —
null ("n/a") when the suite offers zero opportunities to judge it, never
coerced to a number — and the headline score is the floor of the mean of
whichever dimensions actually had something to measure.
Dimension | What it looks at |
Wait discipline | Fixed sleeps, manual polling, brittle |
Locator quality | CSS/XPath/positional selectors, long literal-text matches, raw element handles — anything more coupled to DOM structure than to what a user perceives. |
Test isolation | Shared mutable state, |
Assertion strength | Tests with no assertion, tautological assertions, un-awaited retrying assertions. |
CI hygiene | Missing failure evidence (trace/screenshot), retry counts high enough to mask flake, no parallelism, unpinned browser images. |
Findings are weighted by severity (error > warning > info) and
confidence (high vs. heuristic, at half weight) against the number of
opportunities QAIQ found for that dimension in your suite — so a suite with
ten waits and one bad one is not judged the same as a suite with one wait
that is bad.
The 20 rules
Generated directly from playwrightPack (src/rules/playwright/index.ts) —
listed here so this table cannot drift from what the tool actually runs.
wait-discipline
WAIT-001— Hardcoded waitWAIT-002— Manual visibility pollingWAIT-003— waitForLoadState('networkidle')WAIT-004— Redundant waitForLoadState('domcontentloaded')WAIT-005— Hand-rolled retry loop
locator-quality
LOC-001— CSS or XPath selector instead of a user-facing locatorLOC-002— Positional selectorLOC-003— Long literal text selectorLOC-004— page.$ / page.$$ element handles
test-isolation
ISO-001— Mutable module-level state shared across testsISO-002— Serial execution modeISO-003— Setup hook with no matching teardownISO-004— Hardcoded identity data that collides in parallel
assertion-strength
ASR-001— Test with no assertionASR-002— Tautological assertionASR-004— Missing await on a retrying assertion
ci-hygiene
CI-001— No trace or screenshot retained on failureCI-002— Retry count high enough to mask flakeCI-003— No parallelism configuredCI-004— Unpinned browser image in CI
Each rule also ships a qaiq_explain_rule entry (see MCP)
with a rationale and a bad/good example — static, generic, and identical for
every caller.
Suppressions
A finding can be suppressed line by line:
// qaiq-disable-next-line WAIT-001 -- flaky third-party widget has no better hook yet
await page.waitForTimeout(2000);The text after -- is mandatory, and it must be substantive: at least 15
characters and at least 3 whitespace-separated words. A reason that is
missing, or present but not substantive (-- x, -- ., a single long word
with no spaces), is treated as no reason at all: the suppression does not
apply — the underlying rule still reports — and it is itself flagged as
SUP-001, "Suppression without a reason." A suppression records a
deliberate human decision, and a non-substantive reason gives a false sense
that one was made; without a real one, nobody can tell later whether it is
still valid. This is the one place in QAIQ where an agent cannot silence a
finding by pattern-matching a comment: it has to write down why, and write
down enough that a person plausibly thought about it.
Every suppression that actually applied is recorded, not hidden: it appears
in the scorecard's suppressed array, and the CLI prints a SUPPRESSED (n)
block naming each path:line, rule, and reason whenever n > 0. A score
reached with suppressions in play is never indistinguishable from a
genuinely clean run.
Configuration
An optional qaiq.config.json at the scanned root:
{
"exclude": ["node_modules", "dist", "build", ".git", "coverage"],
"minScore": 80
}Both fields are optional; the values above are the defaults. --min-score
on the command line overrides minScore from the file.
MCP setup
QAIQ also ships an MCP server exposing four tools, all critique-only:
Tool | What it returns |
| A full scorecard for a repository path. |
| Findings for a pasted code snippet. |
| A rule's rationale and examples, by id. |
| The rule list, optionally filtered by dimension. |
Every one of them returns openQuestions, notMeasured, and disclaimer —
none of them returns anything an agent could read as permission to proceed.
qaiq_scan and qaiq_review_snippet also return findings and
suppressed — every suppression that actually silenced a finding, so an
agent cannot read an empty or high-scoring result as "genuinely clean"
without also seeing what was suppressed to get there. None of them rewrites
your code: qaiq_review_snippet critiques the snippet you send, it does not
send one back.
Point an MCP-capable client at the server:
{
"mcpServers": {
"qaiq": {
"command": "npx",
"args": ["-p", "@rafaellabarrocas/qaiq", "qaiq-mcp"]
}
}
}The -p @rafaellabarrocas/qaiq is required: qaiq-mcp is a secondary bin of the qaiq
package, not a package name in its own right, so npx needs to be told
which package to fetch it from.
Writing a rule
See docs/writing-rules.md for the Rule
interface field by field, both detector kinds, and the fixture pair every
rule must ship with. In short: a rule without a passing "clean" fixture is a
rule with a known false positive, and will not be merged.
Licence
Apache-2.0. See LICENSE. Forks and redistributions must carry
NOTICE — that is a term of the licence, not a request.
This server cannot be deployed
Maintenance
Related MCP Connectors
Statically audits MCP tool surfaces for token cost, schema quality, and design issues.
MCP-native AI evaluation: rubric audits, eval suites, and proof reports for AI/LLM output.
Free MCP tools: the only MCP linter, health checks, cost estimation, and trust evaluation.
Playwright selector risk checks with healing suggestions and receipts.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceProvides comprehensive code quality tools including linting, security scanning, TypeScript checking, and testing through a single MCP server. Integrates multiple quality analysis tools like Biome, ESLint, and Playwright for streamlined development workflows.-
- FlicenseNot gradedqualityBmaintenanceLocal MCP server for static Python/TypeScript code audits, Playwright test generation, and SDLC health checks with a 0-100 quality score.-
- AlicenseNot gradedqualityBmaintenanceSelf-hosted design audit MCP server that reviews UI code against Nielsen's heuristics and an accessibility rubric, returning scores and fixable findings. It also enables verifying previously flagged issues against updated code.33 npmBSD 4-Clause "Original" or "Old"
- FlicenseNot gradedqualityBmaintenanceProvides MCP tools that give LLM agents a full QA engineer workflow: scanning projects, generating deterministic test suites, executing them across browser/API/mobile, diagnosing failures, and proposing fixes that require human approval.-