specweave 1.0.239 → 1.0.241

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/CLAUDE.md +31 -30
  2. package/README.md +1 -1
  3. package/bin/specweave.js +16 -0
  4. package/dist/plugins/specweave-ado/lib/ado-permission-gate.d.ts.map +1 -1
  5. package/dist/plugins/specweave-ado/lib/ado-permission-gate.js +17 -2
  6. package/dist/plugins/specweave-ado/lib/ado-permission-gate.js.map +1 -1
  7. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +7 -0
  8. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
  9. package/dist/plugins/specweave-github/lib/github-feature-sync.js +53 -0
  10. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
  11. package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts.map +1 -1
  12. package/dist/plugins/specweave-jira/lib/jira-permission-gate.js +17 -2
  13. package/dist/plugins/specweave-jira/lib/jira-permission-gate.js.map +1 -1
  14. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts +1 -0
  15. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts.map +1 -1
  16. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js +7 -3
  17. package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js.map +1 -1
  18. package/dist/plugins/specweave-testing/lib/playwright-cli-runner.d.ts.map +1 -1
  19. package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js +27 -19
  20. package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js.map +1 -1
  21. package/dist/plugins/specweave-testing/lib/playwright-routing.d.ts +8 -0
  22. package/dist/plugins/specweave-testing/lib/playwright-routing.d.ts.map +1 -1
  23. package/dist/plugins/specweave-testing/lib/playwright-routing.js +10 -7
  24. package/dist/plugins/specweave-testing/lib/playwright-routing.js.map +1 -1
  25. package/dist/src/adapters/agents-md-generator.js +1 -1
  26. package/dist/src/adapters/agents-md-generator.js.map +1 -1
  27. package/dist/src/adapters/claude/README.md +1 -1
  28. package/dist/src/adapters/claude-md-generator.js +1 -1
  29. package/dist/src/adapters/claude-md-generator.js.map +1 -1
  30. package/dist/src/cli/commands/init.d.ts.map +1 -1
  31. package/dist/src/cli/commands/init.js +10 -1
  32. package/dist/src/cli/commands/init.js.map +1 -1
  33. package/dist/src/cli/commands/refresh-marketplace.d.ts.map +1 -1
  34. package/dist/src/cli/commands/refresh-marketplace.js +7 -67
  35. package/dist/src/cli/commands/refresh-marketplace.js.map +1 -1
  36. package/dist/src/cli/commands/team.d.ts +20 -0
  37. package/dist/src/cli/commands/team.d.ts.map +1 -0
  38. package/dist/src/cli/commands/team.js +101 -0
  39. package/dist/src/cli/commands/team.js.map +1 -0
  40. package/dist/src/cli/helpers/init/claude-settings-env.d.ts +16 -0
  41. package/dist/src/cli/helpers/init/claude-settings-env.d.ts.map +1 -0
  42. package/dist/src/cli/helpers/init/claude-settings-env.js +44 -0
  43. package/dist/src/cli/helpers/init/claude-settings-env.js.map +1 -0
  44. package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -1
  45. package/dist/src/cli/helpers/init/plugin-installer.js +9 -13
  46. package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
  47. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  48. package/dist/src/cli/helpers/issue-tracker/index.js +12 -6
  49. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  50. package/dist/src/cli/helpers/issue-tracker/types.d.ts +2 -0
  51. package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
  52. package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
  53. package/dist/src/core/increment/discipline-checker.js +1 -1
  54. package/dist/src/core/increment/discipline-checker.js.map +1 -1
  55. package/dist/src/core/increment/status-commands.d.ts.map +1 -1
  56. package/dist/src/core/increment/status-commands.js +7 -0
  57. package/dist/src/core/increment/status-commands.js.map +1 -1
  58. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts +2 -2
  59. package/dist/src/core/lazy-loading/llm-plugin-detector.d.ts.map +1 -1
  60. package/dist/src/core/lazy-loading/llm-plugin-detector.js +63 -25
  61. package/dist/src/core/lazy-loading/llm-plugin-detector.js.map +1 -1
  62. package/dist/src/core/reflection/reflect-handler.js +2 -2
  63. package/dist/src/core/reflection/reflect-handler.js.map +1 -1
  64. package/dist/src/core/session/handoff-context.js +2 -2
  65. package/dist/src/core/session/handoff-context.js.map +1 -1
  66. package/dist/src/sync/ado-reconciler.d.ts.map +1 -1
  67. package/dist/src/sync/ado-reconciler.js +21 -2
  68. package/dist/src/sync/ado-reconciler.js.map +1 -1
  69. package/dist/src/sync/engine.d.ts.map +1 -1
  70. package/dist/src/sync/engine.js +2 -0
  71. package/dist/src/sync/engine.js.map +1 -1
  72. package/dist/src/sync/github-reconciler.d.ts.map +1 -1
  73. package/dist/src/sync/github-reconciler.js +52 -26
  74. package/dist/src/sync/github-reconciler.js.map +1 -1
  75. package/dist/src/sync/jira-reconciler.d.ts.map +1 -1
  76. package/dist/src/sync/jira-reconciler.js +16 -3
  77. package/dist/src/sync/jira-reconciler.js.map +1 -1
  78. package/dist/src/sync/providers/ado.d.ts.map +1 -1
  79. package/dist/src/sync/providers/ado.js +4 -2
  80. package/dist/src/sync/providers/ado.js.map +1 -1
  81. package/dist/src/sync/providers/github.d.ts.map +1 -1
  82. package/dist/src/sync/providers/github.js +11 -0
  83. package/dist/src/sync/providers/github.js.map +1 -1
  84. package/dist/src/sync/providers/jira.d.ts.map +1 -1
  85. package/dist/src/sync/providers/jira.js +14 -2
  86. package/dist/src/sync/providers/jira.js.map +1 -1
  87. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  88. package/dist/src/sync/sync-coordinator.js +31 -6
  89. package/dist/src/sync/sync-coordinator.js.map +1 -1
  90. package/dist/src/utils/auto-install.js +4 -4
  91. package/dist/src/utils/auto-install.js.map +1 -1
  92. package/package.json +2 -2
  93. package/plugins/FINAL-AUDIT-RECOMMENDATIONS.md +3 -3
  94. package/plugins/SKILLS-VS-AGENTS.md +1 -1
  95. package/plugins/specweave/PLUGIN.md +0 -2
  96. package/plugins/specweave/commands/export-skills.md +1 -1
  97. package/plugins/specweave/commands/role-orchestrator.md +1 -1
  98. package/plugins/specweave/hooks/log-decision.sh +6 -0
  99. package/plugins/specweave/hooks/stop-auto-v5.sh +17 -1
  100. package/plugins/specweave/hooks/stop-reflect.sh +16 -2
  101. package/plugins/specweave/hooks/stop-sync.sh +17 -9
  102. package/plugins/specweave/hooks/user-prompt-submit.sh +119 -35
  103. package/plugins/specweave/lib/vendor/sync/github-reconciler.js +52 -26
  104. package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
  105. package/plugins/specweave/scripts/read-grill-context.sh +149 -0
  106. package/plugins/specweave/skills/code-review/SKILL.md +608 -0
  107. package/plugins/specweave/skills/done/SKILL.md +1 -1
  108. package/plugins/specweave/skills/grill/SKILL.md +91 -0
  109. package/plugins/specweave/skills/performance/SKILL.md +6 -0
  110. package/plugins/specweave/skills/security/SKILL.md +7 -0
  111. package/plugins/specweave/skills/security-patterns/SKILL.md +6 -0
  112. package/plugins/specweave/skills/tdd-orchestrator/SKILL.md +1 -1
  113. package/plugins/specweave/skills/team-build/SKILL.md +1 -1
  114. package/plugins/specweave/skills/team-orchestrate/SKILL.md +1 -1
  115. package/plugins/specweave/skills/tech-lead/SKILL.md +7 -0
  116. package/plugins/specweave-ado/lib/ado-permission-gate.js +18 -2
  117. package/plugins/specweave-ado/lib/ado-permission-gate.ts +19 -2
  118. package/plugins/specweave-frontend/skills/frontend/SKILL.md +138 -2
  119. package/plugins/specweave-frontend/skills/i18n-expert/SKILL.md +989 -0
  120. package/plugins/specweave-github/hooks/github-auto-create-handler.sh +23 -1
  121. package/plugins/specweave-github/lib/github-feature-sync.js +41 -0
  122. package/plugins/specweave-github/lib/github-feature-sync.ts +62 -0
  123. package/plugins/specweave-infrastructure/PLUGIN.md +2 -1
  124. package/plugins/specweave-infrastructure/skills/gcp-deep-dive/SKILL.md +1172 -0
  125. package/plugins/specweave-infrastructure/skills/observability/SKILL.md +6 -0
  126. package/plugins/specweave-infrastructure/skills/opentelemetry/SKILL.md +6 -0
  127. package/plugins/specweave-jira/lib/jira-permission-gate.js +18 -2
  128. package/plugins/specweave-jira/lib/jira-permission-gate.ts +19 -2
  129. package/plugins/specweave-mobile/PLUGIN.md +1 -2
  130. package/plugins/specweave-mobile/README.md +13 -12
  131. package/plugins/specweave-mobile/skills/capacitor-ionic/SKILL.md +4 -18
  132. package/plugins/specweave-mobile/skills/deep-linking-push/SKILL.md +4 -22
  133. package/plugins/specweave-mobile/skills/expo/SKILL.md +4 -24
  134. package/plugins/specweave-mobile/skills/mobile-testing/SKILL.md +4 -22
  135. package/plugins/specweave-mobile/skills/react-native-expert/SKILL.md +404 -47
  136. package/plugins/specweave-testing/PLUGIN.md +3 -11
  137. package/plugins/specweave-testing/lib/playwright-cli-detector.js +8 -3
  138. package/plugins/specweave-testing/lib/playwright-cli-detector.ts +8 -3
  139. package/plugins/specweave-testing/lib/playwright-cli-runner.js +25 -20
  140. package/plugins/specweave-testing/lib/playwright-cli-runner.ts +24 -19
  141. package/plugins/specweave-testing/lib/playwright-routing.js +1 -6
  142. package/plugins/specweave-testing/lib/playwright-routing.ts +11 -8
  143. package/plugins/specweave-testing/skills/accessibility-testing/SKILL.md +998 -0
  144. package/plugins/specweave-testing/skills/e2e-testing/SKILL.md +29 -28
  145. package/plugins/specweave-testing/skills/mutation-testing/SKILL.md +769 -0
  146. package/plugins/specweave-testing/skills/performance-testing/SKILL.md +961 -0
  147. package/plugins/specweave-testing/skills/qa-engineer/SKILL.md +2 -0
  148. package/plugins/specweave/.specweave/logs/decisions.jsonl +0 -12
  149. package/plugins/specweave/.specweave/logs/reflect/reflect.log +0 -8
  150. package/plugins/specweave/.specweave/logs/stop-auto.log +0 -6
  151. package/plugins/specweave/.specweave/logs/stop-sync.log +0 -10
  152. package/plugins/specweave/.specweave/state/dashboard.json +0 -43
  153. package/plugins/specweave/skills/infrastructure/SKILL.md +0 -86
  154. package/plugins/specweave/skills/qa-lead/SKILL.md +0 -77
  155. package/plugins/specweave-mobile/skills/mobile-architect/SKILL.md +0 -30
  156. package/plugins/specweave-testing/commands/e2e-setup.md +0 -1103
  157. package/plugins/specweave-testing/commands/test-coverage.md +0 -983
  158. package/plugins/specweave-testing/commands/test-generate.md +0 -1160
  159. package/plugins/specweave-testing/commands/test-init.md +0 -413
  160. package/plugins/specweave-testing/commands/ui-automate.md +0 -182
  161. package/plugins/specweave-testing/commands/ui-inspect.md +0 -82
