Repository navigation
fix(repo): pass a mutate set that matches no file with an empty report - #115
Merged
Merged
Conversation
…error reasons
An empty mutate set was refused at the instrument stage (exit 3), and an
empty run's NaN score was fed to the exit classifier (exit 1). Plan the
empty set like any other and treat a non-finite score as no score, so the
run writes an empty report and passes whatever thresholds.break says
Prepare, dry-run, and mutation-test stage errors now derive their message
from their reason instead of printing an empty one
Closes #114
…d of NaN
Metrics.mutationScore returned NaN when no mutant counted toward the score,
and every consumer had to remember to check for it. The score is now
Report.MutationScore, a tagged union of Scored { percentage } and Unscored,
so classify-exit, clear-text, merge-reports and the verdict envelope match
on the tag. InstrumentCommand counts are non-negative integers. Property
laws that pinned constants or echoed fields are replaced by laws over the
full score domain
BREAKING CHANGE: Report.Metrics#mutationScore and
mutationScoreBasedOnCoveredCode return Report.MutationScore, not number
Refs #114
determineExitCode checked the unscored case before the classifier, so the rule that an unscored run passes lived in two places. Every score now goes through classifyExit and only the log line depends on the score's tag
detected never exceeds counted for either score, so the clamp was dead and was the only rule on which Metrics and the merge-reports score differed
The unscored-row law derived its oracle from a hand-kept status table; it now asks Report.Metrics, so the merge score cannot drift from the report score unnoticed
Only a scored run can break a threshold, so determineExitCode now dispatches on the score tag and hands the logs a plain percentage; the service no longer renders n/a
The merge workflow cannot call Report.Metrics from its decide body, so the law now requires each merged row's score to equal the Report.Metrics score; a drift in either formula fails the suite
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
A
stryker runwhosemutatepatterns match no file now performs the dry run, writes a mutation report withfiles: {}, and exits 0 whateverthresholds.breaksays. Before, it stopped at the instrument stage with exit 3 and printedInstrumentError:with an empty message, which failed sharded CI jobs whose shard owned no file.Fixes #114
What changed for users
stryker merge-reports: a package whose report has no tested mutant showsn/ainstead of0.00.@systemfsoftware/stryker-js-plugin-interface(major):Report.Metrics#mutationScoreand#mutationScoreBasedOnCoveredCodereturnReport.MutationScore(Scored { percentage }orUnscored) instead of a number that wasNaNfor an empty run. The changeset shows theReport.MutationScore.matchmigration. The machine-mode verdict envelope keepsscore: number | null, andmutation.jsonnever carried metrics, so neither wire format changes.Why it takes this shape
The exit-code failure was not in the instrument stage alone. Once an empty set was admitted,
Metrics.mutationScorereturnedNaN, andClassifyExitCommand's schema rejected it (exit 1). Returning 0 would trip every configured break threshold, so the score became a union that cannot hold an invalid number. Every consumer (exit classifier, clear-text renderer, merge-reports, verdict envelope) now matches on the tag, andclassifyExitis the single place that decides the exit code.Verification
mutate: ["src/a.ts", "!src/a.ts"]andthresholds.break: 100: exit 0,reports/mutation/mutation.jsonhas"files": {}. Before the fix: exit 3.packages/stryker-js/tests/empty-file-selection.integration.test.ts: an empty shard passes with an empty report, and an empty project fails at prepare withNo input files found.as the stated cause.pnpm typecheck58/58. oxlint, tsgo, dprint, andapi:checkare clean.<changed to<=,fileCountforced to at least 1, an unscored run failing a break threshold, the merge scoring an empty module), and all four broken versions failed.Review findings (all applied)
Report.Metrics(merge-report-parts.workflow.ts:215)make-body-puritygate forbids callingReport.Metricsfrom the workflow's decide body, so the dead clamp that made the two differ was removed (2d132aa), and∀cs_Modules_≡RowScoreIsTheReportMetricsScorenow requires every merged row's score to equal theReport.Metricsscore (191f494). A deliberately drifted merge formula fails that law.mutationScoreas a number break (Metrics.schema.ts:53)major; the changeset now shows the before/after migration (670998e).mutation-reporting.service.ts, clear-text renderer)percentageand the service's ownn/arenderer is gone (86d68c3).Review run
20260925-153149-c8aaef73: correctness, project-standards, testing, maintainability, api-contract, and an in-process adversarial lens; no cross-model peer was installed. The validator rejected three further findings with code evidence (the reviewers expectedmutation.jsonor other packages to read the score; they do not).