## 0. Pre-flight (non-negotiable) - [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each) - [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest. ## 1. Catalog and tooling setup - [ ] 1.1 Add `@stryker-mutator/core` and `@stryker-mutator/vitest-runner` to root `package.json` `catalogs.testing` (no bare versions per `openspec/AGENTS.md` §1) - [ ] 1.2 Run `bun install`; confirm lockfile updates cleanly - [ ] 1.3 Add `test:mutation` task to root `turbo.json` with `dependsOn: ["test:coverage"]` so 100% line coverage is a precondition - [ ] 1.4 Document the foundation-package set in `openspec/specs/testing/spec.md` once this change is folded; the same enumeration appears in the spec delta in this folder ## 2. Per-package Stryker configuration - [ ] 2.1 Author a reusable `stryker.base.conf.cjs` template under `tooling/stryker/` (path TBD) that every package extends — testRunner: `vitest`, reporters: `html, json, clear-text`, mutate exclusions per design D4 - [ ] 2.2 For every `@suaveplan/*` package, create `stryker.conf.json` at the package root extending the base template, declaring `mutate: ["src/**/*.ts"]` and the package-specific exclusions - [ ] 2.3 For every package, add a `test:mutation` script in `package.json` invoking Stryker against the package's `vitest.config.ts` ## 3. Threshold enforcement - [ ] 3.1 Encode the foundation-package list in the base config via a tiered `thresholds` block (≥ 85% for foundation, ≥ 70% otherwise) - [ ] 3.2 Wire a CI step that fails the build on `mutationScore` below the package's tier threshold - [ ] 3.3 Wire the PR-scoped mutation run via `--mutate` filtered to files changed in the PR (resolved through `git diff --name-only origin/main...HEAD`) ## 4. Reporter and artefacts - [ ] 4.1 Configure HTML + JSON reporters; archive both as CI artefacts per nightly run - [ ] 4.2 Author a `scripts/report-mutants.ts` that reads the JSON report and emits a Markdown table (path, line, mutator, status) for the PR comment - [ ] 4.3 Wire the PR-comment step to post the table via `gh pr comment` (or workflow equivalent) ## 5. Custom-mutator hook (extension point) - [ ] 5.1 If a custom Stryker mutator or runner adapter is required (e.g. once a bun-test runner exists), package it as `@suaveplan/testing-mutation` under `packages/testing/` - [ ] 5.2 Errors thrown by the adapter MUST extend `TestingError` with codes prefixed `TESTING.MUTATION.*` (e.g. `TESTING.MUTATION.RUNNER_FAILED`, `TESTING.MUTATION.CONFIG_INVALID`) - [ ] 5.3 The adapter package itself MUST meet 100% Istanbul coverage and pass the same mutation-score tier it advertises ## 6. Spec delta and validation - [ ] 6.1 `openspec validate enterprise-testing-mutation --type change` passes - [ ] 6.2 Fold `specs/testing/spec.md` ADDED Requirements into `openspec/specs/testing/spec.md` - [ ] 6.3 Move this change folder to `openspec/archive/-enterprise-testing-mutation/` ## 7. Rollout - [ ] 7.1 Run baseline mutation analysis across every existing genesis package; record current scores - [ ] 7.2 For each package below its tier threshold, file a triage issue listing surviving mutants by mutator class - [ ] 7.3 Land assertion-strengthening fixes per package until every existing package meets its tier threshold - [ ] 7.4 Flip the CI gate from advisory to enforcing once every package is green