| name | vibe-test |
| description | Blast-radius-aware test generation for vibe-* projects. Traces every file a change touches, maps the full dependency graph, and writes complete test coverage across the entire blast radius — component tests, integration tests, and E2E tests. Extends existing tests in-place. Writes from scratch where none exist. Triggers on "test:" prefix, "write tests for", "test this feature", "add test coverage", "what needs testing", "test the blast radius", "coverage is missing", "write regression tests". Runs automatically at the end of vibe-add-feature and vibe-fix-bug sessions. Always use when any code has changed and test coverage must be verified or created. Never skips files. Never assumes untouched means unaffected.
|
Vibe Test Skill
Blast-radius-aware test generation.
Traces the full dependency graph of any change, maps every affected file,
and writes complete coverage — component, integration, and E2E.
No gaps. No assumptions. Everything the change could have touched gets tested.
Runs in agent mode (Claude Code / Cursor). Requires filesystem access.
The principle this enforces
A change is only complete when everything it could affect is verified.
Direct changes are obvious. Indirect consumers are where regressions hide.
This skill does not ask "what should I test?"
It asks "what could this break?" — then tests all of it.
Entry points
Entry point A — On demand
Triggered by: test:, "write tests for X", "test this feature", "coverage is missing"
The user specifies what changed or what to test.
Go to Step 1 — Identify the change set.
Entry point B — After vibe-add-feature
Triggered automatically at the end of a feature: session.
The change set is already known: every file listed in FEATURE_TASKS.md under Touches.
Skip Step 1. Read those files directly as the seed set.
Go to Step 2 — Blast radius trace.
Entry point C — After vibe-fix-bug
Triggered automatically at the end of a bug: session.
The change set is already known: every file listed in BUG_TASKS.md under Touches.
The regression test written during fix-bug already exists — do not duplicate it.
Note it, include it in the coverage map, skip re-writing it.
Go to Step 2 — Blast radius trace.
Step 0 — Read project context
Large codebase check — run first, before reading anything:
find . -type f \
! -path '*/node_modules/*' ! -path '*/.git/*' \
! -path '*/dist/*' ! -path '*/build/*' \
| wc -l
Set blast radius budget by project size:
| Size | Files | Max blast radius files | Grep depth | Source file reads |
|---|
| Small | < 50 | Unlimited | 2 levels | All affected |
| Medium | 50–200 | 30 files | 2 levels | All affected |
| Large | 200–500 | 20 files | 1 level | Direct consumers only |
| XL | 500+ | 15 files | 1 level | Seeds + direct only |
For Large / XL projects:
- Cap total blast radius at the budget above
- Prioritise: seeds → direct importers → contract consumers
- Skip indirect (Pass 2) if budget is reached after Pass 1
- Flag skipped files explicitly in the blast radius map:
⚠️ Budget reached — [N] indirect files not traced. Run test: on those modules separately.
- Tell the user the cap was applied before presenting the blast radius map
Store the size classification. Apply the budget throughout Steps 2–3.
Read in this order before anything else:
vibe/ARCHITECTURE.md — testing philosophy, framework, conventions, test location rules
vibe/CODEBASE.md — section 1 (stack), section 2 (commands), section 4 (key files),
section 5 (routes), section 7 (components), section 10 (patterns)
vibe/SPEC.md — acceptance criteria (drives what behaviour must be verified)
- If Entry B:
vibe/features/[date-slug]/FEATURE_SPEC.md + FEATURE_TASKS.md
- If Entry C:
vibe/bugs/[date-slug]/BUG_SPEC.md + BUG_TASKS.md
Detect test runner from ARCHITECTURE.md and CODEBASE.md:
| Stack | Default runner | Test command |
|---|
| Node / Next.js / React | Jest or Vitest | npm test / npx vitest |
| Python / FastAPI / Django | pytest | pytest |
| Flutter / Dart | flutter test | flutter test |
| Go | go test | go test ./... |
If no test runner detected:
Read references/RUNNER_SETUP.md.
Ask the user which runner to install — present only options appropriate for their stack.
Generate setup instructions + install commands.
Wait for confirmation before proceeding.
Detect E2E runner:
playwright.config.* present → Playwright installed
cypress.config.* present → Cypress installed
- Neither present → E2E runner missing
If E2E runner missing:
"No E2E runner detected. Which would you like to set up?
- Playwright — recommended for most projects
- Cypress — if your team already uses it
- Skip E2E for now — flag as coverage gap"
Wait for answer. If 1 or 2: read references/RUNNER_SETUP.md for setup instructions.
Generate the config file + install command. Present to user. Confirm before proceeding.
Step 1 — Identify the change set (Entry A only)
Ask the user exactly what changed:
"What changed? Give me file paths, a feature name, or describe what was modified."
From their answer, build the initial seed set — the specific files that were directly modified.
If vague ("I changed the auth system"):
- Read
vibe/CODEBASE.md to locate relevant files
- List candidates: "These look like the auth-related files: [list]. Which ones changed?"
- Confirm the seed set before tracing
Step 2 — Blast radius trace
Check for dependency graph first:
ls vibe/graph/DEPENDENCY_GRAPH.json 2>/dev/null && echo "GRAPH EXISTS" || echo "NO GRAPH"
If graph exists — use it for blast radius (fast path):
For each file in the seed set:
vibe-graph: query [file]
The graph returns the pre-computed blast radius — imports, imported_by,
test file, verifier, API routes, concept — in one read (~300 tokens).
Union the blast radii of all seed files. That is the full test scope.
No grep required. No file-by-file tracing.
Validate graph result:
For each file in the returned blast radius — confirm it exists:
ls [file_path] 2>/dev/null || echo "FILE NOT FOUND — graph may be stale for this node"
If any blast radius file doesn't exist → fall back to manual tracing for that node only.
If no graph exists — manual trace (original approach):
Starting from the seed set, trace all dependencies in three passes.
Pass 1 — Direct importers (who imports the changed files?)
For each file in the seed set, run:
grep -rn "from.*[filename-without-ext]" \
--include="*.ts" --include="*.tsx" \
--include="*.js" --include="*.jsx" \
. | grep -v node_modules | grep -v ".test." | grep -v ".spec."
grep -rn "from [module] import\|import [module]" \
--include="*.py" . | grep -v "__pycache__" | grep -v "test_"
grep -rn '"[module-path]"' --include="*.go" . | grep -v "_test.go"
For each importer found — does it use the specific thing that changed?
(function signature, exported type, API response shape, schema field)
If yes → add to blast radius. If it imports but doesn't use what changed → exclude.
Pass 2 — Indirect importers (who imports the importers?)
Repeat Pass 1 for every file added in Pass 1.
Go two levels deep maximum — beyond that, the signal-to-noise ratio drops.
Flag if indirect chain is longer than 2 levels: note it but don't trace further.
Pass 3 — Contract consumers (API / schema / type changes)
If the change touched an API endpoint:
grep -rn "[route-path]" --include="*.ts" --include="*.tsx" \
--include="*.js" --include="*.jsx" . | grep -v node_modules
If the change touched a database schema / model:
grep -rn "[ModelName]\|[table_name]" \
--include="*.ts" --include="*.py" --include="*.go" \
. | grep -v node_modules | grep -v "migrations"
If the change touched a shared type or interface:
grep -rn "[TypeName]\|[InterfaceName]" \
--include="*.ts" --include="*.tsx" \
. | grep -v node_modules | grep -v ".test."
Step 3 — Build the blast radius map
Consolidate all three passes into a structured map.
Classify every affected file by layer:
BLAST RADIUS — [change description]
Seeds (directly changed):
[file path] — [what changed]
Layer 1 — Direct consumers:
[file path] — [what it uses from the seed] — [has tests: yes/partial/no]
Layer 2 — Indirect consumers:
[file path] — [chain: seed → L1 → this file] — [has tests: yes/partial/no]
Contract consumers:
[file path] — [uses: route / model / type] — [has tests: yes/partial/no]
Existing regression tests:
[test file] — [what it covers] (Entry C only — from vibe-fix-bug)
Check test coverage for each file:
find . -name "*.test.*" -o -name "*.spec.*" | grep -v node_modules
For each affected file, check if a corresponding test file exists.
Read existing test files for affected files — note what's covered and what's missing.
Classify each affected file:
COVERED — tests exist and cover the affected behaviour
PARTIAL — tests exist but don't cover what changed
NONE — no test file exists at all
Present the blast radius map to the user.
Ask: "Does this look right? Any files to add or remove from scope?"
Wait for confirmation before proceeding.
Step 4 — Generate the test spec
Read references/TEST_SPEC_TEMPLATE.md for the full format.
For each affected file in the blast radius, draft what tests are needed:
For COVERED files:
Note existing coverage. Identify the specific gap introduced by the change.
Document: "extend [test file] — add [N] tests covering [what changed]."
For PARTIAL files:
List what's already tested. List what's missing.
Document: "extend [test file] — missing: [list of uncovered behaviours]."
For NONE files:
Write a full test plan from scratch.
Derive expected behaviours from SPEC.md acceptance criteria where possible.
Where SPEC.md doesn't cover it, derive from reading the actual source file.
Layer ordering in the spec:
- Integration tests (API endpoints, DB queries) — test the data contracts first
- Component tests (UI interactions, rendering, state) — test against verified data
- E2E tests (full user flows) — test the assembled system last
For each test, specify:
- Test file path (where it will be written)
- Describe block and test name
- Setup (mocks, fixtures, test data)
- Action (what to call or render)
- Assertion (what to verify)
- Why this test — what failure it would catch
Present the full test spec.
"Here's the complete test plan across [N] files — [N] integration,
[N] component, [N] E2E tests. Approve to write all files."
Wait for explicit approval before writing a single file.
Step 5 — Write tests: Layer 1 — Integration tests
Write all integration tests first.
API endpoints and DB queries are the foundation — UI tests depend on them being correct.
For each integration test file:
Read the source file in full before writing tests.
Read any existing test file to understand the established pattern.
Test every affected endpoint:
- Happy path — correct input, expected output
- Auth boundary — authenticated vs unauthenticated where applicable
- Validation — invalid input handled correctly
- Edge cases — empty, null, boundary values
- Error states — what happens when downstream fails
For DB-touching code: use test transactions that roll back, or a test database.
Never write tests that mutate production data.
Write pattern — always follow ARCHITECTURE.md testing conventions:
[Actual test code following the project's exact patterns from CODEBASE.md section 10]
After writing each file: run the tests immediately.
[test command from CODEBASE.md section 2] [test file path]
If tests fail: fix before moving to next file.
If tests pass: mark file complete, continue.
Step 6 — Write tests: Layer 2 — Component tests
Write all component tests after integration tests pass.
For each component test file:
Read the component source in full.
Read vibe/ARCHITECTURE.md for the component testing approach.
Check vibe/DESIGN_SYSTEM.md if it exists — visual states may need testing.
Test every affected component:
- Renders correctly with standard props
- Renders correctly in empty/loading/error states
- User interactions fire correct handlers
- State changes produce correct UI updates
- Conditional rendering shows/hides correctly
For components that call APIs: mock the API layer using the project's established mock pattern.
Never make real API calls in component tests.
Write using the established component test pattern from CODEBASE.md section 10.
Run after each file. Fix failures before continuing.
Step 7 — Write tests: Layer 3 — E2E tests
Write E2E tests last — they verify the assembled system, not individual units.
E2E tests are scoped to full user flows that the change affects.
For each E2E test:
Derive flows from SPEC.md acceptance criteria.
Only test flows that the blast radius change could have affected.
Do not duplicate flows already covered by existing E2E tests.
Map each flow:
- Starting state — what the user sees before
- Actions — exact user interactions (click, type, navigate)
- Assertions — what the user sees after
- Edge case flows — error states, empty states, auth boundaries
Playwright pattern:
test('[flow name]', async ({ page }) => {
})
Cypress pattern:
it('[flow name]', () => {
})
Follow the exact E2E pattern from CODEBASE.md section 10 if one exists.
If no pattern exists (first E2E tests): establish one and document it in CODEBASE.md.
Run E2E suite after all tests are written:
npx playwright test
npx cypress run
Step 8 — Run full suite and verify
After all layers are written, run the complete test suite:
[full test command from CODEBASE.md section 2]
Required outcome: all tests green, zero failures.
If failures exist:
- Read the failure output carefully
- Fix the test if the assertion was wrong
- Fix the source if the test revealed a real bug
- Do not suppress or skip failures
- Do not move to Step 9 until suite is fully green
Step 9 — Update vibe docs
vibe/CODEBASE.md:
Add every new test file to section 4 (key files) or section 10 (patterns).
If a new E2E pattern was established, document it in section 10.
vibe/TASKS.md:
Update "What just happened":
## What just happened
✅ Tests written — [N] files across blast radius
Integration: [N] tests · Component: [N] tests · E2E: [N] tests
All passing. Full suite green.
vibe/reviews/backlog.md:
If any file was excluded from coverage (user removed from scope in Step 3):
Log as P2: "Coverage gap — [file] excluded from blast radius testing on [date]."
FEATURE_TASKS.md / BUG_TASKS.md (if Entry B or C):
Mark test tasks as complete. Tick conformance checklist items:
Step 10 — Report
✅ vibe-test complete
BLAST RADIUS
Seeds: [N] files changed
Direct consumers: [N] files
Indirect consumers: [N] files
Contract consumers: [N] files
Total in scope: [N] files
COVERAGE WRITTEN
Integration tests: [N] new · [N] extended · [files]
Component tests: [N] new · [N] extended · [files]
E2E tests: [N] new · [N] extended · [files]
Regression tests: [N] carried from fix-bug (Entry C only)
SUITE RESULT
[N] tests passing · 0 failing · 0 skipped
GAPS FLAGGED
[Any P2 backlog entries added — or "None"]
WHAT'S NEXT
review: ← gate now has real coverage to verify against
Absolute rules
Never skip a file. If it's in the blast radius, it gets tested.
"It probably still works" is not a reason to skip.
Never mock what you're testing. Mock the layer below.
Testing a service? Mock the DB. Testing a component? Mock the API. Never mock the subject.
Never write tests that can't fail. Every assertion must be able to catch a real regression.
A test that always passes regardless of implementation is noise, not coverage.
Never move to the next layer if the current layer is red.
Integration tests must be green before component tests.
Component tests must be green before E2E tests.
Never write E2E tests for flows the change didn't affect.
E2E tests are expensive. Scope them tightly to the blast radius.
Always run after writing. Never deliver unrun tests.