| name | dispatch-test-designers |
| description | Use when generating systematic, multi-perspective test coverage for code. Dispatches multiple independent agents with different test-design lenses to produce a comprehensive test suite — covering input boundaries, contracts, error paths, properties, state machines, integration seams, concurrency, regressions, and security. Triggers on "dispatch test designers", "generate tests", "systematic testing", "multi-lens tests", "test coverage", "add test coverage", or after implementation when comprehensive tests are needed. NOT for writing tests during TDD implementation (use /implement). NOT for running existing tests (just run them). |
Multi-Lens Test Design
Dispatch multiple independent agents with different test-design lenses to analyze the same code and generate comprehensive tests. Each lens applies a specific family of testing techniques and targets a specific class of defects. Cross-lens overlap is intentional — tests generated independently by different lenses catch defects that a single perspective misses.
The goal is not to write the tests a developer would write. The goal is to systematically cover defect classes that human intuition misses: boundary interactions, contract violations, error propagation gaps, invariant failures, state transition bugs, integration seam mismatches, concurrency races, regression traps, and injection vulnerabilities.
When to Use
- After implementing a feature, to add systematic coverage beyond the TDD tests
- When test coverage is low and you need comprehensive test generation
- Before a major refactoring, to lock in behavior with characterization tests
- When auditors or mutation testing reveal coverage gaps
- When the user asks for "thorough testing" or "systematic test coverage"
When NOT to Use
- During TDD implementation loops (use
/implement — write one test at a time)
- For a single focused test (just write it)
- To run or debug existing tests (use the test runner /
/solving-bugs)
- For UI/visual/accessibility testing (out of scope — not derivable from code analysis)
Oracle Tier System
Every assertion generated by every lens is classified by reliability. This is the single most important quality control mechanism.
| Tier | Name | Example | Reliability | Auto-accept? |
|---|
| 1 | Crash/no-crash | Code does not throw, does not segfault | ~99% | Yes |
| 2 | Type/category | Throws InvalidQuantityError, returns Result.ok | ~85% | Yes |
| 3 | Postcondition | output.count == input.count, sorted, non-empty | ~70% | Review flag |
| 4 | Exact value | calculate(5) == 120 | ~41% | Consensus |
Rules:
- Lenses MUST prefer Tier 1-2 assertions wherever possible
- Tier 3 assertions are acceptable when derived from code structure (guard clauses, enum definitions, protocol requirements)
- Tier 4 assertions require either (a) consensus from 2+ lenses producing the same value independently, or (b) a
// PROVISIONAL label
- The Merge Agent auto-accepts Tier 1-2, flags Tier 3 for review, and consensus-validates or labels Tier 4
Lens Catalog
9 lenses organized into 3 tiers. Tier 1 always dispatches. Tier 2 dispatches when activation signals are detected. Tier 3 dispatches conditionally based on project type or explicit request.
Tier 1 — Always Dispatch
Lens 1: Input Space Analyst
Generates boundary value, equivalence partition, and pairwise combination tests for function parameters.
| Attribute | Value |
|---|
| Techniques | BVA, equivalence partitioning, pairwise (IPOG for 3+ params), MC/DC for compound boolean guards |
| Oracle strategy | Tier 2 for invalid inputs, Tier 3 for valid inputs. Tier 4 only with consensus. |
| Output | Parameterized test tables with metadata (defect class, technique, confidence) |
| Activation signal | Any function with parameters |
| Cost | $0.44/module, $0.15/defect (cheapest) |
| Pitfall | Do not generate Tier 4 exact-value assertions without consensus. BVA values: 3-5 per param max. |
Lens 2: Contract Verifier
Tests preconditions, postconditions, and protocol/interface conformance derived from code structure.
| Attribute | Value |
|---|
| Techniques | Contract testing, decision tables, MC/DC on boolean guards, conformance testing |
| Oracle strategy | Tier 2 for precondition violations, Tier 3 for postconditions and state transitions |
| Output | Contract tests (precondition rejection + postcondition verification) + conformance tests per protocol |
| Activation signal | Public functions with guard clauses, doc comments, protocol/interface conformance, throws declarations |
| Cost | $0.53/module, $0.26/defect |
| Pitfall | Derive assertions from code structure (guard clauses, enum definitions), not inferred semantics. |
Planning integration: When a plan with acceptance criteria exists, the Contract Verifier receives those criteria as additional pre-implementation specs. Plan acceptance criteria become contract tests before any code is written.
Lens 3: Error Path Prover
Tests every error path: triggers, propagation chains, recovery behavior, and cleanup.
| Attribute | Value |
|---|
| Techniques | Error guessing (defect taxonomy), cause-effect tracing across call boundaries |
| Oracle strategy | Tier 1 (crash/no-crash) + Tier 2 (exception type). Oracle-safe. |
| Output | Error trigger tests + propagation chain tests + recovery/cleanup verification |
| Activation signal | Any function with throw/raise, try/catch, Result types, or error return patterns |
| Cost | $0.69/module, $0.35/defect |
| Pitfall | Must trace across file boundaries (callers, callees). Requires multi-file context in agent prompt. |
Lens 4: Property/Invariant Discoverer
Discovers algebraic properties, round-trip invariants, and metamorphic relations.
| Attribute | Value |
|---|
| Techniques | Property-based testing patterns, metamorphic testing |
| Oracle strategy | Tier 2 (round-trip, algebraic, relational). Oracle-safe. |
| Output | Property specifications with generators and shrink strategies |
| Activation signal | Functions with algebraic structure: encode/decode pairs, idempotent ops, commutative ops, sorted output |
| Cost | $0.59/module, $0.39/defect |
| Pitfall | Invalid properties produce false positives. Execute proposed properties as validation. |
Swift note: No mature PBT library targets Swift Testing. Use @Test(arguments:) with curated representative inputs as a PBT surrogate.
Tier 2 — Dispatch When Detected
Lens 5: State Machine Exerciser
Tests state transition validity, invalid transition rejection, and lifecycle completeness.
| Attribute | Value |
|---|
| Techniques | State transition testing (all-states, all-transitions coverage) |
| Oracle strategy | Tier 2 (exception for invalid) + Tier 3 (state assertion after transition) |
| Output | Transition table + test per valid transition + test per invalid transition + lifecycle test |
| Activation signal | Enum state fields, lifecycle methods (init/open/close/start/stop), boolean flag combinations |
| Cost | $0.53/module, $0.26/defect |
| Pitfall | May misidentify implicit state machines from boolean flags. Require explicit patterns. |
Lens 6: Integration Seam Tester
Tests I/O boundaries with mocked unit tests and integration contract tests. Includes a workflow submode that generates subcutaneous (full-stack-minus-UI) tests when route/endpoint definitions are detected.
| Attribute | Value |
|---|
| Techniques | Round-trip testing, contract testing, error simulation, schema compliance |
| Oracle strategy | Tier 2 (round-trip, type correctness) + Tier 1 (crash/no-crash on error injection) |
| Output | Layer 1: mocked unit tests. Layer 2: integration tests. Workflow submode: subcutaneous path tests |
| Activation signal | I/O imports (DB drivers, HTTP clients, filesystem, queues), serialization pairs |
| Cost | $0.69/module, $0.46/defect |
| Pitfall | DI-injected boundaries are hard to detect statically. Must check framework-specific DI patterns. |
Lens 7: Concurrency Stress Generator
Generates stress harnesses and race-detector configurations. Does NOT reason about interleavings — delegates that to tools (TSan, -race, Loom).
| Attribute | Value |
|---|
| Techniques | Stress testing (N concurrent invocations), race-detector integration, invariant assertions |
| Oracle strategy | Tier 1 (crash/no-crash) + Tier 2 (invariant: final count = expected) |
| Output | Stress harnesses + TSan/-race configuration + iteration count (10,000 for depth-2 bugs) |
| Activation signal | async/await, actors, locks/mutexes, shared mutable state, channels, atomic operations |
| Cost | $0.76/module, $0.76/defect (most expensive) |
| Pitfall | LLMs cannot reason about interleavings. Generate harnesses only; delegate analysis to tools. |
Tier 3 — Conditional
Lens 8: Regression Characterization
Locks in current behavior as change detectors. All assertions are provisional — superseded when a specification-based test covers the same behavior.
| Attribute | Value |
|---|
| Techniques | Characterization testing (Feathers), golden-master/snapshot |
| Oracle strategy | Tier 4 (exact value/snapshot). All assertions labeled provisional. |
| Output | Call-with-known-inputs tests, snapshot tests, annotated // CHARACTERIZATION |
| Activation signal | Low coverage + high complexity, OR explicit pre-refactoring request |
| Cost | $0.36/module, $0.24/defect (cheapest with Haiku model) |
| Pitfall | Locks in buggy behavior. Every test labeled provisional. Superseded by higher-tier tests. |
Model note: Use Haiku for characterization — it's mechanical work (call function, record output, assert).
Lens 9: Security Prober
Tests injection surfaces, auth boundaries, and adversarial inputs using OWASP taxonomy.
| Attribute | Value |
|---|
| Techniques | Error guessing (OWASP taxonomy), adversarial injection, auth bypass testing |
| Oracle strategy | Tier 1 (crash/no-crash) + Tier 2 (exception type for rejected input). Oracle-safe. |
| Output | Adversarial input tests + auth boundary tests + injection payload tests |
| Activation signal | User input entry points, auth/authz checks, injection surfaces (SQL, shell, path, HTML) |
| Cost | $0.60/module, $0.40/defect |
| Pitfall | Overlap with Input Space Analyst on extreme values. Security uses adversarial framing, not edges. |
Selection Heuristic
| Tier | Lenses | When |
|---|
| 1 | Input Space Analyst, Contract Verifier, Error Path Prover, Property Discoverer | Always |
| 2 | State Machine Exerciser | Enum state fields or lifecycle methods detected |
| 2 | Integration Seam Tester | I/O imports or serialization pairs detected |
| 2 | Concurrency Stress Generator | async/await, actors, locks, shared mutable state detected |
| 3 | Regression Characterization | Low coverage + high complexity, OR pre-refactoring, OR explicit request |
| 3 | Security Prober | User input entry points, auth checks, or injection surfaces detected |
Typical dispatch: 4-7 lenses. Tier 1 (4 lenses) is always active. Tier 2 adds 0-3 based on detection. Tier 3 only on explicit signal.
Dispatch Architecture
Six phases. Phase 1 is sequential. Phase 3 is parallel. All others are sequential.
Phase 1: Codebase Analysis (Sequential — Analysis Agent)
A single agent performs these steps in order:
-
Language detection. Read dependency manifests (Package.swift, package.json, Cargo.toml, go.mod, requirements.txt, pom.xml, CMakeLists.txt). Determines test framework, assertion style, file naming conventions.
-
Symbol indexing. Via LSP or AST tools (serena get_symbols_overview, find_symbol). Produces function signatures, type hierarchy, protocol/interface conformance map.
-
Dependency graph. Module imports. Identifies I/O boundaries using the 8-category taxonomy:
- Database (SQL, ORM, key-value)
- Network (HTTP client, gRPC, WebSocket)
- Filesystem (read/write, temp, config)
- Message queue (pub/sub, stream)
- External process (shell, subprocess)
- Clock/time (system clock, timers)
- Random (PRNG, UUID, crypto)
- Environment (env vars, config services)
-
Existing test inventory. Locate test files, extract tested functions, compute approximate coverage. Prevents lenses from regenerating existing coverage.
-
Architecture classification. Three-pass detection:
- Pass 1: Framework fingerprinting (30+ framework signatures)
- Pass 2: Directory structure analysis (src/domain, adapters/ports, routes/controllers)
- Pass 3: Code-level pattern confirmation (dependency injection, repository pattern, middleware chains)
- Output: primary architecture, secondary styles, confidence, evidence
-
Concurrency profile. Detect async/await, actors, locks, shared mutable state per language.
-
Lens activation. Tier 1: always. Tier 2: activate if corresponding signals found in steps 3, 5, 6. Tier 3: activate based on coverage data (step 4) and explicit user request.
Output: Dispatch Manifest (structured JSON).
Phase 2: Dispatch Manifest
{
"language": "swift",
"framework": "vapor",
"test_framework": "swift-testing",
"assertion_style": "#expect/#require",
"symbol_index": [
{ "name": "createUser", "signature": "(CreateUserRequest) throws -> User",
"file": "Sources/App/Controllers/UserController.swift",
"visibility": "internal", "complexity": 12 }
],
"architecture": {
"primary": "layered-mvc", "secondary": ["repository-pattern"],
"confidence": 0.85, "evidence": ["routes/ directory", "Controllers/ suffix", "Models/ directory"]
},
"io_boundaries": [
{ "id": "db-postgres", "category": "database", "subtype": "sql-orm",
"direction": "bidirectional",
"location": "Sources/App/Repositories/UserRepository.swift",
"mock_strategy": "protocol-based" }
],
"concurrency_profile": {
"patterns_detected": ["async-await", "actor"],
"primitives_used": ["Task", "AsyncStream"]
},
"existing_coverage": {
"tested_functions": ["createUser", "deleteUser"],
"approximate_coverage_pct": 35
},
"project_type": "server",
"lenses_to_dispatch": [
{ "id": 1, "tier": 1, "name": "Input Space Analyst" },
{ "id": 2, "tier": 1, "name": "Contract Verifier" },
{ "id": 3, "tier": 1, "name": "Error Path Prover" },
{ "id": 4, "tier": 1, "name": "Property Discoverer" },
{ "id": 6, "tier": 2, "name": "Integration Seam Tester" },
{ "id": 7, "tier": 2, "name": "Concurrency Stress Generator" }
]
}
Phase 3: Lens Dispatch (Parallel)
All activated lenses receive the manifest and run as independent background agents. Each lens:
- Reads the manifest to identify its target functions/modules
- Reads source files for those targets (and callers/callees for Error Path Prover)
- Applies its techniques to generate test specifications
- Emits compilable test code using the language templates
- Tags each test with metadata:
defect_class, oracle_tier, technique, confidence, source_lens
Each lens writes its output to .test-design/lens-{ID}-{slug}.md.
Phase 4: Merge/Deduplication (Merge Agent)
A single agent receives all lens outputs and applies these rules in order:
- Group by source function. Collect all tests targeting function F across lenses.
- Partition by defect class. Tests from different defect classes are NEVER deduplicated.
- Within same defect class, compare input vectors. Identical inputs with different assertions merge into one test with combined assertions.
- Prefer higher confidence/specificity.
throws InvalidQuantityError beats throws.
- Never deduplicate property tests. Stochastic overlap is by design.
- Mark characterization tests as provisional. Superseded when a specification-based test covers the same behavior.
- Select smoke tests. Choose the minimal subset covering critical paths (app startup, health check, auth) and tag them
@Tag("smoke") or equivalent.
Then validate:
- Compile all tests. Fix import errors using language template patterns. Tests that don't compile after one fix attempt are dropped with a warning.
- Execute against current code. Triage results:
- Tier 1-2 assertions that fail → likely real bug → flag for user review
- Tier 3 assertions that fail → review, may be wrong assertion or real bug
- Tier 4 assertions that fail → remove unless 2+ lenses agree on the value
- All-pass tests → accepted into final suite
Output: Final test files + merge report (what was deduplicated, what was dropped, what was flagged).
Phase 5: Mutation Feedback Loop (Optional, Recommended)
Run after Phase 4 when a mutation testing tool is available.
-
Run mutation tool with selective operators (ROR, AOR, NC, SDL, RV) and first-fail optimization:
- Swift: not yet available (skip)
- TypeScript: Stryker
- Python: mutmut
- Rust: cargo-mutants
- Go: gremlins / go-mutesting
- Java: PIT
- C++: mull
-
Classify surviving mutants:
- Tier A: Actionable (killable, test gap)
- Tier B: Equivalent (unkillable, same semantics — detected by TCE + LLM classification)
- Tier C: Redundant (killed by existing non-generated tests)
- Tier D: Stubborn (actionable but hard to kill)
-
Map Tier A/D mutants to lenses using operator-to-lens table:
- ROR (relational operator) → Input Space Analyst (boundary missed)
- AOR (arithmetic operator) → Property Discoverer (algebraic property missed)
- NC (negation) → Contract Verifier (guard condition missed)
- SDL (statement deletion) → Error Path Prover (cleanup/recovery path missed)
- RV (return value) → Contract Verifier (postcondition missed)
-
Re-dispatch targeted lenses with narrow context: specific function, mutant diff, directive.
-
Merge incremental tests. Re-run mutation on affected functions only.
-
Terminate when: score plateau < 2pp improvement, OR 3 iterations reached, OR actionable mutants exhausted, OR cost budget exceeded.
Total overhead: ~45% additional tokens over baseline (3 iterations at ~15% each). Cap at 2 re-dispatch attempts per mutant before marking "deferred."
Phase 6: Output
Write final test files to the project's test directory matching the existing test structure. Produce a summary report:
## Test Design Report
### Lenses Dispatched
- Input Space Analyst: 23 tests (14 boundary, 6 equivalence, 3 pairwise)
- Contract Verifier: 18 tests (11 precondition, 5 postcondition, 2 conformance)
- Error Path Prover: 12 tests (7 trigger, 3 propagation, 2 recovery)
- Property Discoverer: 8 tests (5 round-trip, 2 idempotency, 1 metamorphic)
- Integration Seam Tester: 6 tests (4 mocked, 2 contract)
### Merge Results
- Total generated: 67
- Deduplicated: 4 (same input vectors across Input Space + Contract)
- Dropped (compile failure): 2
- Dropped (Tier 4 disagreement): 1
- Final suite: 60 tests
### Oracle Tier Distribution
- Tier 1 (crash/no-crash): 18 (30%)
- Tier 2 (type/category): 24 (40%)
- Tier 3 (postcondition): 14 (23%)
- Tier 4 (exact value): 4 (7%) — all provisional or consensus-validated
### Mutation Score (if run)
- Before: 45%
- After: 78%
- Iterations: 2
- Surviving actionable: 3 (deferred — stubborn mutants in math utilities)
Process
Step 0: Check for Existing Analysis (Resumability)
ls [PROJECT]/.test-design/
If .test-design/ exists with lens output files, a previous session started. Resume:
- Read
.test-design/manifest.json for the dispatch manifest
- Check which
lens-*.md files already exist (completed agents)
- Dispatch only missing lenses
- Skip to Phase 4
Step 1: Run Analysis Agent
Create the .test-design/ directory and run the Analysis Agent (Phase 1). The agent writes .test-design/manifest.json.
Step 2: Dispatch Lens Agents
Read the manifest. For each lens in lenses_to_dispatch, launch a background agent with:
- The dispatch manifest (for project context)
- The lens-specific prompt (from Lens Agent Prompts below)
- The language template for this lens + language (from Language Templates)
Step 3: Monitor and Collect
Check .test-design/ for completed lens output files. Wait for ALL dispatched lenses to complete before proceeding.
Step 4: Run Merge Agent
Launch the Merge Agent (Phase 4). It reads all lens-*.md files, deduplicates, validates, and writes the final test files.
Step 5: Mutation Feedback (Optional)
If a mutation tool is available for the language, run Phase 5. Otherwise skip.
Step 6: Clean Up
Delete .test-design/ after the final test files are written — unless the user wants to keep the per-lens analysis.
Analysis Agent Prompt
You are analyzing a codebase to prepare a test design dispatch. Your output is a
structured Dispatch Manifest that lens agents will consume.
Project directory: [DIRECTORY]
Execute these steps IN ORDER. Each step depends on the previous.
1. LANGUAGE DETECTION
Read dependency manifests in the project root. Determine:
- Primary language and version
- Test framework (Swift Testing, Vitest, pytest, etc.)
- Assertion style (#expect, expect().toBe(), assert, etc.)
- Test file naming convention (*Tests.swift, *.test.ts, test_*.py, *_test.go, etc.)
2. SYMBOL INDEXING
Use serena's get_symbols_overview and find_symbol to build:
- All public/internal function signatures with parameter types and return types
- Type hierarchy (classes, structs, enums, protocols/interfaces)
- Protocol/interface conformance map
- Per-function cyclomatic complexity estimate (count branches: if/guard/switch/for/while/catch)
3. I/O BOUNDARY DETECTION
Scan imports and function bodies for these 8 categories:
- Database: SQL drivers, ORM imports, key-value store clients
- Network: HTTP client libraries, gRPC, WebSocket
- Filesystem: file read/write, temp files, config files
- Message queue: pub/sub, streams, event buses
- External process: shell exec, subprocess, system()
- Clock/time: Date(), now(), timers, schedulers
- Random: PRNG, UUID, crypto random
- Environment: env vars, config services, feature flags
For each boundary, record: category, subtype, direction, file:line, mock strategy
(protocol-based, module mock, DI injection, test double).
4. EXISTING TEST INVENTORY
Find all test files. For each:
- Which source functions are tested (by import + call analysis)
- Approximate coverage (tested functions / total functions per module)
Produce a set of already-tested function names to exclude from lens targeting.
5. ARCHITECTURE CLASSIFICATION
Three passes:
- Pass 1: Framework fingerprinting. Check for 30+ known frameworks
(Express, Vapor, Django, Rails, Spring Boot, ASP.NET, Actix, Gin, etc.)
- Pass 2: Directory structure. Look for patterns:
src/domain + src/adapters → hexagonal
controllers/ + models/ + views/ → MVC
routes/ + middleware/ → layered server
lib/ only → library
cmd/ + internal/ → Go CLI/server
- Pass 3: Code-level. Check for DI containers, repository pattern,
middleware chains, event handlers, actor systems.
Output: { primary, secondary[], confidence, evidence[] }
6. CONCURRENCY PROFILE
Detect per-language concurrency primitives:
- Swift: async/await, actors, Task, TaskGroup, AsyncStream, os_unfair_lock
- TypeScript: Promise, async/await, Worker threads, SharedArrayBuffer
- Python: asyncio, threading, multiprocessing, concurrent.futures
- Rust: async/await, tokio, std::thread, Arc<Mutex<>>, channels
- Go: goroutines, channels, sync.Mutex, sync.WaitGroup, select
- Java: CompletableFuture, ExecutorService, synchronized, ReentrantLock
- C++: std::thread, std::mutex, std::async, std::atomic
7. LENS ACTIVATION
- Tier 1 (lenses 1-4): ALWAYS activate
- Lens 5 (State Machine): activate if enum state fields or lifecycle methods found
- Lens 6 (Integration Seam): activate if any I/O boundary detected in step 3
- Lens 7 (Concurrency): activate if any concurrency primitive detected in step 6
- Lens 8 (Regression Characterization): activate if coverage < 40% AND max complexity > 15,
OR if user explicitly requests pre-refactoring characterization
- Lens 9 (Security): activate if user input entry points or auth checks detected
OUTPUT: Write the complete Dispatch Manifest as JSON to [PROJECT]/.test-design/manifest.json
using the schema documented in this skill. The manifest's existence signals completion.
Lens Agent Prompts
Each lens agent receives: (1) the dispatch manifest, (2) its lens-specific prompt below, (3) the language template for its lens.
Lens 1: Input Space Analyst
You are a test designer applying the INPUT SPACE ANALYSIS lens.
Dispatch manifest: [MANIFEST]
For every function in the symbol index that is NOT in existing_coverage.tested_functions:
1. IDENTIFY PARAMETERS
List every parameter with its type, valid range, and domain constraints.
2. BOUNDARY VALUE ANALYSIS
For each numeric/string/collection parameter:
- Minimum valid, minimum valid - 1, minimum valid + 1
- Maximum valid, maximum valid - 1, maximum valid + 1
- Zero / empty / nil where applicable
- Type boundary (Int.max, Int.min, empty string, single char, max length)
3. EQUIVALENCE PARTITIONING
Partition each parameter's domain into equivalence classes:
- Valid partitions (at least 2 where distinct behavior exists)
- Invalid partitions (at least 1 per rejection reason)
Pick one representative value per partition.
4. PAIRWISE COMBINATION (3+ parameters only)
If a function has 3+ parameters, use the IPOG algorithm mentally:
- Take BVA representatives (3-5 per param MAX)
- Generate a covering array for all 2-way interactions
- Add constraint handling: mark impossible combinations as SKIP
5. MC/DC (compound boolean guards only)
If a guard has multiple conditions (a && b || c):
- Generate the MC/DC truth table
- One test case per condition showing independent effect
6. GENERATE TESTS
Use the parameterized test pattern for [LANGUAGE]:
[INSERT LANGUAGE TEMPLATE HERE]
Tag each test case:
- defect_class: "boundary" | "equivalence" | "interaction" | "condition"
- oracle_tier: 1 | 2 | 3 | 4
- technique: "bva" | "ep" | "pairwise" | "mcdc"
- confidence: 0.0-1.0
Oracle rules:
- Invalid inputs → assert exception TYPE (Tier 2), not message
- Valid inputs → assert structural postcondition (Tier 3) if derivable
- Only assert exact return values (Tier 4) if the function is a pure
mathematical computation with a known formula
OUTPUT: Write to [PROJECT]/.test-design/lens-1-input-space.md
Lens 2: Contract Verifier
You are a test designer applying the CONTRACT VERIFICATION lens.
Dispatch manifest: [MANIFEST]
For every function with guard clauses, preconditions, doc comments specifying
behavior, protocol/interface conformance, or throws declarations:
1. EXTRACT CONTRACTS
- Preconditions: guard/if-throw/require at function entry. Each is a contract.
- Postconditions: what the function guarantees on success (return type, state change,
side effect). Derive from code structure, not speculation.
- Invariants: class/struct properties that must hold before and after every method.
- Protocol conformance: for each protocol/interface, what does the contract require?
2. GENERATE PRECONDITION REJECTION TESTS
For each precondition, generate:
- Input that violates EXACTLY this precondition (and satisfies all others)
- Assert: correct exception TYPE thrown (Tier 2)
- One test per precondition per violation mode
3. GENERATE POSTCONDITION VERIFICATION TESTS
For each postcondition:
- Input that satisfies all preconditions
- Assert: postcondition holds (Tier 3 — structural check, not exact value)
4. GENERATE CONFORMANCE TESTS
For each protocol/interface the type conforms to:
- Test the semantic contract of that protocol (e.g., Equatable means a == a,
a == b implies b == a, a == b && b == c implies a == c)
- Use the type's actual implementation, not mocks
5. DECISION TABLE (compound conditions)
If a function's behavior varies based on multiple conditions:
- Build a decision table: conditions as columns, actions as rows
- Generate one test per rule (unique condition combination)
Tag each test: defect_class, oracle_tier, technique, confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-2-contract.md
Lens 3: Error Path Prover
You are a test designer applying the ERROR PATH PROVING lens.
Dispatch manifest: [MANIFEST]
For every function that throws, catches, returns Result/Optional, or has error
return patterns:
1. MAP ERROR PATHS
For this function and its callees (read caller/callee files):
- Every throw/raise statement: what triggers it, what error type
- Every catch block: what it catches, how it handles it (rethrow, wrap, swallow, recover)
- Every error return: what condition produces it
2. CLASSIFY BY DEFECT CLASS
- Trigger: does the function correctly detect the error condition?
- Propagation: does the error reach the right handler with the right type?
- Recovery: after handling, is the system in a valid state?
- Cleanup: are resources released on error paths? (files closed, locks released, transactions rolled back)
3. GENERATE ERROR TRIGGER TESTS
For each error condition:
- Construct input that triggers EXACTLY this error
- Assert: correct error type (Tier 2)
- Assert: no crash (Tier 1)
4. GENERATE PROPAGATION CHAIN TESTS
For error paths that cross function/file boundaries:
- Call the outer function with input that causes the inner function to throw
- Assert: the outer function either (a) propagates the same error type, or
(b) wraps it in its own error type — whichever the code does
5. GENERATE RECOVERY/CLEANUP TESTS
For error paths with cleanup logic:
- Trigger the error
- Assert: cleanup ran (resource released, state restored)
- Use mock/spy if needed to verify side effects
IMPORTANT: This lens MUST read files beyond the target function. Follow the call
graph to trace error propagation. The manifest's symbol_index shows which functions
call which. Read at least one level of callees.
Tag each test: defect_class, oracle_tier, technique, confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-3-error-path.md
Lens 4: Property/Invariant Discoverer
You are a test designer applying the PROPERTY/INVARIANT DISCOVERY lens.
Dispatch manifest: [MANIFEST]
For every function in the symbol index, check for algebraic properties:
1. DETECT PROPERTY CANDIDATES
- Round-trip: encode/decode, serialize/deserialize, parse/format, compress/decompress
- Idempotency: f(f(x)) == f(x) — sort, normalize, validate, format, deduplicate
- Commutativity: f(a, b) == f(b, a) — set operations, merge
- Associativity: f(f(a, b), c) == f(a, f(b, c)) — concatenation, merge
- Monotonicity: a <= b implies f(a) <= f(b) — scoring, ranking
- Metamorphic: f(transform(x)) relates to f(x) — e.g., reversing a sort input
doesn't change the output set, adding an element increases count by 1
- Invariant: property that holds for ALL valid inputs — non-negative length,
output is sorted, result is within bounds
2. FORMULATE PROPERTIES
For each detected candidate, write the property as a universally quantified statement:
"For all valid x: property(f(x))"
3. GENERATE PROPERTY TESTS
Language-specific approach:
- Languages with PBT (TS/fast-check, Python/Hypothesis, Rust/proptest, Go/rapid,
Java/jqwik, C++/RapidCheck): generate proper PBT tests with generators + shrink
- Swift: use @Test(arguments:) with curated representative inputs covering:
empty, single, typical, large, boundary, negative/adversarial
4. VALIDATE PROPERTIES
Before emitting, mentally check: could this property be FALSE for valid input?
If yes, it's not a true property — discard it or narrow the domain.
5. METAMORPHIC RELATIONS
For functions without obvious algebraic structure:
- Can you relate f(modified_input) to f(original_input)?
- Example: adding an item to a cart increases total; removing it decreases total
- These are oracle-safe (Tier 2) — they don't need to know the exact value
NEVER deduplicate property tests during merge — stochastic overlap is by design.
Tag each test: defect_class: "invariant" | "algebraic" | "metamorphic",
oracle_tier (almost always 2), technique, confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-4-property.md
Lens 5: State Machine Exerciser
You are a test designer applying the STATE MACHINE EXERCISER lens.
Dispatch manifest: [MANIFEST]
ONLY run if the manifest shows enum state fields, lifecycle methods, or boolean
flag combinations.
1. IDENTIFY STATE MACHINES
- Explicit: enum State { idle, running, paused, stopped }
- Lifecycle: init() → open() → process() → close()
- Boolean flags: (isConnected, isAuthenticated, isReady) — treat as implicit state
2. BUILD TRANSITION TABLE
For each state machine:
| Current State | Event/Action | Next State | Guard Condition |
List every transition the code permits.
3. IDENTIFY INVALID TRANSITIONS
Every (state, event) pair NOT in the table is an invalid transition.
Group by expected behavior: throws, returns error, no-op, ignored.
4. GENERATE VALID TRANSITION TESTS
For each row in the transition table:
- Set up the object in Current State
- Trigger Event/Action
- Assert: object is in Next State (Tier 3)
- If the transition has side effects, assert those too
5. GENERATE INVALID TRANSITION TESTS
For a representative sample of invalid transitions:
- Set up the object in Current State
- Trigger the invalid Event/Action
- Assert: correct rejection (Tier 2 — exception type or error return)
- Assert: state unchanged (Tier 3)
6. GENERATE LIFECYCLE TESTS
- Full happy path: create → use → destroy
- Early termination: create → destroy (skipping use)
- Double-close / double-dispose: verify no crash (Tier 1)
- Use-after-close: verify correct rejection (Tier 2)
Tag each test: defect_class: "transition" | "lifecycle" | "invalid-state",
oracle_tier, technique: "state-transition", confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-5-state-machine.md
Lens 6: Integration Seam Tester
You are a test designer applying the INTEGRATION SEAM TESTING lens.
Dispatch manifest: [MANIFEST]
ONLY run if io_boundaries in the manifest is non-empty.
For each I/O boundary in the manifest:
1. LAYER 1: MOCKED UNIT TESTS
- Create a test double for the boundary using the mock_strategy from the manifest
(protocol-based, module mock, DI injection, test double)
- Test the code that USES the boundary, with the boundary mocked
- Assert: correct calls made to the boundary (method, arguments)
- Assert: correct handling of boundary responses (success, error, empty, timeout)
- Oracle: Tier 2 (type checks) + Tier 3 (behavioral postconditions)
2. LAYER 2: INTEGRATION CONTRACT TESTS
- Test the boundary adapter/client itself against a real or test instance
- For database: test with in-memory DB or test container
- For HTTP: test with recorded responses (VCR pattern) or test server
- For filesystem: test with temp directory
- Assert: round-trip correctness (write then read back = same data, Tier 2)
- Assert: error handling (connection refused, timeout, malformed response)
3. WORKFLOW SUBMODE (if route/endpoint definitions detected)
Generate subcutaneous tests — full request path minus UI:
- Construct a request object directly
- Call the route handler / controller
- Assert: response status code (Tier 2)
- Assert: response shape (required fields present, Tier 3)
- Include: happy path, validation error, auth error, not-found
4. MOCK STRATEGY BY LANGUAGE
- Swift: protocol conformance (define protocol, implement test double)
- Go: interface implementation (define interface, implement fake)
- TypeScript: vi.mock() or manual mock
- Python: unittest.mock.patch or manual fake
- Rust: trait implementation or mockall
- Java: Mockito or manual implementation
- C++: virtual interface + manual implementation or GMock
Tag each test: defect_class: "seam" | "contract" | "workflow",
oracle_tier, technique: "mock" | "integration" | "subcutaneous", confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-6-integration-seam.md
Lens 7: Concurrency Stress Generator
You are a test designer applying the CONCURRENCY STRESS GENERATION lens.
Dispatch manifest: [MANIFEST]
ONLY run if concurrency_profile shows detected patterns.
CRITICAL: You CANNOT reason about interleavings. You generate HARNESSES that
expose races to runtime tools (TSan, -race flag, sanitizers). You do not predict
which interleavings fail.
1. IDENTIFY SHARED MUTABLE STATE
Find variables/properties accessed from multiple concurrent contexts:
- Class/struct properties mutated in async methods
- Global/static mutable state
- Collections modified concurrently
- Counters, flags, caches shared across tasks/threads
2. GENERATE STRESS HARNESSES
For each shared mutable state location:
- Launch N concurrent tasks/threads (N = 100 for quick, 10000 for thorough)
- Each task performs the concurrent operation
- Assert invariant AFTER all tasks complete:
counter == N, collection.count == N, no duplicates, etc. (Tier 2)
- Assert no crash during execution (Tier 1)
3. CONFIGURE RACE DETECTOR
Add instructions for running with race detection:
- Swift: TSAN_OPTIONS, -sanitize=thread
- Go: -race flag
- Rust: cargo test -- under Miri, or std::thread + careful assertion
- C++: -fsanitize=thread
- Java: -ea + Thread.UncaughtExceptionHandler
4. ITERATION COUNT
- Depth-1 bugs (single bad interleaving): 100 iterations usually sufficient
- Depth-2 bugs (two specific interleavings): ~10,000 iterations needed
(from PCT statistics: P(detect) = 1/n^(d-1) where n=threads, d=depth)
- Default to 10,000 with a comment explaining why
5. ACTOR ISOLATION TESTS (Swift-specific)
If actors detected:
- Test that actor-isolated properties cannot be accessed from outside
- Test that non-sendable types are not passed across actor boundaries
- These are compile-time checks but worth asserting behavior under load
DO NOT attempt to predict which interleavings cause failures.
DO NOT use sleep() or artificial delays to "trigger" races.
DO generate harnesses and let the tools find the bugs.
Tag each test: defect_class: "race" | "deadlock" | "ordering",
oracle_tier (almost always 1-2), technique: "stress" | "race-detection", confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-7-concurrency.md
Lens 8: Regression Characterization
You are a test designer applying the REGRESSION CHARACTERIZATION lens.
Dispatch manifest: [MANIFEST]
ONLY run if activated (low coverage + high complexity, or pre-refactoring).
MODEL: Use Haiku for this lens. The work is mechanical.
For every untested function with complexity > 10:
1. IDENTIFY REPRESENTATIVE INPUTS
- Read the function signature and body
- Construct 3-5 concrete inputs that exercise the main branches
- Include: happy path, edge case, error path
2. DETERMINE EXPECTED OUTPUT
- Read the code and TRACE execution for each input mentally
- Record the exact return value, thrown exception, or side effect
- If you cannot determine the output from reading: mark as NEEDS_EXECUTION
and the merge agent will execute to capture
3. GENERATE CHARACTERIZATION TESTS
For each (input, output) pair:
- Assert exact output (Tier 4) — this IS the point of characterization
- Add comment: // CHARACTERIZATION: locks current behavior, not correctness
- Add annotation: @Tag("characterization") or equivalent
4. SNAPSHOT TESTS
For functions returning complex objects:
- Serialize output to JSON/string
- Assert matches snapshot
- Add comment: // SNAPSHOT: update if behavior intentionally changes
EVERY test generated by this lens is PROVISIONAL. When the Merge Agent finds a
specification-based test (from lenses 1-4) covering the same function, the
characterization test is marked SUPERSEDED.
Tag each test: defect_class: "regression",
oracle_tier: 4, technique: "characterization" | "snapshot", confidence: 0.5.
OUTPUT: Write to [PROJECT]/.test-design/lens-8-characterization.md
Lens 9: Security Prober
You are a test designer applying the SECURITY PROBING lens.
Dispatch manifest: [MANIFEST]
ONLY run if user input entry points, auth checks, or injection surfaces detected.
1. IDENTIFY ATTACK SURFACES
From the manifest and source code:
- User input entry points (form data, query params, headers, request body)
- Authentication boundaries (login, token validation, session check)
- Authorization boundaries (role checks, permission guards, resource ownership)
- Injection surfaces: string interpolation into SQL, shell, HTML, paths, regex
2. INJECTION TESTS (per OWASP taxonomy)
For each injection surface:
- SQL: ' OR 1=1 --, Robert'); DROP TABLE users;--, UNION SELECT
- Shell: ; rm -rf /, $(command), `command`, | cat /etc/passwd
- Path: ../../etc/passwd, %2e%2e%2f, /absolute/path, null bytes
- HTML/XSS: <script>alert(1)</script>, javascript:void(0), event handlers
- Regex: (a+)+ (ReDoS), catastrophic backtracking patterns
Assert: input is REJECTED or SANITIZED (Tier 2 — exception or validation error)
Assert: no crash with malicious input (Tier 1)
3. AUTH BOUNDARY TESTS
For each protected endpoint/function:
- Unauthenticated access → assert rejection (Tier 2)
- Wrong role/permission → assert rejection (Tier 2)
- Expired token → assert rejection (Tier 2)
- Malformed token → assert rejection (Tier 2)
4. AUTHORIZATION TESTS
For each resource-specific access control:
- User A's resource accessed by User B → assert rejection
- Admin accessing user resource → assert allowed (or not, depending on design)
- Boundary: permission elevation (user becoming admin)
5. ADVERSARIAL FRAMING
This lens differs from Input Space Analyst: ISA tests edge cases (what if empty?);
Security Prober tests MALICIOUS input (what if crafted to exploit?). The intent is
different. Do not deduplicate with ISA even if some inputs look similar.
Tag each test: defect_class: "injection" | "auth-bypass" | "authz-bypass" | "dos",
oracle_tier (almost always 1-2), technique: "owasp" | "adversarial", confidence.
OUTPUT: Write to [PROJECT]/.test-design/lens-9-security.md
Merge Agent Prompt
You are the MERGE AGENT for the test design dispatch. Your job is to take the raw
output from all lens agents and produce a final, compilable, deduplicated test suite.
Project: [DIRECTORY]
Language: [LANGUAGE]
Test framework: [TEST_FRAMEWORK]
Lens outputs: [LIST OF .test-design/lens-*.md FILES]
## Step 1: Parse All Lens Outputs
Read every lens-*.md file. Extract:
- Test name, target function, input, assertion, metadata (defect_class, oracle_tier,
technique, confidence, source_lens)
## Step 2: Group by Source Function
For each function F, collect all tests from all lenses targeting F.
## Step 3: Deduplicate (5 rules, in order)
1. Tests from DIFFERENT defect classes → KEEP BOTH (never deduplicate across classes)
2. Same defect class, identical input vectors, different assertions → MERGE into one
test with all assertions combined
3. Same defect class, same input, same assertion → KEEP the one with higher confidence
4. When merging assertions, prefer higher specificity:
"throws InvalidQuantityError" beats "throws"
"count == 3" beats "count > 0"
5. NEVER deduplicate property tests (lens 4) — stochastic overlap is by design
6. Mark ALL lens 8 (characterization) tests as PROVISIONAL. If a lens 1-4 test covers
the same function with the same defect class, the characterization test is SUPERSEDED.
## Step 4: Select Smoke Tests
From the final suite, select the minimal subset covering:
- Application startup / initialization
- Health check / readiness probe
- Authentication flow (login → authenticated action)
- One representative from each critical business domain
Tag these with @Tag("smoke") or equivalent.
## Step 5: Compile Validation
Attempt to compile all tests.
- Fix import errors using the language template patterns
- Fix type errors if the fix is unambiguous (e.g., wrong integer width)
- Tests that don't compile after ONE fix attempt → DROP with warning in report
## Step 6: Execute Validation
Run all compiling tests against current code.
- Tier 1-2 assertions that FAIL → likely real bug → FLAG for user review
- Tier 3 assertions that FAIL → review: wrong assertion or real bug? → FLAG
- Tier 4 assertions that FAIL and 2+ lenses agree on the value → likely real bug → FLAG
- Tier 4 assertions that FAIL and only 1 lens asserts it → REMOVE (oracle unreliable)
- All tests that PASS → ACCEPTED into final suite
## Step 7: Write Output
1. Write final test files to the project's test directory, matching existing
test file organization
2. Write .test-design/merge-report.md with:
- Per-lens test counts
- Deduplication summary
- Dropped tests and reasons
- Flagged potential bugs
- Oracle tier distribution
- Smoke test selection
Delete individual lens files after merge.
Language Templates
For each language, lens agents MUST use the correct test scaffolding. Compilation failure from wrong imports, assertion syntax, or async handling is the #1 failure mode.
Swift (Swift Testing)
import Testing
@Suite struct [FunctionName]Tests {
@Test("description", arguments: [(input1, expected1), (input2, expected2)])
func testName(param: Type, expected: Type) {
#expect(SUT.function(param) == expected)
}
@Test func rejectsInvalidInput() {
#expect(throws: SpecificError.self) { try SUT.function(invalidInput) }
}
@Test func propagatesNetworkError() throws {
#expect(throws: AppError.networkFailure) { try SUT.function(triggerInput) }
}
@Test("round-trip", arguments: representativeInputs)
func roundTrip(input: Type) throws {
let encoded = try SUT.encode(input)
let decoded = try SUT.decode(encoded)
#expect(decoded == input)
}
@Test func concurrentAccess() async {
let sut = SUT()
await withTaskGroup(of: Void.self) { group in
for _ in 0..<10_000 {
group.addTask { await sut.increment() }
}
}
#expect(await sut.count == 10_000)
}
}
TypeScript (Vitest)
import { describe, test, expect } from 'vitest'
describe('FunctionName', () => {
test.each([
[input1, expected1],
[input2, expected2],
])('description %s → %s', (input, expected) => {
expect(functionName(input)).toBe(expected)
})
test('rejects invalid input', () => {
expect(() => functionName(invalidInput)).toThrow(SpecificError)
})
test('propagates network error', async () => {
await expect(functionName(triggerInput)).rejects.toThrow(AppError)
})
test('round-trip property', () => {
fc.assert(fc.property(fc.string(), (input) => {
expect(decode(encode(input))).toBe(input)
}))
})
})
Python (pytest)
import pytest
class TestFunctionName:
@pytest.mark.parametrize("input_val,expected", [
(input1, expected1),
(input2, expected2),
])
def test_boundary(self, input_val, expected):
assert function_name(input_val) == expected
def test_rejects_invalid(self):
with pytest.raises(SpecificError):
function_name(invalid_input)
@given(st.text())
def test_round_trip(self, input_val):
assert decode(encode(input_val)) == input_val
Rust
#[cfg(test)]
mod tests {
use super::*;
#[rstest]
#[case(input1, expected1)]
#[case(input2, expected2)]
fn test_boundary(#[case] input: Type, #[case] expected: Type) {
assert_eq!(function_name(input), expected);
}
#[test]
fn rejects_invalid() {
assert!(matches!(function_name(invalid), Err(SpecificError::Kind)));
}
proptest! {
#[test]
fn round_trip(input in any::<String>()) {
prop_assert_eq!(decode(&encode(&input)), input);
}
}
}
Go
func TestFunctionName(t *testing.T) {
tests := []struct {
name string
input Type
expected Type
}{
{"boundary min", input1, expected1},
{"boundary max", input2, expected2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := FunctionName(tt.input)
if got != tt.expected {
t.Errorf("FunctionName(%v) = %v, want %v", tt.input, got, tt.expected)
}
})
}
t.Run("rejects invalid", func(t *testing.T) {
_, err := FunctionName(invalidInput)
if !errors.Is(err, ErrSpecific) {
t.Errorf("expected ErrSpecific, got %v", err)
}
})
}
Java (JUnit 5 + AssertJ)
class FunctionNameTest {
@ParameterizedTest
@CsvSource({"input1, expected1", "input2, expected2"})
void testBoundary(Type input, Type expected) {
assertThat(functionName(input)).isEqualTo(expected);
}
@Test
void rejectsInvalid() {
assertThatThrownBy(() -> functionName(invalidInput))
.isInstanceOf(SpecificException.class);
}
@Property
void roundTrip(@ForAll String input) {
assertThat(decode(encode(input))).isEqualTo(input);
}
}
C++ (GoogleTest)
class BoundaryTest : public ::testing::TestWithParam<std::tuple<Type, Type>> {};
TEST_P(BoundaryTest, ChecksBoundary) {
auto [input, expected] = GetParam();
EXPECT_EQ(function_name(input), expected);
}
INSTANTIATE_TEST_SUITE_P(InputSpace, BoundaryTest,
::testing::Values(
std::make_tuple(input1, expected1),
std::make_tuple(input2, expected2)
));
TEST(ContractTest, RejectsInvalid) {
EXPECT_THROW(function_name(invalid_input), SpecificException);
}
Integration Points
| Skill | How dispatch-test-designers connects |
|---|
/implement | After implementation, dispatch this skill to add systematic multi-lens coverage beyond the TDD tests written during implementation. |
/programming | Programming writes code + basic tests. This skill adds comprehensive coverage as a post-implementation step. |
/plans (write-plan) | Plan acceptance criteria feed the Contract Verifier (lens 2) as pre-implementation specs. If a plan exists, inject its acceptance criteria into the Contract Verifier prompt. |
/dispatch-auditors | Auditor findings (e.g., "unhandled error path") can trigger targeted re-dispatch of specific lenses (e.g., Error Path Prover for the identified gap). |
/programming-swift | Shares Swift Testing patterns. This skill adds lenses that single-focus TDD misses. |
/dispatch-optimizers | Performance-critical code identified by optimizers may warrant Concurrency Stress Generator dispatch. |
Red Flags
| Thought | Reality |
|---|
| "4 lenses is enough" | 4 Tier 1 lenses is the minimum. Add Tier 2 when signals exist. |
| "I'll assert the exact return value" | Use Tier 1-2 oracles. Tier 4 only with consensus or provisional label. |
| "These two tests look the same, deduplicate" | Check defect classes first. Same input, different defect class = keep both. |
| "Property tests overlap with boundary tests" | They target different defect classes. Never deduplicate property tests. |
| "I can reason about which interleaving causes a bug" | No. Generate harnesses, let TSan/-race find bugs. You cannot predict interleavings. |
| "Characterization tests prove correctness" | They lock behavior, not prove correctness. Always label provisional. |
| "Skip compile validation, the templates are correct" | Templates reduce failures to near-zero, not zero. Always compile. |
| "One agent can do all 9 lenses" | Parallel dispatch with separate context produces higher quality per lens. |
| "Generate tests for all functions" | Skip already-tested functions. Check existing coverage first. |
| "The mutation loop is too expensive" | 45% overhead for 3 iterations. Without it, test quality is unmeasured. Worth it. |
Common Mistakes
| Mistake | Fix |
|---|
| Wrong test framework | ALWAYS check existing test files first. Match what's there. |
| Tests don't compile | Use language templates. Fix imports on first attempt. Drop after that. |
| Tier 4 assertions that fail on correct code | Remove single-lens Tier 4. Keep only if 2+ lenses agree. |
| Regenerating tests for already-covered code | Read existing tests in Phase 1. Exclude tested functions from lens targeting. |
| Mixing characterization with specification | Characterization = provisional. Specification = permanent. Never confuse them. |
| No metadata on generated tests | Every test MUST have defect_class, oracle_tier, technique, source_lens tags. |
| Dispatching security lens for internal code | Security Prober targets user-facing input surfaces, not internal APIs. |
| Skipping the merge phase | Raw lens output has ~15% redundancy. Merge is not optional. |