Make the mutation gate refuse a mutated run that executed no tests #893
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#893
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The mutation gate passes when the mutated test run exits nonzero for any reason.
Mutation::Check#survived?(plugins/os-sdlc/lib/os_sdlc/adapter/mutation/check.rb) returnsstatus.success?from the diff-test command. #875 says the check exits 0 only when the test run is red, "executed and failed". A patch that makes the production file fail to load (a syntax error, a missing constant) makes the run exit nonzero before any guard test executes. The gate then accepts a guard test that guards nothing.Reproduce
n/a: found by reading the #875 diff (commit
461ff366); no run used it yet.Approach
Required work: none beyond the acceptance criteria.
Do not add a new output parser or exit-code heuristic. The red-versus-broken rule already has one owner:
Gate::StepOutcome#classify(expect_red: true)(plugins/os-sdlc/lib/os_sdlc/model/gate/step_outcome.rb), which usesTestReport#summaryandSummary#executed_failure?. The test-writer red gate uses it.Mutation::Checkcaptures the diff-test output and exit status, builds aGate::StepOutcomefor thediff-teststep, and sendsclassify(expect_red: true)::redpasses (exit 0).:unexpected_passfails with the existing "mutation survived" line.:brokenfails with a line that says the mutated run executed no tests (load error or no test summary), naming the mutation path.The
ensurerestore stays as it is. No map, ADR, or lens change: the guard mapfailedge already routes to test-writer, which owns the patch.Acceptance criteria
mutationpatch makes the production file fail to load, whenos-sdlc-mutation-checkruns, then it exits nonzero, the output says the mutated run did not execute the tests, and the production file is restored.Owner sketch
Tests: one new case in plugins/os-sdlc/tests/controller/boot_mutation_check_test.rb with a load-breaking patch constant in plugins/os-sdlc/tests/support/mutation_check_fixture.rb. AC2 is the existing breaking-patch test.
Origin
survived?reads only the exit status ← DESIGN (plugins/os-sdlc/lib/os_sdlc/adapter/mutation/check.rb, commit461ff366)Parent: #859
VERDICT: CREATE —
survived?reads only the exit status; a load failure passes the gate (ticket-skeptic, claude-sonnet-5-5).PREDICTED RULING: REVISE
a. No. The body said "Required work: none" and named no mechanism. An implementer could add a new regex or exit-code test for "no tests ran".
b. Reasons:
c. The user would call it lazy to leave the mechanism to the programmer, or to parse the output again. Research done: check.rb, StepOutcome, TestReport/Summary, GreenAssert, guard map edges, boot_mutation_check_test.rb, the fixture, and ADR-0258.
d. Mutation::Check builds a Gate::StepOutcome for the diff-test run and classifies it with expect_red: true. :red passes. :unexpected_pass gives "mutation survived". :broken gives "mutated run executed no tests". Restore and map stay unchanged. The approach is now in the body.
e. Not read: the ticket comments (issue-show has none), the lens-* skills in full, and Gate::Commands for runners other than Minitest. A runner that does not print the Minitest summary would always classify as :broken.
Work started on branch ticket-893 through /os-sdlc:implement (poodr-ticket-implementation map).
Resolution
Done: Mutation::Check now classifies the mutated diff-test run with Gate::StepOutcome#classify(expect_red: true): red passes, unexpected_pass fails as survived, broken (no tests executed) fails with a new message instead of counting as killed.
Evidence: Implementation 20 complete, 8 dispatches all pass, suite gate exit 0. Commits
8009c244(code + tests) anda485ad62(History row) on main.Follow-ups: none
Finding after close, recorded 2026-10-03 (session a0dbd5c4). How much does this matter: a lot, if confirmed on a real target.
The shipped fix (
8009c244) sends the mutated run toGate::StepOutcome#classify(expect_red: true). That rule usesSummary#executed_failure?, which isruns.positive? && (failures + errors).positive?. On a target that loads code through Zeitwerk, a patch that breaks a file or names a missing constant shows as a Minitest Error inside a test (1 runs, 0 assertions, 0 failures, 1 errors). The gate classifies that as:redand accepts the mutation. These are the two defects that this ticket names. The shipped test passes only because its fixture loads the file withrequire_relative(tests/support/mutation_check_fixture.rb:10).Evidence level: reproduced in a scratch Zeitwerk project only. No breaking mutation has run against a real target, and 8 pipeline databases hold 0 mutation handoffs and 0 guard-map runs.
Candidate design, not yet ruled on by Jared: only a failed assertion counts as a kill (
Summary#failed_assertion?), and the guard map test-writer node getsgate_resultsso that a refused rerun sees the reason. A ticket-skeptic verdict of DROP stopped a new ticket until Jared rules. Draft and design records:docs/research/challenge-moves/2026-10-03-scoring/anddocs/research/challenge-moves/prediction-scores.md.