chore: add mutation testing with Stryker (per-package CI gates)#49
Open
iliassjabali wants to merge 28 commits intomainfrom
Open
chore: add mutation testing with Stryker (per-package CI gates)#49iliassjabali wants to merge 28 commits intomainfrom
iliassjabali wants to merge 28 commits intomainfrom
Conversation
Stryker CLI does not support nested flags like --thresholds.break or --vitest.configFile. Move all per-package overrides into stryker.config.mjs which reads STRYKER_PKG and STRYKER_BREAK env vars. The npm scripts set these env vars instead of passing CLI flags.
- docs/mutation-testing.md -> docs/guides/mutation-testing.md - docs/superpowers/specs/... -> docs/decisions/2026-04-12-mutation-testing.md - docs/superpowers/plans/... -> docs/decisions/2026-04-12-mutation-testing-plan.md - Remove docs/superpowers/ (skill framework artifact, not project structure)
Thresholds now live in the THRESHOLDS map in stryker.config.mjs. scripts/mutation.mjs is a thin wrapper that takes a package name from argv, sets STRYKER_PKG, and execs stryker run. Usage: pnpm mutation adapter-claude (or pnpm mutation for all).
Adds a coverage matrix job to ci.yml that runs vitest --coverage per package on PRs and uses vitest-coverage-report-action to post a sticky comment with overall %, per-file breakdown, and diff coverage on changed lines. Requires pull-requests: write permission.
Contributor
Coverage Report for sdk
File CoverageNo changed files found. |
Contributor
Coverage Report for adapter-claude
File CoverageNo changed files found. |
Contributor
Coverage Report for sidecar
File CoverageNo changed files found. |
Contributor
Coverage Report for cli
File Coverage
|
||||||||||||||||||||||||||||||||||||||
…mment Removes the separate coverage matrix from ci.yml. The mutation.yml workflow (renamed to Quality) now runs both vitest --coverage and Stryker per package in one job, then a final comment job posts a single sticky PR comment with a table of coverage % and mutation % per package.
Contributor
Quality Report
|
- Surviving mutants now appear as warning annotations inline on the PR diff, on the exact line Stryker mutated. - PR comment shows deltas vs main baseline (e.g. 54.3% (+2.1)). Baseline stored in quality-baseline.json, updated post-merge. - Auto-ratchet workflow runs on push to main. If a package's mutation score exceeds its threshold by >5 points, it opens a PR to bump the threshold in stryker.config.mjs.
There was a problem hiding this comment.
Pull request overview
Adds mutation testing (Stryker) and PR-facing quality reporting across the monorepo, with per-package configuration and CI workflows intended to annotate survivors and gate merges.
Changes:
- Introduces a root Stryker config + scripts to run mutation tests per package, annotate surviving mutants, and auto-ratchet thresholds.
- Adds CI workflows to run coverage + mutation per package and post a sticky “Quality Report” comment on PRs.
- Adds/standardizes Vitest configs for packages that lacked explicit configuration, plus docs and baseline score storage.
Reviewed changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
stryker.config.mjs |
Root Stryker config with per-package thresholds and Vitest runner wiring. |
scripts/mutation.mjs |
CLI wrapper to run mutation testing sequentially per package. |
scripts/auto-ratchet.mjs |
Parses reports and bumps break thresholds when scores improve enough. |
scripts/annotate-survivors.mjs |
Emits GitHub Actions warning annotations for surviving mutants. |
quality-baseline.json |
Stores baseline coverage/mutation scores used for deltas in PR comments. |
package.json |
Adds Stryker dependencies and a pnpm mutation script entrypoint. |
pnpm-lock.yaml |
Locks Stryker and transitive dependencies. |
packages/adapter-claude/vitest.config.ts |
Adds explicit Vitest config for adapter-claude. |
packages/cli/vitest.config.ts |
Adds explicit Vitest config for cli. |
packages/cli/vitest.stryker.config.ts |
Adjusts cli tests under Stryker (excludes cli.test.ts). |
packages/mcp-server/vitest.config.ts |
Adds explicit Vitest config for mcp-server (incl. timeout). |
docs/guides/mutation-testing.md |
Documents running mutation tests, CI behavior, and thresholds. |
.gitignore |
Ignores Stryker temp dir and mutation reports. |
.github/workflows/mutation.yml |
PR workflow for coverage + mutation runs, annotations, and PR comment. |
.github/workflows/auto-ratchet.yml |
Post-merge workflow to update baseline and open ratchet PRs. |
.github/workflows/ci.yml |
Minor comment punctuation change. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. mutation.yml: Stryker || true was swallowing the gate exit code. Now captures pass/fail, runs all post-steps with if: always(), then fails the job at the end via an 'Enforce mutation threshold' step. 2. auto-ratchet.yml: no longer pushes directly to main. Both baseline- only and threshold-bump updates now go through a PR. 3. auto-ratchet.yml: branch name includes short SHA to avoid collision when two merges happen on the same day. 4. auto-ratchet.yml: imports PACKAGES from stryker.config.mjs instead of duplicating the package list inline. 5. stryker.config.mjs: replaced em dash with double hyphen in comment.
1. mutation.yml: replace || true on coverage step with continue-on-error so test failures are visible in the UI but don't block post-steps. 2. mutation.yml: add issues: write permission for the PR comment API calls (listComments/createComment/updateComment). 3. mutation.yml: skip PR comment job on fork PRs where GITHUB_TOKEN is read-only (guard via head.repo.full_name == github.repository). 4. auto-ratchet.yml: merge with existing baseline instead of overwriting. Coverage fields are preserved from the previous baseline when no new coverage file is available (this workflow runs mutation, not coverage). 5. annotate-survivors.mjs: escape %, newlines, colons, and commas per GitHub Actions annotation spec to prevent truncated/broken output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds mutation testing and coverage reporting across all 5 packages, with three layers of feedback on every PR.
What you see on PRs
1. Quality Report comment -- one sticky comment showing coverage % and mutation score per package, with deltas vs main:
2. Inline annotations -- surviving mutants appear as warnings directly on the PR diff, on the exact line Stryker mutated. No need to download the HTML report for the common case.
3. Mutation gate -- merge is blocked if any package's mutation score drops below its threshold.
What happens post-merge
quality-baseline.jsonis updated with the latest scores (used for deltas on future PRs).What was added (no production code changes)
stryker.config.mjs-- single root config with per-package threshold mapscripts/mutation.mjs-- wrapper:pnpm mutation adapter-claudeorpnpm mutationfor allscripts/annotate-survivors.mjs-- emits GitHub warning annotations for surviving mutantsscripts/auto-ratchet.mjs-- bumps thresholds when scores improve by >5 pointsquality-baseline.json-- last-known scores on main.github/workflows/mutation.yml-- Quality workflow (coverage + mutation + PR comment).github/workflows/auto-ratchet.yml-- post-merge baseline update + threshold ratchetpackages/*/vitest.config.ts-- explicit configs for packages that lacked thempackages/cli/vitest.stryker.config.ts-- excludes cli.test.ts (spawns dist binary, can't see mutations)docs/guides/mutation-testing.md-- user guidePilot scores (local run on main)
Test plan