Use mutation testing to ask whether tests detect plausible defects in production code. A passing suite or high coverage alone does not answer that question. Mutate production source; improve the tests that fail to detect meaningful mutations.
Choose the work
- Introduce Stryker: read setup.md, adapt the assets, and verify the integration with a small real module before widening the scope.
- Review or improve tests locally: read evaluate-tests.md. Run mutations, inspect the surviving changes and covering tests, then improve the missing inputs or assertions.
- Add CI: copy mutation-diff.yml to
.github/workflows/mutation-diff.yml, along with the selector scripts and package commands. CI must pass--diff-only.
Retain the project's runner, configuration, workspace layout and package manager. The supplied integration targets pnpm, Node.js 24+ and Vitest; use the appropriate Stryker runner for Jest/Mocha rather than migrating tests just to use these snippets. Match @stryker-mutator/core and runner versions and check their actual peer dependencies.
CI and local scope
| Context | Command | Meaning |
|---|---|---|
| PR CI | pnpm test:mutation:changed --base "$MUTATION_BASE" --diff-only |
Mutate changed production source relative to the merge-base; never expand to all source for test/config changes. |
| Preview locally | pnpm test:mutation:changed --base origin/main --diff-only --list |
Read-only selection, including local edits and untracked source. |
| Local uncommitted work | pnpm test:mutation:changed |
HEAD versus worktree; test/config/dependency changes widen to all configured source. |
| Review one module's tests | pnpm test:mutation --mutate 'src/fee.ts' |
Evaluate the full module, even if only tests changed. |
| Review the suite | pnpm test:mutation |
All configured production source; use when a full baseline is warranted. |
| AI-readable result | pnpm test:mutation:summary reports/mutation/changed.json |
Counts and concrete unresolved mutations with source locations and covering tests. |
For new or renamed files and hunks that reduce the line count, the selector conservatively inspects the whole changed file. Deleted files have no current code to mutate. Pure test/config/doc changes in CI produce scope: none: skipped, unmeasured. They do not establish test quality. Evaluate affected modules locally after changing tests or shared test helpers.
Run commands from the repository root. The supplied selector reads explicit positive/negative glob patterns from stryker.config.json; adapt includes/excludes to monorepos and exclude tests, declarations and generated code. It passes literal paths and ranges through Stryker's programmatic API, so commas in filenames are not split by a CLI parser. Missing history, invalid refs and unresolved conflicts fail instead of appearing as an empty diff.
Keep Git diff selection separate from Stryker's --incremental cache. The CI selector disables incremental reuse. During local test improvement, rerun the affected module with fresh results; if using incremental mode, use --force when validating changed assertions.
Local AI feedback loop
- Establish that the original tests pass. Select the behavioral module under review and run Stryker with HTML and JSON output. Record the source revision/worktree, scope, versions and any exclusions.
- Read the report, source, relevant tests and specification. For each meaningful
SurvivedorNoCoverageresult, identify the smallest legal input and observable difference between original and mutant. - Add a focused regression or a property using an independent oracle. The original must pass; the relevant mutant must fail. Improve assertions or input generation before changing production behavior. Use TDD for genuine production defects.
- Rerun the module and summarize the result. If a supplied score target is unmet, continue fixing demonstrated gaps until it is met or a concrete blocker requires clarification. Do not invent a score target.
See evaluate-tests.md for classification, property-based testing, and the evidence to report. Neither a mutation score of 100% nor a diff-only result proves the absence of bugs.
Interpretation
Stryker's score is (Killed + Timeout) / (Killed + Timeout + Survived + NoCoverage) * 100. Report timeouts separately; they can arise from an infinite-loop mutation or infrastructure problems. Compile/runtime errors, ignored mutants and pending mutants are also visible evidence, not successful assertion checks. The summary emits null when there are no valid mutants and marks pending results incomplete.
The config snippet starts with thresholds.break: 0 for measurement. CI records results but does not fail solely for survivors at this setting. If the project specifies a gate, set the agreed threshold and validate failure propagation; apply it to the selected diff scope. Keep ordinary tests/type checks in the project's existing CI.
Assets
| Asset | Destination / use |
|---|---|
| stryker.config.json | Root config; adapt mutate and vitest.configFile. |
| package.scripts.json | Merge commands into existing package.json; preserve unrelated fields. |
| justfile | Append/adapt recipes in the project's justfile. |
| gitignore.snippet | Append report/cache/temp paths to .gitignore. |
| mutation-plan.mjs | scripts/mutation-plan.mjs: pure selection and read-only Git inspection. |
| mutation-changed.mjs | scripts/mutation-changed.mjs: preview/run, fresh plan/report handling. |
| mutation-summary.mjs | scripts/mutation-summary.mjs: JSON evidence for AI review. |
| mutation-diff.yml | .github/workflows/mutation-diff.yml: PR/manual diff-only CI. |
| shipping.ts, shipping.test.ts | Optional isolated setup/PBT smoke example; adapt the specified domain rather than copying assertions into unrelated tests. |
| Vitest 5 patch | Version-specific fallback; read setup notes before using. |
The helper tests live in tests/mutation.test.mjs. In mizchi/skills, run just --justfile stryker.justfile test-stryker to validate them without installing Stryker.
Sources
- StrykerJS configuration: source patterns, ranges, reporters and thresholds.
- Vitest runner: peer requirements, configuration and related tests.
- Mutant states and metrics: scoring and outcome semantics.
- Incremental mode: reuse and forced reruns.
- Working template: Vite Plus, fast-check, shrinking and Uneffect experiments; formal verification is separate from mutation testing.