/qa — Quality Assurance Workflow
Run this skill to perform thorough QA on recent changes or a specific file.
Recently Changed Files
!git diff --name-only HEAD~1 2>/dev/null | head -20 || echo "no recent changes detected"
Phase 1: Reconnaissance
- Detect test framework by checking for:
Gemfilewith rspec/minitest → RSpec or Minitestmix.exs→ ExUnitpackage.jsonwith jest/vitest → Jest or Vitestplaywright.config→ Playwright
- Identify changed files — if
$ARGUMENTSis a file path, use that; otherwise rungit diff --name-only HEAD~1 - Classify each file by risk level:
- CRITICAL: auth, payments, data models, shared utilities, API endpoints
- HIGH: business logic, services, controllers
- MEDIUM: views, serializers, helpers, config
- LOW: docs, comments, formatting
- Read each changed file and its existing tests (if any). If more than ~5 files
changed, delegate this reading to the
codebase-exploreragent and work from its summary — keep the main context for the questions and the tests. - Build a context table:
| File | Type | Risk | Existing Tests? |
|---|---|---|---|
| ... | ... | ... | ... |
Phase 2: Interrogation
Before writing any tests, ask 3-8 probing questions using AskUserQuestion. Group questions — don't ask one at a time.
Good questions (risk-focused):
- "This touches the payment flow — should I test refund edge cases?"
- "The new validation rejects blank input. What about unicode-only strings?"
- "This service calls an external API — should I mock it or test integration?"
Bad questions (don't ask these):
- "What does this code do?" (read it yourself)
- "Should I write tests?" (yes, always)
- "What test framework should I use?" (detect it)
Phase 3: Test Plan
Present a structured test plan BEFORE writing any specs. Organize by category:
Test Categories
- Happy Path — expected inputs produce expected outputs
- Edge Cases — boundary values, empty inputs, max values, nil/null
- Error Cases — invalid input, network failures, timeouts, permission denied
- Bizarre/Adversarial — SQL injection strings, script tags, emoji, 10MB strings, concurrent access
- Performance — N+1 queries, unbounded collections, slow queries (only for CRITICAL/HIGH risk)
- Integration — interactions between components (only when multiple files changed)
Present as a table:
| # | Category | Test Description | Risk Level |
|---|---|---|---|
| 1 | Happy Path | Creates record with valid params | HIGH |
| ... | ... | ... | ... |
Wait for user approval before proceeding.
Phase 4: Spec Writing
Write specs following these patterns:
- One assertion per test (when practical)
- Descriptive names —
it "returns 404 when record not found"notit "works" - Arrange-Act-Assert structure
- No mystery guests — all test data visible in the test
- Test behavior, not implementation — don't test private methods directly
- Use factories/fixtures appropriate to the framework
- Group by behavior with
describe/contextblocks
Test Code Quality
Tests are code too — apply the same principles:
- KISS: No over-engineered test helpers. If setup is >10 lines, simplify the test or the code under test.
- YAGNI: Don't test scenarios that can't happen. Don't add tests "just in case" for impossible states.
- DRY (with restraint): Prefer clear duplication over clever shared examples. A test should be readable top-to-bottom without jumping to 3 helper files.
- Clean Code: Descriptive test names that read like documentation. No mystery guests — all data visible in the test.
See @reference.md for framework-specific test notes (RSpec, Minitest, ExUnit, Jest/Vitest).
Phase 5: Execution & Verification
- Run the new specs — targeted run (just the new file)
- Fix any failures — read error, read source, fix root cause
- Run the full suite — ensure nothing else broke
- Produce the QA report — See @reference.md for report template, core values audit, and rationalization check.
Verdict: SHIP IT / FIX AND RE-TEST / NEEDS REWORK