Repository navigation
[quality] Nightly mutation set omits src/plugins/tide.ts and src/utils/mergeQueue.ts, the code that performs the merge #650
Description
Finding
stryker.config.mjs / stryker.nightly.config.mjs mutate the merge-decision code (mergeGate.ts, labelMatch.ts, hold.ts, duration.ts), but the scheduled mutation job never runs a mutant of src/plugins/tide.ts (tryMergePullRequest, mergeOnce, explainForkWorkflows, the tideOn* event filters, loadTide/evaluate) or src/utils/mergeQueue.ts — the code that actually merges or enqueues a pull request. A behavioural regression there that line coverage still reaches would go unnoticed, which is the exact class the nightly gate exists for.
Evidence (main @ 424fb95, npx stryker run with mutate: ['src/plugins/tide.ts', 'src/utils/mergeQueue.ts'], same vitest.mutation.config.mjs, 26 min locally):
| File | Score | Killed | Survived | No coverage |
|---|---|---|---|---|
src/plugins/tide.ts |
95.30 % | 487 | 24 | 0 |
src/utils/mergeQueue.ts |
100 % | 109 | 0 | 0 |
Every one of the 24 survivors was applied at its exact span and read: 14 change only platform().log.debug/log.error text or a condition that selects a debug line, 2 change joiner strings in the fork-workflows comment, and 8 are equivalent (?? [] → placeholder filtered by workflowFiles; catch { return false } → catch {} read for truthiness; data.base.repo?. on a field GitHub always sends; action === undefined before a Set.has that is false for undefined; let hasOwners = false → true only read when tide.labels is unset, where it is reassigned; { required: false } → {}; lookup !== undefined where every empty-list caller passes lookup). So the files are clean today — the gap is that nothing would tell us when they stop being clean.
Unit coverage for both files is 100 % lines (test.yml run 41176427955 artifact coverage-424fb95…), and the bundle run reaches them too (coverage-e2e-424fb95…); this is not a coverage gap, it is a gate-scope gap.
Recommendation
Add both files to the nightly set only (stryker.nightly.config.mjs: mutate: [...base.mutate, 'src/plugins/tide.ts', 'src/utils/mergeQueue.ts']) and record the 24 reviewed survivors in the docs/contributing.md survivor table so the per-file tally can be read against them. Combined score at 424fb95 is (596+142)/(596+142+24+6) = 96.1 %, above the existing break: 95. The nightly job ran 148 mutants in 2.6 min after a 54 s dry run on GitHub-hosted runners (job of run 41176427955), so ~620 more fits well inside its 60-minute timeout; the local npm run test:mutation default set is left as is so a contributor's run stays ~12 minutes.
Decision taken by hive: nightly-only rather than the shared base set, because the base set's size is documented as a deliberate bound for local runs and the scheduled job is where the extra ~25 min is free.
Priority
- Impact: medium (merge executor is unguarded against silent assertion loss; no defect today)
- Effort: low
Filed by quality agent (hold-gated mode)
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5q9t | SHA: d6296b6
— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88
Activity