| name | write-tests |
| description | Write tests for existing production code. Processes ONE file at a time through a full pipeline: analyze, write, verify, blind coverage audit, adversarial review, log. Uses CodeSift for discovery and analysis when available. Modes: [path] (specific target), auto (discover and loop until done), --dry-run (plan only; skips suite verification).
|
| codesift_tools | {"always":["analyze_project","index_status","index_folder","index_file","plan_turn","search_symbols","get_symbol","get_symbols","get_file_outline","get_context_bundle","find_references","search_text","search_patterns","get_test_fixtures"],"by_stack":{"typescript":["get_type_info","resolve_constant_value"],"javascript":[],"python":["python_audit","analyze_async_correctness","resolve_constant_value"],"php":["php_project_audit","php_security_scan","resolve_php_namespace"],"kotlin":["analyze_sealed_hierarchy","find_extension_functions","trace_flow_chain","trace_suspend_chain","trace_compose_tree","analyze_compose_recomposition","trace_hilt_graph","trace_room_schema","analyze_kmp_declarations","extract_kotlin_serialization_contract"],"nestjs":["nest_audit"],"nextjs":["framework_audit","nextjs_route_map"],"astro":["astro_audit","astro_actions_audit","astro_hydration_audit"],"hono":["analyze_hono_app","audit_hono_security"],"express":[],"fastify":[],"react":["react_quickstart","analyze_hooks","analyze_renders"],"django":["analyze_django_settings","effective_django_view_security","taint_trace"],"fastapi":["trace_fastapi_depends","get_pydantic_models"],"flask":["find_framework_wiring"],"jest":[],"yii":["resolve_php_service"],"prisma":["analyze_prisma_schema"],"drizzle":[],"sql":["sql_audit"],"postgres":["migration_lint"]}} |
zuvo:write-tests — Single-File Test Pipeline
Generate high-quality tests for production code. Each file goes through the full pipeline individually — no batching of files or pipeline steps, no skipping verification in normal mode, no skipping coverage audit.
Scope: Existing production files with missing or partial test coverage.
Out of scope: New feature tests (use zuvo:build), mass anti-pattern repair (use zuvo:fix-tests), audit without writing (use zuvo:test-audit).
Argument Parsing
| Input | Behavior |
|---|
[file.ts] | Write tests for one production file |
[directory/] | Write tests for all production files in the directory |
auto | Discover uncovered files, process one at a time until done |
--dry-run | Run Phase 0 + Step 1 for all files, print plan, stop |
--no-cache | Force regeneration of project profile before test planning |
--no-cache clears cached project-profile and queue hints before discovery/classification.
Mandatory File Loading
PHASE 0 — Bootstrap (always, before reading production file)
1. ../../shared/includes/codesift-setup.md -- [READ | MISSING -> DEGRADED]
2. ../../shared/includes/no-pause-protocol.md -- [READ | MISSING -> WARN] (HARD: no mid-file pauses in batch/auto mode)
These files are loaded before reading the production file. Do NOT load test-contract, quality-gates, testing rules, or any other include at this point — you don't know the code type yet.
If codesift-setup.md is missing, print [CONTEXT] codesift-setup missing — assuming CodeSift unavailable and continuing in degraded mode. Continue the run with legacy detection and native tools. Do not stop the file solely because the bootstrap include is absent.
PHASE 0.5 — Classify (read production file, determine loading tier)
After CodeSift setup, read the production file fully. Then read ../../shared/includes/test-code-types.md and classify from that file's canonical table. Do NOT classify from memory.
- Code type: VALIDATOR / SERVICE / CONTROLLER / HOOK / PURE / COMPONENT / GUARD / API-CALL / ORCHESTRATOR / STATE-MACHINE / ORM-DB
- Complexity: THIN / STANDARD / COMPLEX
- Testability: UNIT_MOCKABLE / UNIT_REFLECTION / NEEDS_INTEGRATION / MIXED
Determine loading tier:
IF code_type IN (PURE, VALIDATOR) AND complexity == THIN → LIGHT
IF code_type IN (PURE, VALIDATOR) AND complexity == STANDARD → STANDARD
IF code_type IN (STATE-MACHINE) AND complexity == THIN → LIGHT
IF code_type IN (COMPONENT, HOOK) → COMPONENT
IF code_type IN (CONTROLLER, ORCHESTRATOR) → HEAVY
IF complexity == COMPLEX → HEAVY
IF module mixes code types (e.g. PURE/VALIDATOR + API-CALL/ORCHESTRATOR) → STANDARD (HEAVY if any unit is COMPLEX)
ELSE → STANDARD
When classification is ambiguous, default to STANDARD (loads more than LIGHT, less than HEAVY).
Print: [CLASSIFIED] {file}: {code_type} {complexity} → tier {TIER}
PHASE 1 — Conditional Load (based on classification tier + detected stack)
Load ONLY the includes matching the detected tier AND stack. Print READ/SKIP status for each.
If an include is missing:
- print
[PHASE1] MISSING: <file> — continuing with degraded rules
- continue loading the remaining includes
- after Phase 1, print
loaded=<N>/<M> for the files expected for this tier/stack
- if fewer than half of expected includes loaded, print
[WARN] Low include availability — coverage planning and Q-score confidence are reduced for this file. Do not overclaim clean states.
Always load (all tiers, all stacks):
| Include | LIGHT | STANDARD | HEAVY | COMPONENT |
|---|
../../shared/includes/test-contract.md | Full | Full | Full | Full |
../../shared/includes/test-blocklist.md | Full | Full | Full | Full |
../../shared/includes/quality-gates.md | Q1-Q19 only* | Q1-Q19 only* | Q1-Q19 only* | Q1-Q19 only* |
../../rules/testing.md | Full | Full | Full | Full |
../../shared/includes/test-mock-safety-core.md | Full | Full | Full | Full |
../../shared/includes/test-code-types-core.md | Full | Full | Full | Full |
Stack-specific (load ONLY matching stack):
| Include | LIGHT | STANDARD | HEAVY | COMPONENT |
|---|
test-mock-safety-js.md OR test-mock-safety-php.md | SKIP | Full | Full | SKIP |
test-code-types-js.md OR test-code-types-php.md | SKIP | Full | Full | SKIP |
../../shared/includes/test-edge-cases.md | SKIP | Full | Full | Full |
Stack detection: resolve stack per target file using the nearest manifest:
- nearest
package.json => JS/TS
- nearest
composer.json => PHP
- nearest
pyproject.toml => Python (core-only mode: no Python-specific test-mock-safety-* or test-code-types-* includes exist yet)
If multiple manifests are equally near, prefer package.json > composer.json > pyproject.toml and print the conflict decision.
Load at most one stack-specific include family. Python uses test-mock-safety-core.md, test-code-types-core.md, and test-edge-cases.md; rows 8-9 are SKIP until Python-specific split includes exist.
* quality-gates.md: Read ONLY from ## Q1-Q19: Test Quality Gates to end of file. Skip CQ1-CQ29.
PHASE 1 — LOADED:
2. test-contract.md -- [READ]
3. test-blocklist.md -- [READ]
4. quality-gates.md -- [READ Q1-Q19 only]
5. testing.md -- [READ]
6. test-mock-safety-core.md -- [READ]
7. test-code-types-core.md -- [READ]
8. test-mock-safety-{stack}.md -- [READ | SKIP — per tier/stack]
9. test-code-types-{stack}.md -- [READ | SKIP — per tier/stack]
10. test-edge-cases.md -- [READ | SKIP — per tier]
DEFERRED — Load after queue empty (Completion only, once per run)
D1. ../../shared/includes/run-logger.md -- [READ at completion]
D2. ../../shared/includes/retrospective.md -- [READ at completion]
D3. ../../shared/includes/knowledge-curate.md -- [READ at completion]
Phase 0: Bootstrap + Classify (baseline once per run, classification once per file)
-
CodeSift setup per codesift-setup.md. Note repo identifier.
-
Read production file. Read the target file fully. This happens BEFORE loading any other includes.
-
Detect stack per the nearest-manifest rule above. If target file extension conflicts with the manifest winner, the target file extension wins. Record the final stack before Phase 1 loading.
-
Classify per PHASE 0.5 above. Determine code type, complexity, testability, and loading tier.
-
Load conditional includes per PHASE 1 table above. Print READ/SKIP status for each.
-
Dynamic context retrieval (when CodeSift available): Run targeted retrieval dimensions for the target file. Which dimensions run depends on the tier:
- LIGHT tier: D1 only (file is self-contained, no complex mocks)
- STANDARD tier: D1 + D2 (conditional) + D3 (conditional) + D4
- HEAVY tier: D1 + D2 + D3 + D4
- COMPONENT tier: D1 + D4 (setup comes from exemplar, skip D2-D3)
Skip any dimension that times out or fails — partial context is better than none.
Print: [CONTEXT] Tier: {TIER}, exemplar={path}, {N} import mocks, {N} signatures
Retrieval queries are stack-aware. Use the stack resolved in Phase 0 Step 3. Use the matching query set below.
Dimension 1 — Exemplar test (ALL tiers): Find an existing test file to use as pattern reference.
JS/TS stack:
find_references(repo, "<main_export_of_target_file>")
→ look for *.test.* or *.spec.* files in results
→ fallback: search_text(repo, query: "describe.*<ClassName>", file_pattern: "**/__tests__/*")
→ fallback: search_text(repo, query: "describe", file_pattern: "**/<same_module>/__tests__/*")
PHP stack:
search_text(repo, query: "extends Unit|extends TestCase", file_pattern: "tests/**/*Test.php", max_results: 5)
→ prefer test file in same module: tests/unit/<ModuleName>*Test.php
→ fallback: search_text(repo, query: "createMock|getMockBuilder", file_pattern: "tests/**/*Test.php", max_results: 3)
Python stack:
search_text(repo, query: "class Test<ClassName>|def test_<function>", file_pattern: "tests/**/test_*.py|**/tests.py", max_results: 5)
→ fallback: search_text(repo, query: "mock.patch|MagicMock", file_pattern: "tests/**/test_*.py", max_results: 3)
Read the exemplar fully — it shows how THIS project writes tests (mock style, describe/class structure, import conventions, setup patterns).
Print: [CONTEXT] Exemplar: {path} or [CONTEXT] No exemplar found — using generic patterns.
Dimension 2 — Import mocks (STANDARD+ tiers, skip for LIGHT/COMPONENT): Also skip if Dimension 1 found an exemplar in the same module (exemplar already shows mock patterns).
For at most 5 imports from the target file (skip vendor/node_modules, skip type-only imports):
JS/TS: search_text(repo, query: "vi.mock.*<import_path>|jest.mock.*<import_path>", file_pattern: "**/__tests__/*|*.test.*|*.spec.*", max_results: 3)
PHP: search_text(repo, query: "createMock.*<ClassName>|getMockBuilder.*<ClassName>", file_pattern: "tests/**/*Test.php", max_results: 3)
Python: search_text(repo, query: "mock.patch.*<module_path>|MagicMock.*<ClassName>", file_pattern: "tests/**/test_*.py", max_results: 3)
Collect: which dependencies are mocked, what mock patterns are used.
Print: [CONTEXT] Import mocks: {N} dependencies with existing mock patterns. or [CONTEXT] D2 skipped — exemplar covers mock patterns.
Dimension 3 — Test setup (STANDARD+ tiers, skip for LIGHT/COMPONENT): Also skip if CLAUDE.md describes test infrastructure OR if exemplar test already imports setup helpers.
JS/TS: search_text(repo, query: "setupFiles", file_pattern: "vitest.config.*|jest.config.*")
PHP: search_text(repo, query: "_bootstrap|Helper|ActorActions", file_pattern: "tests/**/*.php|codeception.yml")
Python: search_text(repo, query: "conftest|fixtures|factory", file_pattern: "tests/**/conftest.py|pytest.ini|setup.cfg")
Extract setup file paths → read their outlines. These contain global mocks, fixtures, helpers.
Print: [CONTEXT] Setup: {N} setup files. or [CONTEXT] D3 skipped — setup info available from exemplar/CLAUDE.md.
Dimension 4 — Hub signatures (STANDARD+ and COMPONENT tiers, skip for LIGHT): What do the target file's imported utilities look like?
Extract import/use names from target file → query signatures:
search_symbols(repo,
query: "<imported_function_or_class_names>",
detail_level: "compact",
include_source: true,
token_budget: 800
)
Do NOT use get_symbols() for bare names — it requires a qualified symbol ID (file.ts::FunctionName) which is unknown at this stage. search_symbols accepts bare names and returns matches. Use get_symbols only when the qualified ID is already known from a prior search_symbols result.
This gives function/method signatures without full source.
Print: [CONTEXT] Signatures: {N} utility functions.
Error handling per dimension: If any query times out or returns an error, print [CONTEXT] Dimension N skipped — {reason}. and continue with remaining dimensions.
If a CodeSift query fails with a repo/index error (Repository not found, not indexed, or equivalent), run the recovery loop from codesift-setup.md (index_status -> optional index_folder -> retry once) before skipping that dimension.
If a CodeSift query fails with Transport closed (or equivalent transport/session teardown) after initialization succeeded, stop retrying CodeSift for the rest of the current run and switch immediately to legacy detection/manual reads.
If CodeSift unavailable: Skip all 4 dimensions. Print: [CONTEXT] CodeSift unavailable — using legacy detection.
-
Test runner refinement: Read the nearest manifest/config for the resolved stack. Detect test runner (vitest/jest/phpunit/pytest). Find existing test patterns (DB helpers, factory functions, mock conventions). If no manifest exists, infer runner from the target file extension. If still unknown, mark the file FAILED and backlog the environment issue. If Dimension 1 found an exemplar, stack is already implied — but confirm the runner from config.
For JS/TS COMPONENT or HOOK targets, explicitly inspect whether project setup registers DOM matchers (@testing-library/jest-dom / @testing-library/jest-dom/vitest) and whether cleanup is global. If the project lacks global setup for those, reuse the exemplar's local import and afterEach(cleanup) pattern instead of guessing.
-
Build queue:
- Explicit mode: queue = user's target file(s)
- Auto mode with CodeSift: use available CodeSift primitives to gather, at minimum:
- dead or leaf production candidates
- 90-day hotspots
- whether each candidate export is referenced from test files
- role/classification signals under
<source-root>/
Prefer one batched retrieval when the environment supports it; otherwise run equivalent read-only calls and merge the results.
Dead/leaf symbols with 0 test refs = UNCOVERED. Priority: hub symbols first (many connections = failures cascade), then high-churn, then leaf.
If any sub-query fails or returns empty, log degraded discovery and fall back to the non-CodeSift queue builder.
- Auto mode without CodeSift: resolve
<source-root> from the nearest manifest (src/, app/, lib/, else repo root), then glob for production files in that root using stack-appropriate extensions. Files without matching tests = UNCOVERED.
-
Baseline test run: execute test suite once per run, after the queue is known and before the queue loop starts, and record pre-existing failures. These are ignored in verification. Skip Step 9 in --dry-run. If the runner/config is unavailable, backlog one run-level environment issue and mark every queued file FAILED with Blind Audit=skipped and Adversarial=not_run, then stop.
- Schema-drift pre-probe (only when the queue contains an ORM-DB target OR a DB test helper — seed/factory/migration — is detected): before the per-file loop, run one schema-drift probe (invoke the project's seed/setup helper once, or diff
information_schema against the ORM-declared schema). Treat a column/relation/table does not exist result as a run-level ENV blocker: print and backlog it, mark every queued DB-backed file FAILED with Blind Audit=skipped and Adversarial=not_run, then stop. Skip this probe in --dry-run.
--dry-run mode: after Step 8 builds the queue, run Step 1 (Analyze) for each file, print classification table, STOP. Never run Step 9 or any other shell command that would validate or mutate the suite.
Per-File Loop
For each file in the queue, execute Steps 1, 2, 3, 3.5, 4, and 5 in order. Do NOT skip any step unless a later step explicitly defines a degraded terminal state such as SKIPPED_REVIEW. Do NOT proceed to the next file until every required checkpoint completes or is explicitly downgraded by the skill.
Step 1: Analyze
The production file was already read and classified in Phase 0.5. If a test file already exists, read it now. Assess existing test quality:
- No test file → action: CREATE
- Test file exists, quality OK (behavioral assertions, no anti-patterns) → action: ADD TO (extend with missing coverage)
- Test file exists, quality BAD (fragile string tests, tautological oracles, security theatre, duplicated positives, structural tests that duplicate behavioral ones) → action: REWRITE. Fix the whole file, not just add tests. Net test count MAY decrease. Remove anti-patterns, consolidate with it.each, keep only behavioral tests.
Duplicate test file detection: Before locking the action, search sibling and legacy test trees for other test files that target the same production module (same import target, same basename, or same co-located __tests__/ pattern).
- If 2+ active test files target the same production file, print
[DUPLICATE] Found {N} test files for {production-file}: {paths}.
- Read every duplicate before deciding
ADD TO vs REWRITE.
- Prefer the nearest co-located test file as the canonical file. If no co-located file exists, prefer the file with the strongest existing behavioral coverage.
- Do not silently create or extend a second overlapping test suite.
- If the duplicates materially overlap and cannot be safely consolidated within this single-file run, mark the file
FAILED, backlog duplicate-test-suite, and stop instead of deepening the duplication.
Do NOT add good tests on top of bad tests. If existing tests are weak, fix them first. "ONE file, FULL pipeline" means the WHOLE test file, not just the gap you were sent to fix.
Rewrite scope is still single-file. Do not turn one target file into a broad anti-pattern cleanup campaign across unrelated tests; use zuvo:fix-tests for that.
Barrel file detection: If the file contains ONLY export { X } from './sub-module' lines (zero owned logic), it is a barrel/re-export file. Do NOT write delegation tests for it — write a coverage row immediately with Status=SKIPPED_BARREL, Tests=0, Q Score=N/A, Blind Audit=skipped, Adversarial=not_run, then expand the queue to the sub-modules it re-exports from. Print: [BARREL] {file} is a re-export barrel — expanding to {N} sub-modules.
If exemplar test loaded in Phase 0 (Dimension 1): Use it as the primary pattern reference.
First, extract these patterns from the exemplar before planning tests:
- Cleanup pattern: Does it use
afterEach(cleanup)? afterAll? Nothing?
- Matcher library: testing-library (
screen.getByRole) vs enzyme (wrapper.find) vs direct (container.querySelector)?
- Async pattern:
findBy (auto-wait) vs waitFor vs act?
- Mock factory style: inline
vi.mock vs shared factory vs __mocks__/ directory?
- Import conventions: path aliases, relative imports, barrel imports?
Then apply:
- Copy mock import style from exemplar (vi.mock paths, mock factory patterns)
- Match describe/it nesting structure
- Reuse setup patterns (beforeEach, afterEach, shared helpers)
- Match assertion style (toEqual vs toBe, exact vs loose)
Do NOT invent new patterns — follow what the exemplar does.
If import mocks loaded (Dimension 2): Use discovered mock patterns in MOCK INVENTORY section of test contract. Copy mock patterns from existing project tests, not from memory.
If hub signatures loaded (Dimension 4): Reference utility function signatures when planning assertions. Know what isPrismaNotFound(error) returns before writing error-path tests.
Step 1.5: Bug Scan (before writing tests)
You just read the production code. Before planning tests, scan for bugs:
- Missing error handling (uncaught promise, empty catch)
- Logic errors (wrong operator, off-by-one, inverted condition)
- Security gaps (missing auth check, unsanitized input, unbounded query)
- Edge cases the code doesn't handle (null, empty, duplicates)
If you find a bug: note it (file:line + description) as a fix-in-run candidate for Step 4.5 — do NOT silently route it to memory/backlog.md and walk away. A fixable production bug surfaced by this skill is fixed in this run, not parked.
- If the strongest honest regression test would be red against current production code, do NOT weaken the assertion just to satisfy Step 2.
- Instead, write a characterization test that documents current (buggy) behavior so Step 2 stays green and the bug is provably captured — then FIX the bug in Step 4.5 and flip that test to assert the corrected contract.
- Hand-off/backlog is reserved for fixes whose scope reaches outside the production file under test (see Step 4.5 disposition) — never for an in-scope bug you can fix here.
This converts the old bug-exposure/green-test deadlock into a fix-in-run: capture the behavior now, correct it before the file closes.
Print: [BUG-SCAN] Found {N} potential issues. or [BUG-SCAN] Clean.
With CodeSift: gather outline, complexity, and call-chain context for the target file. Prefer one batched retrieval when supported; otherwise use equivalent discrete CodeSift calls and continue if any one dimension is unavailable.
Without CodeSift: Read the file, count branches manually.
Classification already done in Phase 0.5. Includes already loaded per tier in Phase 1.
Plan: target test count (from code-type formula), describe/it outline, mock strategy. For STANDARD+ tiers, apply edge cases from test-edge-cases.md (already loaded in Phase 1).
PURE optimization (LIGHT tier): Contract: skip MOCK INVENTORY if only Logger. Keep BRANCHES, ERROR PATHS, EXPECTED VALUES. Do NOT skip adversarial — retro shows it catches real bugs even on simple files.
COMPONENT optimization (COMPONENT tier): After finding exemplar (D1), extract: cleanup pattern (afterEach(cleanup)), matcher library (testing-library vs enzyme), async pattern (findBy vs waitFor vs act). Do NOT skip adversarial or edge-cases.
Test contract output: Do NOT print the full contract to the conversation. Use it as an internal checklist. Show the user only: branch coverage table + test outline + planned test count. The contract costs ~2K output tokens and the user doesn't read it.
Print: [file]: [type] [complexity] [testability] → [N] tests planned
Step 2: Write
- Fill test contract per
test-contract.md: BRANCHES, ERROR PATHS, EXPECTED VALUES, MOCK INVENTORY, MUTATION TARGETS, TEST OUTLINE. If 3+ methods share the same control flow pattern (e.g., null guard + try/catch), use per-pattern mode from test-contract.md instead of per-branch.
- Check blocklist per
test-blocklist.md — verify you are NOT about to write any blocked pattern.
- Apply mock rules per the loaded
test-mock-safety-core.md plus test-mock-safety-{stack}.md when that stack file was loaded.
- Write the test file. Use the
Write tool (full file, atomic) — NEVER sequential Edit calls for initial creation or full rewrite. Linters may rewrite the file between Edit operations, destroying all progress. Edit is only permitted for targeted single-hunk changes after the file exists and all tests pass. When introducing a shared test helper and replacing its inline body with a call (replace_all), exclude the helper's own definition line (new pattern == old body) — prefer per-call-site Edits or anchor the replace away from the def, or the helper self-clobbers. Follow the contract and plan exactly.
- When creating a new test file or fully rewriting one under this skill, prepend a generated marker using stack-native comment syntax:
- JS/TS/PHP:
// Generated by zuvo:write-tests
- Python:
# Generated by zuvo:write-tests
- Pre-flight extension check (JS/TS only): Before running baseline, verify test file extension matches runner config. Read vitest.config/jest.config
include pattern. If config uses **/*.spec.ts and test file is .test.ts (or vice versa), rename BEFORE the baseline run. Files with wrong extension compile but never run in CI — silent coverage gap.
- Run tests:
[test runner] [test file]. All new tests must pass. Pre-existing failures ignored. Fix red tests before proceeding.
If the runner output is truncated, unclear, or shows 5+ failures, switch to structured diagnostics before editing blindly:
- rerun one failing test in isolation when the runner supports it
- for JS/Vitest/Jest, prefer structured output (
--reporter json, --reporter verbose, or equivalent) when plain stdout hides the real exception
- extract the first concrete failure mode, then fix that root cause before mass-editing tests
For JS/TS Testing Library failures:
Invalid Chai property: toBeInTheDocument or similar -> inspect setup for DOM matcher registration; add the local matcher import only when global setup does not already provide it
- repeated
Found multiple elements ... after sequential runs -> inspect cleanup pattern from setup/exemplar; add local afterEach(cleanup) only when cleanup is not already global
Red regression tests for known production bugs are not a valid terminal state for write-tests. If the truthful test stays red: do NOT weaken the assertion and do NOT park the bug. Either (a) write it as a characterization test of current behavior now and fix the bug in Step 4.5 — flipping the assertion to the corrected contract — or (b) if the fix is out-of-scope, escalate per Step 4.5 disposition. Backlogging a fixable bug and failing the file is no longer a valid exit.
Step 3: Verify
Context resume guard: If this session was resumed from a compaction summary AND the test file was previously reverted or rewritten, treat as a first-run session. Do NOT skip Step 3.5 (blind audit) or Step 4 (adversarial) based on prior-session claims in the compaction summary. Re-run both on the current file. Claims like "clean blind audit" or "adversarial pass 1 done" from compaction are UNVERIFIABLE — only tool output in the current window counts.
- Anti-tautology check: grep test file for mock-return-echoed-in-assertion patterns. Verify every expected value is spec-derived, not implementation-derived. Any tautological oracle found = fix immediately.
Exception for THIN delegation: When code type is THIN and the method body is a single
return delegateFunction(args), echo testing IS the behavioral test — the facade's contract is to forward unchanged. expect(result).toBe(mockReturnValue) combined with CalledWith is correct, not tautological. P-70 does NOT apply to pure delegation pass-through.
1b. COMPONENT interaction gate: For COMPONENT files, grep the production file for owned callback routing such as onNext=, onBack=, onClick=, onSubmit=, onChange=, or equivalent handler-selection branches. Then grep the test file for fireEvent or userEvent.
- If the production file forwards callbacks and the test file has 0 interaction calls, STOP and add flow tests before self-eval.
- For every distinct owned routing decision where the same child prop slot can receive different handlers by mode, type, or state, add at least one representative interaction test proving the correct handler fires and the competing handler does not.
- Render-only assertions and label-only assertions do not satisfy Q3 or Q14 for callback-routing rows.
- Q1-Q19 self-eval per
quality-gates.md. Print scorecard with evidence:
Self-eval: Q1=1 Q2=1 Q3=0 ... → [N]/19 [PASS|FIX|REWRITE]
Critical gates: Q7=[0|1] Q11=[0|1] Q13=[0|1] Q15=[0|1] Q17=[0|1]
Then print critical-gate evidence with one specific test-file:line citation per gate:
Q7: the error-path test proving exact type/message, or explicit N/A — no production error paths
Q11: the test(s) covering each owned production branch or routing path
Q13: the import line proving the real production module is under test
Q15: the assertion line proving content/value, not just count/shape
Q17: the assertion line plus expected-value source proving the oracle is not echoed from the mock
If you cannot cite a specific test-file:line for a critical gate, score that gate 0. Do not invent scores from memory or from general confidence.
Any critical gate at 0: fix immediately and re-score.
Q-score is a quality gate, not an exhaustive coverage map. Step 3 validates test quality. Step 3.5 validates production behavior coverage.
Step 3.5: Blind Coverage Audit
Read ../../shared/includes/blind-coverage-audit.md now. This is the source of truth for the audit protocol.
Goal: run a production-first coverage audit before adversarial review. Strict contract-blind isolation is required for a passing blind audit. This is not another Q-score and must not reuse the writer's test contract.
Reviewer routing is mandatory before audit dispatch.
Resolve the writer hint using environment precedence:
CLAUDE_MODEL
ZUVO_CODEX_MODEL
CURSOR_AGENT_MODEL
CURSOR_MODEL
GEMINI_MODEL
ANTIGRAVITY_MODEL
- otherwise treat the writer hint as
unknown
Resolve the plugin base first. Bash resolves ../../ against the current working directory — which during a run is the user's PROJECT, not the plugin — so ../../scripts/... does not exist when a skill shells out, and any absolute version dir captured earlier dies on the next release. Set $ZUVO_BASE once (canonical recipe in ../../shared/includes/env-compat.md), then call every script/resource by absolute path:
ZUVO_BASE="${ZUVO_BASE:-$(sed -n 's/.*"installPath"[[:space:]]*:[[:space:]]*"\([^"]*zuvo[^"]*\)".*/\1/p' \
"$HOME/.claude/plugins/installed_plugins.json" 2>/dev/null | head -1)}"
[ -d "$ZUVO_BASE/scripts" ] || ZUVO_BASE=$(ls -d "$HOME/.claude/plugins/cache/zuvo-marketplace/zuvo"/*/ \
2>/dev/null | grep -E '/[0-9]+\.[0-9]+\.[0-9]+/$' | sort -V | tail -1 | sed 's:/$::')
Run $ZUVO_BASE/scripts/reviewer-model-route.sh with no override flags before selecting the blind-audit reviewer artifact. Enforce a 5s timeout. Runtime callers must not eval resolver output.
Treat resolver output as valid only when stdout contains exactly one single-line KEY=VALUE entry for each required key:
platform
writer_model
writer_lane
reviewer_lane
reviewer_model
routing_status
Any missing key, duplicate key, unknown key, multi-line value, timeout, missing script, or non-zero exit status = routing-failed.
Print a routing note immediately after resolution, then repeat the same line in the final Step 3.5 output block:
Reviewer routing: writer=<model>, reviewer=<model>, lane=<review-primary|review-alt|same-model-fallback>, status=<ok|same-model-fallback|unknown-writer-model|routing-failed>
Routing rules:
reviewer_lane=review-primary and routing_status=ok -> use blind-coverage-auditor
reviewer_lane=review-alt and routing_status=ok -> use blind-coverage-auditor-alt
reviewer_lane=same-model-fallback or routing_status=unknown-writer-model -> use blind-coverage-auditor, record degraded routing explicitly, and never describe the audit as cross-model
routing_status=routing-failed -> do not select an agent artifact from lane data; only a fresh subprocess may continue
If the resolver is missing, exits non-zero, times out, or emits malformed output, treat routing as degraded:
Reviewer routing: writer=<writer-hint-or-unknown>, reviewer=unknown, lane=same-model-fallback, status=routing-failed
- continue only if strict isolated execution is still available
- never invent a reviewer mapping inline
Execution paths:
- Required: isolated read-only
blind-coverage-auditor or blind-coverage-auditor-alt, chosen from the resolver output above, or the canonical fresh-subprocess wrapper $ZUVO_BASE/scripts/blind-audit-codex.sh
Strict isolated execution receives only:
$ZUVO_BASE/shared/includes/blind-coverage-audit.md
- production file
- test file
- optional repo identifier
Do not use CodeSift in strict mode.
Canonical fresh-subprocess fallback: when agent-based strict isolation is unavailable or routing is routing-failed, use:
$ZUVO_BASE/scripts/blind-audit-codex.sh \
--protocol "$ZUVO_BASE/shared/includes/blind-coverage-audit.md" \
--production "<absolute-path-to-production-file>" \
--test "<absolute-path-to-test-file>"
This wrapper is the only allowed subprocess fallback. It is platform-aware and must use the local client CLI (codex, gemini, or claude) without repo tools. It must exit 0 and emit a validated strict block containing:
Audit mode: strict
Coverage verdict:
INVENTORY COMPLETE:
- the required inventory table header from
blind-coverage-audit.md
If the wrapper script is missing, exits non-zero, times out, or fails validation, do NOT substitute an inline same-run audit. Mark the file FAILED, persist Blind Audit=skipped, set Adversarial=blocked, and stop after backlog persistence.
Audit order:
- Read the production file first and enumerate owned behaviors.
- Classify each row as owned vs delegated.
- Read the test file second and map evidence.
- Assign one coverage state per row:
FULL | PARTIAL | NONE | STRUCTURAL_ONLY | N/A
- Issue one verdict:
CLEAN | FIX | REWRITE
- Name exactly one highest-value missing test.
Thin delegators and wrappers are audited on forwarding contract only. Do NOT demand downstream implementation tests. Barrels remain out of scope. Accessibility fallbacks, including nodes such as role="status", are owned behavior when this module renders them.
Pass budget: max 2 blind-audit passes per file.
- Pass 1: audit the current test file.
- If verdict = FIX: patch tests, re-run the target test file, then rerun Step 3.5 once.
- If verdict = REWRITE: rewrite the test file from Step 2, rerun Step 3, then rerun Step 3.5 once.
- If verdict remains FIX or REWRITE after pass 2: mark the file
FAILED, backlog the findings, and do NOT proceed to Step 4.
Blind-audit state machine:
| Blind-audit result | Step 4 transition | coverage.md Blind Audit value | Resume behavior |
|---|
CLEAN via strict path (routing_status=ok, reviewer differs from writer) | Proceed to Step 4 | clean:strict | If adversarial status is missing, resume at Step 4 |
CLEAN via degraded path (routing_status=unknown-writer-model OR same-model-fallback) | Proceed to Step 4 | clean:degraded | Same-model blind spots expected; adversarial compensates |
FIX on pass 1 | Block Step 4; patch tests and rerun once | fix:<n> | Resume at Step 3.5 |
REWRITE on pass 1 | Block Step 4; rewrite from Step 2, then rerun Step 3 + 3.5 once | rewrite | Resume at Step 2 |
FIX or REWRITE on pass 2 | Do NOT run Step 4; mark file FAILED and set Adversarial=blocked | fix:<n> or rewrite | Skip after backlog persistence |
| Wrapper timeout, missing script, or non-zero exit without validated block | Do NOT run Step 4; mark file BLOCKED_INFRA (not FAILED — tests may be fine, infrastructure is the issue) | skipped + Failure Cause=blind-audit-timeout or blind-audit-invalid | Skip after backlog persistence |
| Strict audit unavailable or inputs unreadable | Do NOT run Step 4; mark file BLOCKED_INFRA and set Adversarial=blocked | skipped | Skip after backlog persistence |
Freshness guard: Before each blind-audit pass, compute sha256 of both production and test files. A blind-audit result is valid ONLY for the exact file pair it was run against. If either file is edited after a pass, all prior blind-audit results become INVALID — do not reuse an earlier CLEAN for a newer file pair. Only the latest validated pass for the current hashes may unlock Step 4.
Emit the exact table schema from blind-coverage-audit.md. Summary-only prose is not enough.
Print:
Reviewer routing: writer=<model>, reviewer=<model>, lane=<review-primary|review-alt|same-model-fallback>, status=<ok|same-model-fallback|unknown-writer-model|routing-failed>
Audit mode: strict
Coverage verdict: [CLEAN|FIX|REWRITE]
INVENTORY COMPLETE: [N] rows
| id | kind | production lines | owned_or_delegated | coverage | test evidence | notes |
Prioritized findings: [N or none]
Highest-value missing test: [one concrete test]
Step 4: Adversarial Review (iterative, complexity-tiered)
Enter Step 4 only when Step 3.5 returned Audit mode: strict and Coverage verdict: CLEAN.
Run adversarial passes sequentially, one RANDOM provider per pass (--rotate). Each pass sees the FIXED code from previous passes. Early exit when a pass returns 0 findings. Run until clean or max passes exhausted (whichever first).
Pass count by complexity:
| Complexity | Max passes | Rationale |
|---|
| THIN | 1 | Sanity check — wiring correctness only |
| STANDARD | 2 | Pass 1 finds gaps, pass 2 verifies fixes |
| COMPLEX | 2 + optional 3rd | Extra pass ONLY IF pass 2 found CRITICAL with high confidence |
Agent data shows passes 3-4 yield 0 new findings and cost ~60K tokens. 99% of value is in first 2 passes.
Production bug → fix in-run (NOT hand-off): If any adversarial finding is a high-confidence production bug (not a test gap) and the strongest honest regression test would be RED against current production code:
- Verify it against source first (reject false positives with an attack-vector refutation — e.g. an "unreachable when callers pre-normalize" path is NOT a bug).
- Route the confirmed bug to Step 4.5 and fix it in this run. Do NOT mark the file
FAILED and hand it to zuvo:build/zuvo:debug when the fix is in-scope — that backlog-and-walk-away is the behavior this skill no longer permits.
- The file is still not closed as PASS while the bug is unfixed — but the resolution is to FIX it (Step 4.5), not to defer it.
- Only a fix whose scope reaches outside the production file under test is escalated (see Step 4.5 disposition), and even then loudly, with the in-scope portion still fixed.
Input: production + test file (not just diff). Reviewer needs to see what's being tested to find gaps:
adversarial-review --rotate --mode test \
--context "STACK: [language] [version] / [test-framework] [version]. Code type: [type] [complexity] [testability]. Q-GATES: Q7=[0|1] Q11=[0|1] Q13=[0|1] Q15=[0|1] Q17=[0|1]" \
--files "<absolute-path-to-production-file> <absolute-path-to-test-file>"
STACK in context is mandatory. Without it, reviewers assume JS/TS and generate false positives for PHP/Python mock patterns. Examples:
STACK: PHP 8.3 / Codeception 5 / PHPUnit 10
STACK: TypeScript 5.4 / Vitest 2.0
STACK: Python 3.12 / pytest 8.0
Always use absolute paths for --files. Relative paths fail silently.
Capture full output — never tail/head as the triage source. Redirect the run to a file (adversarial-review ... > zuvo/review.txt 2>&1) and read it whole before triaging. NEVER pipe the adversarial output through tail/head to decide findings — truncation silently drops findings (a pass-2 CRITICAL was lost to tail -60). tail is allowed only for a quick human glance, never as the source of the verdict.
The provider sees both files and focuses on gaps between production behavior and test coverage. Without production code, reviewer can't detect missing ordering tests, auth boundary gaps, or untested error messages.
Adversarial routing priority:
- Primary path: external cross-provider
adversarial-review --rotate
- Fallback-local path: same environment, different-from-writer read-only agent selected via
$ZUVO_BASE/scripts/reviewer-model-route.sh
- Final degraded state:
SKIPPED_REVIEW
If the primary path is missing, exits non-zero, or every provider returns empty:
- run
$ZUVO_BASE/scripts/reviewer-model-route.sh with the same 5s timeout and parser rules used in Step 3.5
- print:
Adversarial routing: path=fallback-local, writer=<model>, reviewer=<model>, lane=<review-primary|review-alt>, status=<ok|same-model-fallback|unknown-writer-model|routing-failed>
- route
review-primary -> adversarial-test-reviewer
- route
review-alt -> adversarial-test-reviewer-alt
- require
routing_status=ok
- if routing resolves to
same-model-fallback, unknown-writer-model, or routing-failed, do NOT run local adversarial fallback; mark file SKIPPED_REVIEW
Fallback-local review is a degraded second opinion. It is valid only when the fallback reviewer model differs from the writer model. Never label it as cross-provider review.
Pass sequence with structured context (prevents repetition):
Pass 1 (primary path):
adversarial-review --rotate --mode test --context "..." --files "<prod> <test>"
Record provider identity only if the script exposes it reliably.
→ fix CRITICAL/WARNING → re-run tests
Pass 2 (primary path):
adversarial-review --rotate [--exclude <pass-1-provider> if known] --mode test \
--context "... FIXED: [...]. REJECTED: [...]. KNOWN: [...]." \
--files "<prod> <test>"
→ fix findings → re-run tests
Pass 3 (COMPLEX only, if pass 2 had CRITICAL):
adversarial-review --rotate [--exclude <pass-2-provider> if known] --mode test \
--context "..." --files "<prod> <test>"
Fallback-local path (only if the primary path never produced a successful provider result):
dispatch `adversarial-test-reviewer` or `adversarial-test-reviewer-alt`
with the production file, test file, stack context, and current FIXED/REJECTED/KNOWN notes
use the same pass budget and fix policy as above
persist the result as `clean:fallback-local` or `<n> findings:fallback-local`
Context rules:
- FIXED findings must NOT be re-raised. If reviewer repeats a fixed finding, ignore it.
- REJECTED findings have a severity cap:
REJECTED: [finding] — max re-raise: INFO. If reviewer escalates a rejected finding above the cap (e.g. INFO → CRITICAL), auto-ignore. This prevents adversarial from overriding conscious scope decisions.
- Before rejecting any CRITICAL/WARNING finding, restate the attack vector in one sentence and verify that your rejection defeats that attack vector, not just the reviewer's suggested fix.
- If the suggested fix is wrong but the attack vector still applies, the finding is not rejected. Either fix it another way or carry it forward as
KNOWN / backlog.
- Each pass adds its own fixes/rejections to the context for the next pass.
- Early exit: 0 new findings (not counting repeats of FIXED/REJECTED).
Stub fidelity rule for ORCHESTRATOR: Route module stubs MUST use all() (catch-all). Testing HTTP methods (GET vs POST) is the responsibility of route module tests, not orchestrator tests. If adversarial flags "stubs don't verify HTTP methods" — REJECT with "scope mismatch, route module responsibility".
If adversarial-review is not found: check $ZUVO_BASE/scripts/adversarial-review.sh. If missing entirely, attempt fallback-local routing. If fallback-local is unavailable or not safely different-from-writer, mark file SKIPPED_REVIEW, record a degraded completion note, and proceed.
Fix policy per pass:
| Finding | Action |
|---|
| CRITICAL | Fix immediately. Re-run tests. |
| WARNING (<10 lines) | Fix immediately. |
| WARNING (>10 lines) | Add to backlog with file:line. |
| INFO on security-context file | Treat as WARNING. Cannot be rejected without backlog entry. Security context = lines 1-5 of production file contain: CQ4, CQ5, GDPR, PII, auth, token, password, secret. |
| INFO (no security context) | Known concerns (max 3). May be rejected with justification. |
| 0 findings | Early exit — stop passes, file is clean. |
| After final pass with unresolved CRITICAL | Mark file FAILED in coverage.md. Backlog findings. |
| Provider unavailable on all passes and fallback-local unavailable | Mark file SKIPPED_REVIEW in coverage.md. |
Step 4.5: Fix surfaced production bugs (in-run)
A write-tests run that surfaces a real production bug fixes it before the file closes — it does not backlog it and hand off. This mirrors zuvo:refactor Phase 3.5 (see [[refactor-fix-in-run]]) and zuvo:review's no-silent-deferral rule (see [[no-silent-backlog-deferral]]): a test run leaves the file tested AND correct, not "tested but still buggy with a note in the backlog." Parking a fixable bug — especially while writing the tests that just proved it — is exactly what the user has repeatedly rejected ([[proper-solutions-only]]).
Trigger: Step 1.5, Step 2, or Step 4 surfaced one or more confirmed-real production bugs (verified against source — false positives are rejected with an attack-vector refutation, NOT carried here).
Disposition is fix-SCOPE, not severity. Severity decides merge-blocking and follow-up breadth; it does NOT decide fix-now-vs-defer. A HIGH-severity security bug with a clear in-scope fix is fixed now, exactly like a trivial one:
| Situation | Action |
|---|
| Real bug, fix is within the production file under test (or a clearly-owned helper) | FIX it now. Any size. Then write/flip the regression test to assert corrected behavior. |
| Fix needs changes OUTSIDE the test's production target (cross-module reorder, shared guard, schema/migration) | Escalate loudly to zuvo:build / zuvo:security-audit with file:line + repro — AND fix any clearly in-scope portion. Record the escalation; never a silent backlog row. |
| The "bug" is a behavior/product DECISION (e.g. partial-result vs hard-error on total failure) | Interactive: ask. Batch/--auto: pick the safe default, log it, proceed. Backlog only if the user declines. |
| HIGH/CRITICAL security bug, in-scope | Fix now AND surface to zuvo:security-audit for breadth (is this a class of holes?). Fixing the instance is mandatory; the audit is the follow-up, never a substitute for the fix. |
Stacked-commit structure (preserves characterization purity):
- Commit 1 — the test file written against current behavior (characterization). For a buggy path, this is the test that documents/exposes the bug.
- Commit 2 — the production fix + the regression test flipped to the corrected contract (red on the commit-1 SHA, green now). The two commits together prove "this is what it did → this is the fix."
Use the auto-commit policy already in effect. Do NOT weaken an assertion to dodge a fix; do NOT collapse the two concerns into one hidden edit.
After Step 4.5 the file's terminal state is PASS (bug fixed, regression test green) — NOT FAILED. Re-run the touched tests and re-run the blind audit on the new (production, test) hash pair if production code changed (the freshness guard invalidates the prior CLEAN). Only genuinely out-of-scope or user-declined items remain in memory/backlog.md. Record fixed files in Files tested: ... ([J] fixed) and in the Step 2b review artifact.
Step 5: Log
Update memory/coverage.md:
| File | Status | Tests | Q Score | Blind Audit | Adversarial | Date |
Statuses: PASS, FAILED, SKIPPED_REVIEW, SKIPPED_BARREL
Blind Audit values: clean:strict, fix:<n>, rewrite, skipped
Adversarial values: clean, clean:fallback-local, <n> findings, <n> findings:fallback-local, skipped, blocked, not_run
SKIPPED_REVIEW is a degraded terminal state, not a clean pass. Never silently collapse it into PASS.
Rows that never enter Step 4 must persist Adversarial=blocked or Adversarial=not_run; never leave the column empty.
Persist Q Score as a durable value, not prose memory: <score>/19 (Q7=?,Q11=?,Q13=?,Q15=?,Q17=?).
Print per-file summary: [status] [file] — [N] tests, Q [N]/19, blind audit: [clean:strict|fix:<n>|rewrite|skipped], adversarial: [clean|clean:fallback-local|N findings|N findings:fallback-local|skipped|blocked|not_run]
Do NOT treat a file as complete unless both Blind Audit and Adversarial columns are populated.
→ NEXT file in queue.
Completion (after queue empty)
- Backlog persistence: write unfixed issues to
memory/backlog.md
- Knowledge curation per
knowledge-curate.md
2b. Content-keyed review artifact (on success only): if this run modified any
production files — which now includes any Step 4.5 in-run fix (the
pipeline-entry classifier excludes *.test.*/*.spec.*, so a test-only run records
nothing here, but a run that fixed a production bug DOES) — write the
content-keyed artifact memory/reviews/<base7>..<head7>-<slug>.md with the
range:/files: header per ../../shared/includes/review-artifact.md, listing
those production files. This run's blind-audit + adversarial review ALREADY reviewed
them, so the artifact records that content as reviewed — the pre-push/CI gates then
accept it without demanding a redundant standalone zuvo:review. Skip when only
test/docs files changed (those are never gate-eligible).
Retrospective (REQUIRED)
Follow the retrospective protocol from retrospective.md.
Gate check -> structured questions -> TSV emit -> markdown append.
This step is MANDATORY — do not skip it. Write the retro BEFORE the terminal report below.
- Report:
WRITE-TESTS COMPLETE
-----
Files tested: [N] ([M] new, [K] extended, [J] fixed)
Tests written: [N] total
Q gates: [N]/19 avg (critical gates: all pass)
Blind audit: [N] clean, [M] failed/rewrite, [K] skipped
Validation: [full-suite|scoped:touched-tests]
Failures: pre-existing: [N], new in scope: 0
FAILED files: [list or "none"]
SKIPPED_REVIEW: [list or "none"]
SKIPPED_BARREL: [list or "none"]
Run: <ISO-8601-Z> write-tests <project> - <Q> <VERDICT> <TASKS> <DURATION> <NOTES> <BRANCH> <SHA7> <INCLUDES> <TIER>
-----
Append via wrapper (REQUIRED). Never >> directly to ~/.zuvo/runs.log — the wrapper is the gate that verifies a retro entry exists for this run. Order: retro bash executed → wrapper invoked → completion claimed.
printf '%b\n' "$RUN_LINE" | ~/.zuvo/append-runlog
Expected stdout: OK: appended to runs.log (retro verified for <skill> on <project>). If exit 2 with RETRO_REQUIRED — go execute the retro bash from retrospective.md first; never bypass with ZUVO_SKIP_RETRO_GATE=1. After the wrapper succeeds, print a Logs: evidence line (tail -1 ~/.zuvo/retros.log, grep -c "^<!-- RETRO -->" ~/.zuvo/retros.md, tail -1 ~/.zuvo/runs.log) before claiming completion. Printing the markdown retro section without executing the bash leaves all three log files empty.
Run one final full-suite validation, or explicitly scope the final failure count to touched test files only before printing new in scope: 0.
Completion gate checklist — print BEFORE the WRITE-TESTS COMPLETE block:
COMPLETION GATE CHECK
[ ] Step 2: Test file written with Write tool (not sequential Edit)
[ ] Step 3: Q-score self-eval printed with per-gate evidence (Q7,Q11,Q13,Q15,Q17)
[ ] Step 3.5: Blind audit ran (result: clean:strict|clean:degraded|skipped|blocked_infra)
[ ] Step 4: Adversarial review ran (result: clean|Nfindings|skipped|blocked|not_run)
[ ] Step 4.5: every confirmed in-scope production bug FIXED in-run (not backlogged/handed off); out-of-scope items escalated loudly with in-scope portion fixed
[ ] Step 5: coverage.md row has Status + Blind Audit + Adversarial columns filled
[ ] Step 5: backlog.md updated if any unfixed findings
[ ] Step 2b: content-keyed memory/reviews artifact written IF production files changed (skip if test-only)
[ ] Final test run: all tests pass (N/N)
If ANY checkbox is [ ] (not done), you MUST go back and complete that step before printing WRITE-TESTS COMPLETE. After context compaction/resume, re-read this checklist and verify each step against actual tool outputs in the current window — not from memory or compaction summary.
Do NOT print WRITE-TESTS COMPLETE if any file is missing Status, Blind Audit, or Adversarial in coverage.md.
Resume / Crash Recovery
On start, read memory/coverage.md. If the file uses the old pre-blind-audit schema, normalize the row once by adding empty Blind Audit and Adversarial cells with note legacy-pre-blind-audit, then resume from Step 3.5 before Step 4.
| Status | Blind Audit | Adversarial | Resume action |
|---|
| PASS | clean:strict | present | Skip |
| FAILED | fix:<n> or rewrite or skipped | any | Skip (already backlogged) |
| SKIPPED_REVIEW | clean:strict | skipped | Re-process Step 4 only |
| SKIPPED_BARREL | skipped | not_run | Skip |
| status missing or non-terminal legacy row | fix:<n> | missing or stale | Re-process Step 3.5 |
| status missing or non-terminal legacy row | rewrite | missing or stale | Re-process from Step 2, then Step 3 + 3.5 |
| status missing or non-terminal legacy row | skipped | missing or stale | Re-process Step 3.5 only if inputs are now readable |
| (absent) | - | - | Process from Step 1 |
If a test file exists on disk but file is absent from coverage.md → partial run. Check if file was auto-generated (contains stack-native marker Generated by zuvo:write-tests, e.g. // ... or # ...). If yes, delete and re-process from Step 1. If no (pre-existing/manual test), assess quality in Step 1 and choose ADD TO or REWRITE.
Auto mode: re-run CodeSift discovery to rebuild priority queue (queue order not persisted).
Principles
- Read production code before planning tests. Every assertion traces to real behavior.
- Test depth matches complexity. A 25-line wrapper does not need 30 edge-case tests.
- Test what the code OWNS, mock what it DELEGATES.
- ONE file, FULL pipeline. No batching of files or pipeline steps. Batched read-only discovery queries are allowed.
- Blind coverage audit and adversarial review are separate gates. Step 4 never runs until Step 3.5 is clean.