@@ -0,0 +1,769 @@
1
+ ---
2
+ description: Mutation testing expert using Stryker Mutator for TypeScript/JavaScript. Analyzes test suite effectiveness by injecting code mutations and measuring kill rates. Use for mutation testing setup, score analysis, survived mutant triage, CI integration, and improving test quality beyond code coverage.
3
+ allowed-tools: Read, Write, Edit, Bash
4
+ model: opus
5
+ context: fork
6
+ ---
7
+
8
+ # Mutation Testing Expert - Stryker Mutator
9
+
10
+ ## When to Use
11
+
12
+ Trigger this skill when the user mentions: "mutation testing", "Stryker", "test quality",
13
+ "mutation score", "survived mutants", "mutant", "test effectiveness", "kill rate",
14
+ "are my tests good enough", "test suite quality", "weak tests".
15
+
16
+ ## What Is Mutation Testing
17
+
18
+ Mutation testing measures the **effectiveness** of your test suite, not just what code it
19
+ executes. Code coverage tells you which lines ran; mutation testing tells you whether your
20
+ tests actually **detect bugs**.
21
+
22
+ The process:
23
+ 1. A tool (Stryker) injects small faults ("mutants") into your source code
24
+ 2. Your test suite runs against each mutant
25
+ 3. If a test fails, the mutant is **killed** (good -- your tests caught it)
26
+ 4. If all tests pass, the mutant **survived** (bad -- your tests missed a real bug)
27
+
28
+ **Mutation score** = killed mutants / total mutants x 100
29
+
30
+ ### Why It Matters
31
+
32
+ - **80% code coverage can miss 50% of bugs** -- coverage is necessary but not sufficient
33
+ - Mutation testing reveals **weak assertions**, missing edge cases, and untested branches
34
+ - It answers: "If a bug were introduced here, would my tests catch it?"
35
+
36
+ ## Stryker Mutator (Primary Tool)
37
+
38
+ ### Installation
39
+
40
+ ```bash
41
+ # Install Stryker for a TypeScript/JavaScript project
42
+ npm install --save-dev @stryker-mutator/core @stryker-mutator/typescript-checker @stryker-mutator/vitest-runner
43
+
44
+ # For Jest projects
45
+ npm install --save-dev @stryker-mutator/core @stryker-mutator/typescript-checker @stryker-mutator/jest-runner
46
+
47
+ # Initialize configuration (interactive)
48
+ npx stryker init
49
+ ```
50
+
51
+ ### Configuration: stryker.config.mjs
52
+
53
+ **Minimal configuration for a Vitest project:**
54
+
55
+ ```javascript
56
+ // stryker.config.mjs
57
+ /** @type {import('@stryker-mutator/api/core').PartialStrykerOptions} */
58
+ export default {
59
+ mutate: [
60
+ 'src/**/*.ts',
61
+ '!src/**/*.test.ts',
62
+ '!src/**/*.spec.ts',
63
+ '!src/**/*.d.ts',
64
+ '!src/**/index.ts',
65
+ ],
66
+ testRunner: 'vitest',
67
+ checkers: ['typescript'],
68
+ tsconfigFile: 'tsconfig.json',
69
+ reporters: ['html', 'clear-text', 'progress', 'dashboard'],
70
+ coverageAnalysis: 'perTest',
71
+ timeoutMS: 60000,
72
+ thresholds: {
73
+ high: 80,
74
+ low: 60,
75
+ break: null, // Set to a number to fail CI below this score
76
+ },
77
+ };
78
+ ```
79
+
80
+ **Jest configuration:**
81
+
82
+ ```javascript
83
+ // stryker.config.mjs
84
+ /** @type {import('@stryker-mutator/api/core').PartialStrykerOptions} */
85
+ export default {
86
+ mutate: [
87
+ 'src/**/*.ts',
88
+ '!src/**/*.test.ts',
89
+ '!src/**/*.spec.ts',
90
+ ],
91
+ testRunner: 'jest',
92
+ jest: {
93
+ configFile: 'jest.config.ts',
94
+ },
95
+ checkers: ['typescript'],
96
+ reporters: ['html', 'clear-text', 'progress'],
97
+ coverageAnalysis: 'perTest',
98
+ };
99
+ ```
100
+
101
+ **Targeted configuration for critical modules only:**
102
+
103
+ ```javascript
104
+ // stryker.config.mjs
105
+ export default {
106
+ mutate: [
107
+ // Only mutate business-critical code
108
+ 'src/services/payment/**/*.ts',
109
+ 'src/services/auth/**/*.ts',
110
+ 'src/utils/validators/**/*.ts',
111
+ 'src/core/parser/**/*.ts',
112
+ '!src/**/*.test.ts',
113
+ '!src/**/*.d.ts',
114
+ ],
115
+ testRunner: 'vitest',
116
+ checkers: ['typescript'],
117
+ reporters: ['html', 'clear-text', 'progress'],
118
+ coverageAnalysis: 'perTest',
119
+ timeoutMS: 120000,
120
+ concurrency: 4, // Limit parallel workers
121
+ };
122
+ ```
123
+
124
+ ### Running Stryker
125
+
126
+ ```bash
127
+ # Full mutation testing run
128
+ npx stryker run
129
+
130
+ # Target specific files
131
+ npx stryker run --mutate 'src/services/auth/**/*.ts'
132
+
133
+ # Incremental mode (only re-test changed code)
134
+ npx stryker run --incremental
135
+
136
+ # With specific log level for debugging
137
+ npx stryker run --logLevel debug
138
+ ```
139
+
140
+ ### Incremental Mode
141
+
142
+ Incremental mode is essential for fast feedback. It caches previous results and only
143
+ re-mutates files that changed since the last run.
144
+
145
+ ```javascript
146
+ // stryker.config.mjs
147
+ export default {
148
+ // ... other config
149
+ incremental: true,
150
+ incrementalFile: '.stryker-cache/incremental.json',
151
+ };
152
+ ```
153
+
154
+ ```bash
155
+ # First run: full analysis (slow)
156
+ npx stryker run
157
+
158
+ # Subsequent runs: only changed files (fast)
159
+ npx stryker run --incremental
160
+ ```
161
+
162
+ Add `.stryker-cache/` and `.stryker-tmp/` to `.gitignore`.
163
+
164
+ ## Mutation Operators
165
+
166
+ Stryker applies these mutation operators to your source code:
167
+
168
+ ### Arithmetic Mutations
169
+ | Original | Mutant |
170
+ |----------|--------|
171
+ | `a + b` | `a - b` |
172
+ | `a - b` | `a + b` |
173
+ | `a * b` | `a / b` |
174
+ | `a / b` | `a * b` |
175
+ | `a % b` | `a * b` |
176
+
177
+ ### Conditional Mutations
178
+ | Original | Mutant |
179
+ |----------|--------|
180
+ | `a > b` | `a >= b`, `a < b` |
181
+ | `a < b` | `a <= b`, `a > b` |
182
+ | `a >= b` | `a > b`, `a <= b` |
183
+ | `a <= b` | `a < b`, `a >= b` |
184
+ | `a === b` | `a !== b` |
185
+ | `a !== b` | `a === b` |
186
+
187
+ ### Logical Mutations
188
+ | Original | Mutant |
189
+ |----------|--------|
190
+ | `a && b` | `a \|\| b` |
191
+ | `a \|\| b` | `a && b` |
192
+ | `!a` | `a` |
193
+
194
+ ### String Mutations
195
+ | Original | Mutant |
196
+ |----------|--------|
197
+ | `"foo"` | `""` |
198
+ | `""` | `"Stryker was here!"` |
199
+ | `` `template ${x}` `` | `` `template` `` |
200
+
201
+ ### Array Mutations
202
+ | Original | Mutant |
203
+ |----------|--------|
204
+ | `[].push(x)` | removed |
205
+ | `[].pop()` | removed |
206
+ | `[].sort()` | removed |
207
+ | `[].filter(fn)` | `[]` |
208
+ | `[].map(fn)` | `[]` |
209
+
210
+ ### Unary Mutations
211
+ | Original | Mutant |
212
+ |----------|--------|
213
+ | `+a` | `-a` |
214
+ | `-a` | `+a` |
215
+ | `~a` | `a` |
216
+
217
+ ### Block Statement Mutations
218
+ | Original | Mutant |
219
+ |----------|--------|
220
+ | `if (cond) { block }` | `if (cond) { }` |
221
+ | `function() { body }` | `function() { }` |
222
+
223
+ ### Boolean Mutations
224
+ | Original | Mutant |
225
+ |----------|--------|
226
+ | `true` | `false` |
227
+ | `false` | `true` |
228
+
229
+ ### Optional Chaining
230
+ | Original | Mutant |
231
+ |----------|--------|
232
+ | `a?.b` | `a.b` |
233
+ | `a?.[b]` | `a[b]` |
234
+ | `a?.()` | `a()` |
235
+
236
+ ## Mutation Score Analysis
237
+
238
+ ### Understanding the Report
239
+
240
+ After running Stryker, examine the HTML report at `reports/mutation/html/index.html`.
241
+
242
+ **Score interpretation:**
243
+ - **80%+**: Strong test suite -- tests catch most injected faults
244
+ - **60-79%**: Adequate but has gaps -- review survived mutants
245
+ - **Below 60%**: Significant weaknesses -- tests give false confidence
246
+
247
+ **Mutant statuses:**
248
+ - **Killed**: A test failed when this mutant was active (good)
249
+ - **Survived**: All tests passed despite the mutation (bad -- add/fix tests)
250
+ - **No coverage**: No test executes this code at all (worst -- add tests)
251
+ - **Timeout**: Mutant caused an infinite loop (usually counts as killed)
252
+ - **Compile error**: Mutant produced invalid code (ignored)
253
+
254
+ ### Triaging Survived Mutants
255
+
256
+ When a mutant survives, ask these questions in order:
257
+
258
+ **1. Is it an equivalent mutant?**
259
+ An equivalent mutant changes the code but produces identical behavior. It cannot be
260
+ killed because no observable difference exists.
261
+
262
+ ```typescript
263
+ // Original
264
+ function getIndex(arr: string[], item: string): number {
265
+ return arr.indexOf(item);
266
+ }
267
+
268
+ // Equivalent mutant: changing >= 0 to > 0 in a caller
269
+ // If indexOf never returns 0 in practice, these are equivalent
270
+ if (getIndex(items, 'x') >= 0) { ... }
271
+ if (getIndex(items, 'x') > 0) { ... } // equivalent if 'x' is never first
272
+ ```
273
+
274
+ Mark equivalent mutants in Stryker config to exclude them from the score:
275
+
276
+ ```javascript
277
+ // stryker.config.mjs
278
+ export default {
279
+ mutate: [
280
+ 'src/**/*.ts',
281
+ // Exclude known equivalent mutant locations
282
+ '!src/utils/constants.ts',
283
+ ],
284
+ };
285
+ ```
286
+
287
+ **2. Is the assertion weak?**
288
+ The most common cause of survived mutants. The test runs the code but does not assert
289
+ on the affected value.
290
+
291
+ ```typescript
292
+ // Survived mutant: `price * quantity` -> `price / quantity`
293
+ function calculateTotal(price: number, quantity: number): number {
294
+ return price * quantity;
295
+ }
296
+
297
+ // WEAK test -- does not catch arithmetic mutation
298
+ it('should calculate total', () => {
299
+ const result = calculateTotal(10, 1); // 10 * 1 === 10 / 1
300
+ expect(result).toBe(10);
301
+ });
302
+
303
+ // STRONG test -- catches the mutation
304
+ it('should calculate total', () => {
305
+ const result = calculateTotal(10, 3); // 10 * 3 = 30, 10 / 3 = 3.33
306
+ expect(result).toBe(30);
307
+ });
308
+ ```
309
+
310
+ **3. Is the boundary untested?**
311
+
312
+ ```typescript
313
+ // Survived mutant: `age >= 18` -> `age > 18`
314
+ function canVote(age: number): boolean {
315
+ return age >= 18;
316
+ }
317
+
318
+ // Missing boundary test
319
+ it('should allow voting at 18', () => {
320
+ expect(canVote(18)).toBe(true); // Kills >= -> > mutant
321
+ expect(canVote(17)).toBe(false); // Kills >= -> <= mutant
322
+ });
323
+ ```
324
+
325
+ **4. Is the code actually untestable or dead?**
326
+ If no test can reach the mutated code, you have dead code. Remove it.
327
+
328
+ ### Score Improvement Strategies
329
+
330
+ **Priority 1: Fix "No coverage" mutants**
331
+ These indicate completely untested code. Write tests that at minimum execute these paths.
332
+
333
+ **Priority 2: Strengthen weak assertions**
334
+ Look for tests that execute the code but use loose assertions:
335
+ ```typescript
336
+ // Weak: only checks truthiness
337
+ expect(result).toBeTruthy();
338
+
339
+ // Strong: checks exact value
340
+ expect(result).toEqual({ total: 30, tax: 2.70 });
341
+ ```
342
+
343
+ **Priority 3: Add boundary tests**
344
+ For every comparison operator, test the exact boundary value:
345
+ ```typescript
346
+ describe('isAdult', () => {
347
+ it('should return true at exactly 18', () => expect(isAdult(18)).toBe(true));
348
+ it('should return false at 17', () => expect(isAdult(17)).toBe(false));
349
+ it('should return true above 18', () => expect(isAdult(25)).toBe(true));
350
+ });
351
+ ```
352
+
353
+ **Priority 4: Test negation and logical paths**
354
+ Ensure both branches of every `if/else` and `&&/||` are tested.
355
+
356
+ ## Integration Patterns
357
+
358
+ ### GitHub Actions Workflow
359
+
360
+ ```yaml
361
+ # .github/workflows/mutation-testing.yml
362
+ name: Mutation Testing
363
+
364
+ on:
365
+ pull_request:
366
+ branches: [main, develop]
367
+ # Optional: scheduled full run
368
+ schedule:
369
+ - cron: '0 3 * * 1' # Weekly Monday 3am
370
+
371
+ jobs:
372
+ mutation-test:
373
+ runs-on: ubuntu-latest
374
+ timeout-minutes: 60
375
+
376
+ steps:
377
+ - uses: actions/checkout@v4
378
+ with:
379
+ fetch-depth: 0 # Needed for incremental mode
380
+
381
+ - uses: actions/setup-node@v4
382
+ with:
383
+ node-version: 20
384
+ cache: 'npm'
385
+
386
+ - run: npm ci
387
+
388
+ - name: Restore Stryker cache
389
+ uses: actions/cache@v4
390
+ with:
391
+ path: .stryker-cache
392
+ key: stryker-${{ runner.os }}-${{ hashFiles('src/**/*.ts') }}
393
+ restore-keys: |
394
+ stryker-${{ runner.os }}-
395
+
396
+ - name: Run mutation testing (incremental)
397
+ run: npx stryker run --incremental
398
+
399
+ - name: Upload mutation report
400
+ if: always()
401
+ uses: actions/upload-artifact@v4
402
+ with:
403
+ name: mutation-report
404
+ path: reports/mutation/
405
+ retention-days: 14
406
+ ```
407
+
408
+ ### Incremental PR-Only Testing
409
+
410
+ For faster CI, only mutate files changed in the PR:
411
+
412
+ ```yaml
413
+ - name: Get changed files
414
+ id: changed
415
+ run: |
416
+ FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \
417
+ -- 'src/**/*.ts' \
418
+ | grep -v '\.test\.' \
419
+ | grep -v '\.spec\.' \
420
+ | grep -v '\.d\.ts' \
421
+ | tr '\n' ',' | sed 's/,$//')
422
+ echo "files=$FILES" >> $GITHUB_OUTPUT
423
+
424
+ - name: Run mutation testing on changed files
425
+ if: steps.changed.outputs.files != ''
426
+ run: |
427
+ npx stryker run --mutate "${{ steps.changed.outputs.files }}"
428
+ ```
429
+
430
+ ### Report Formats
431
+
432
+ Configure multiple reporters in `stryker.config.mjs`:
433
+
434
+ ```javascript
435
+ export default {
436
+ reporters: [
437
+ 'html', // Detailed HTML report in reports/mutation/html/
438
+ 'clear-text', // Terminal summary
439
+ 'progress', // Progress bar during execution
440
+ 'json', // Machine-readable JSON in reports/mutation/mutation.json
441
+ 'dashboard', // Stryker dashboard (stryker-mutator.io/dashboard)
442
+ ],
443
+ htmlReporter: {
444
+ fileName: 'reports/mutation/html/index.html',
445
+ },
446
+ jsonReporter: {
447
+ fileName: 'reports/mutation/mutation.json',
448
+ },
449
+ // Dashboard reporter config (requires STRYKER_DASHBOARD_API_KEY)
450
+ dashboard: {
451
+ project: 'github.com/your-org/your-repo',
452
+ version: 'main',
453
+ module: 'core',
454
+ },
455
+ };
456
+ ```
457
+
458
+ ### Setting a CI Quality Gate
459
+
460
+ Fail the build if mutation score drops below a threshold:
461
+
462
+ ```javascript
463
+ // stryker.config.mjs
464
+ export default {
465
+ thresholds: {
466
+ high: 80, // Green in report
467
+ low: 60, // Yellow in report
468
+ break: 60, // FAIL CI if score drops below this
469
+ },
470
+ };
471
+ ```
472
+
473
+ ### Integration with Existing Test Scripts
474
+
475
+ ```json
476
+ {
477
+ "scripts": {
478
+ "test": "vitest run",
479
+ "test:mutation": "stryker run",
480
+ "test:mutation:incremental": "stryker run --incremental",
481
+ "test:mutation:changed": "stryker run --incremental --since main",
482
+ "test:all": "npm run test && npm run test:mutation:incremental"
483
+ }
484
+ }
485
+ ```
486
+
487
+ ## Strategy and Best Practices
488
+
489
+ ### When Mutation Testing Is Valuable
490
+
491
+ **High value -- always mutate:**
492
+ - Payment/billing logic
493
+ - Authentication and authorization
494
+ - Data validation and sanitization
495
+ - Parsers and serializers
496
+ - State machines and workflow engines
497
+ - Cryptographic operations
498
+ - Rate limiters and throttlers
499
+ - Business rule engines
500
+
501
+ **Medium value -- mutate selectively:**
502
+ - API route handlers (focus on error handling)
503
+ - Database query builders (focus on WHERE clauses)
504
+ - Middleware chains (focus on short-circuit logic)
505
+
506
+ **Low value -- skip or defer:**
507
+ - UI components (visual testing is more effective)
508
+ - Configuration files and constants
509
+ - Auto-generated code (OpenAPI clients, Prisma, GraphQL codegen)
510
+ - Thin wrappers around third-party libraries
511
+ - Logging and telemetry code
512
+ - Migration scripts
513
+
514
+ ### Balancing Quality vs Execution Time
515
+
516
+ Mutation testing is CPU-intensive. A full run on a large codebase can take 30+ minutes.
517
+
518
+ **Strategies for speed:**
519
+
520
+ 1. **Incremental mode** (always use in development):
521
+ ```bash
522
+ npx stryker run --incremental
523
+ ```
524
+
525
+ 2. **Target critical modules only** -- do not mutate everything:
526
+ ```javascript
527
+ mutate: [
528
+ 'src/core/**/*.ts',
529
+ 'src/services/payment/**/*.ts',
530
+ '!src/**/*.test.ts',
531
+ ],
532
+ ```
533
+
534
+ 3. **Limit concurrency** to avoid OOM on CI:
535
+ ```javascript
536
+ concurrency: Math.max(1, require('os').cpus().length - 1),
537
+ ```
538
+
539
+ 4. **Use `perTest` coverage analysis** -- Stryker only runs tests that cover each mutant:
540
+ ```javascript
541
+ coverageAnalysis: 'perTest',
542
+ ```
543
+
544
+ 5. **Increase timeout for slow test suites**:
545
+ ```javascript
546
+ timeoutMS: 120000,
547
+ timeoutFactor: 2.5,
548
+ ```
549
+
550
+ 6. **Schedule full runs nightly, use incremental on PRs.**
551
+
552
+ ### Prioritizing Which Modules to Mutate
553
+
554
+ Use this decision matrix:
555
+
556
+ | Factor | Weight | Assessment |
557
+ |--------|--------|------------|
558
+ | Business criticality | High | Payment, auth, data integrity |
559
+ | Bug history | High | Files with frequent bug fixes |
560
+ | Complexity (cyclomatic) | Medium | High-complexity functions |
561
+ | Change frequency | Medium | Frequently modified files |
562
+ | Coverage gap | Medium | High line coverage but untested logic |
563
+ | User-facing impact | High | Features affecting end users directly |
564
+
565
+ Start with the intersection of "high business criticality" and "high code coverage" --
566
+ these are the modules where mutation testing adds the most value (you think they are
567
+ well-tested but may not be).
568
+
569
+ ### Common Pitfalls
570
+
571
+ 1. **Trying to reach 100% mutation score** -- diminishing returns past 80%. Some mutants
572
+ are equivalent or test trivial code.
573
+
574
+ 2. **Running mutation testing on the entire codebase at once** -- start with critical
575
+ modules and expand gradually.
576
+
577
+ 3. **Ignoring survived mutants without analysis** -- each survived mutant is a potential
578
+ bug your tests would miss. Triage them.
579
+
580
+ 4. **Not using incremental mode** -- full runs on every commit waste CI minutes.
581
+
582
+ 5. **Mutating generated code** -- exclude auto-generated files, they inflate the
583
+ denominator without adding value.
584
+
585
+ ## Disabling Specific Mutators
586
+
587
+ If certain mutation operators produce too many equivalent mutants for your codebase:
588
+
589
+ ```javascript
590
+ // stryker.config.mjs
591
+ export default {
592
+ // Disable specific mutators
593
+ mutator: {
594
+ excludedMutations: [
595
+ 'StringLiteral', // Skip string mutations if you have many display strings
596
+ 'ObjectLiteral', // Skip object literal removal
597
+ ],
598
+ },
599
+ };
600
+ ```
601
+
602
+ Available mutator group names:
603
+ - `ArithmeticOperator`, `ArrayDeclaration`, `AssignmentOperator`
604
+ - `BlockStatement`, `BooleanLiteral`, `ConditionalExpression`
605
+ - `EqualityOperator`, `LogicalOperator`, `MethodExpression`
606
+ - `ObjectLiteral`, `OptionalChaining`, `Regex`
607
+ - `StringLiteral`, `UnaryOperator`, `UpdateOperator`
608
+
609
+ ## Interpreting Results: Worked Example
610
+
611
+ Given this source file:
612
+
613
+ ```typescript
614
+ // src/services/discount.ts
615
+ export function applyDiscount(price: number, discountPct: number): number {
616
+ if (discountPct < 0 || discountPct > 100) {
617
+ throw new Error('Invalid discount percentage');
618
+ }
619
+ if (discountPct === 0) {
620
+ return price;
621
+ }
622
+ return price - (price * discountPct) / 100;
623
+ }
624
+ ```
625
+
626
+ And this test:
627
+
628
+ ```typescript
629
+ it('should apply 20% discount', () => {
630
+ expect(applyDiscount(100, 20)).toBe(80);
631
+ });
632
+
633
+ it('should reject negative discount', () => {
634
+ expect(() => applyDiscount(100, -5)).toThrow();
635
+ });
636
+ ```
637
+
638
+ **Stryker results:**
639
+ - `discountPct < 0` -> `discountPct <= 0`: **SURVIVED** (no test for `discountPct === 0` boundary)
640
+ - `discountPct > 100` -> `discountPct >= 100`: **SURVIVED** (no test for `discountPct === 100`)
641
+ - `price * discountPct` -> `price / discountPct`: **KILLED** by the 20% test
642
+ - `price - (...)` -> `price + (...)`: **KILLED** by the 20% test
643
+ - `discountPct === 0` -> `discountPct !== 0`: **SURVIVED** (no test for zero discount)
644
+
645
+ **Tests to add:**
646
+
647
+ ```typescript
648
+ it('should return full price for 0% discount', () => {
649
+ expect(applyDiscount(100, 0)).toBe(100);
650
+ });
651
+
652
+ it('should apply 100% discount', () => {
653
+ expect(applyDiscount(100, 100)).toBe(0);
654
+ });
655
+
656
+ it('should reject discount over 100', () => {
657
+ expect(() => applyDiscount(100, 101)).toThrow();
658
+ });
659
+
660
+ it('should reject exactly 0 boundary correctly', () => {
661
+ // This is valid -- 0% discount should NOT throw
662
+ expect(applyDiscount(50, 0)).toBe(50);
663
+ });
664
+ ```
665
+
666
+ ## Beyond Stryker: Other Ecosystems
667
+
668
+ ### Java: PIT (pitest)
669
+
670
+ ```xml
671
+ <!-- pom.xml -->
672
+ <plugin>
673
+ <groupId>org.pitest</groupId>
674
+ <artifactId>pitest-maven</artifactId>
675
+ <version>1.15.0</version>
676
+ <configuration>
677
+ <targetClasses>
678
+ <param>com.example.service.*</param>
679
+ </targetClasses>
680
+ <mutationThreshold>70</mutationThreshold>
681
+ </configuration>
682
+ </plugin>
683
+ ```
684
+
685
+ ```bash
686
+ mvn org.pitest:pitest-maven:mutationCoverage
687
+ ```
688
+
689
+ ### Python: mutmut
690
+
691
+ ```bash
692
+ pip install mutmut
693
+
694
+ # Run mutation testing
695
+ mutmut run --paths-to-mutate=src/
696
+
697
+ # View results
698
+ mutmut results
699
+ mutmut html # Generate HTML report
700
+ ```
701
+
702
+ ### Rust: cargo-mutants
703
+
704
+ ```bash
705
+ cargo install cargo-mutants
706
+
707
+ # Run mutation testing
708
+ cargo mutants
709
+
710
+ # Target specific modules
711
+ cargo mutants -- --package my-crate -f src/parser.rs
712
+
713
+ # Skip slow tests
714
+ cargo mutants --timeout 60
715
+ ```
716
+
717
+ ### When to Use Framework-Specific Tools
718
+
719
+ | Language | Tool | Best For |
720
+ |----------|------|----------|
721
+ | TypeScript/JavaScript | Stryker | Full ecosystem, Vitest/Jest/Mocha support |
722
+ | Java/Kotlin | PIT (pitest) | Maven/Gradle integration, fast bytecode mutation |
723
+ | Python | mutmut | Simple setup, good defaults |
724
+ | Rust | cargo-mutants | Cargo integration, type-system-aware mutations |
725
+ | C# | Stryker.NET | .NET ecosystem, NUnit/xUnit/MSTest |
726
+ | Scala | Stryker4s | sbt integration |
727
+
728
+ **General rule**: Use the tool native to your build ecosystem. Stryker covers JS/TS/.NET/Scala.
729
+ PIT dominates Java. mutmut and cargo-mutants are the pragmatic choices for Python and Rust.
730
+
731
+ ## Quick Reference
732
+
733
+ ### Setup Checklist
734
+
735
+ - [ ] Install Stryker and test-runner plugin
736
+ - [ ] Create `stryker.config.mjs` with targeted `mutate` globs
737
+ - [ ] Enable `coverageAnalysis: 'perTest'`
738
+ - [ ] Enable `incremental: true` for development
739
+ - [ ] Add `.stryker-cache/` and `.stryker-tmp/` to `.gitignore`
740
+ - [ ] Add `test:mutation` script to package.json
741
+ - [ ] Configure CI workflow (incremental on PRs, full weekly)
742
+ - [ ] Set `thresholds.break` for CI quality gate
743
+
744
+ ### Commands Cheat Sheet
745
+
746
+ ```bash
747
+ npx stryker init # Interactive setup
748
+ npx stryker run # Full run
749
+ npx stryker run --incremental # Incremental (fast)
750
+ npx stryker run --mutate 'src/core/**' # Target specific files
751
+ npx stryker run --logLevel debug # Debug output
752
+ npx stryker run --concurrency 2 # Limit parallelism
753
+ ```
754
+
755
+ ### Score Targets
756
+
757
+ | Context | Target Score |
758
+ |---------|-------------|
759
+ | Payment/auth/security code | 80%+ |
760
+ | Core business logic | 70%+ |
761
+ | Utility libraries | 60%+ |
762
+ | General application code | 50%+ |
763
+ | Initial adoption (baseline) | Any improvement |
764
+
765
+ ## Related Skills
766
+
767
+ - `unit-testing` - Write and improve the unit tests that mutation testing evaluates
768
+ - `qa-engineer` - Overall test strategy and quality metrics
769
+ - `e2e-testing` - E2E and visual regression tests