Make mutation-pass packet use the tier-2 operators of the whole pass #1700

Closed
opened 2026-11-11 23:11:26 +00:00 by jared · 2 comments
Owner

Problem

os-sdlc-mutation-pass packet does not create the mutants of the tier-2 operators (condition_true, condition_false, negation_removal). The whole pass creates them. The survivor loop closes a ticket when each packet shows 0 unruled survivors, so a ticket can close while its tier-2 survivors stay alive. Whole pass 3 found 155 pass 2 survivors that survived again for this reason, after their tickets closed.

Observed

MutationTest::Pass::Command#operators returns the operators of .mutineer.yml plus TIER_TWO_OPERATORS (lib/os_sdlc/mutation_test/pass/command.rb, line 25, since #1636 on 2026-11-08). The packet command uses only the operators of .mutineer.yml. One cop file, one test:

Run Mutants Survivors
mutineer run with the whole-pass options 53 14
os-sdlc-mutation-pass packet 37 9

The 5 extra survivors are all tier 2, for example f14935dfb65b, line 27, return [] unless root to return [] unless (true).

Reproduce

cd plugins/os-sdlc
bundle exec bin/os-sdlc-mutation-pass packet . --source lib/os_sdlc/cops/dry/duplicated_statement_sequence.rb --test test/cops/duplicated_statement_sequence_test.rb

The first line prints # 9 survivors of 37. The whole pass reports 53 mutants and 14 survivors for the same file.

Approach

The packet and the whole pass take their operator list from one owner, so the two lists cannot differ.

Required work: none beyond the acceptance criteria.

Acceptance criteria

  1. Given a source file that has a tier-2 survivor, when os-sdlc-mutation-pass packet runs on it, then the packet lists that survivor.
  2. Given the same source and tests, when the packet and the whole pass run, then both create the same number of mutants for that source.

Owner sketch

MutationTest::Pass::Packet::Command#to_s passes no --operators flag today, so Mutineer uses only the .mutineer.yml list. One object owns the operator list of a pass; the whole-pass Command and the packet Command both read it.

class OsSdlc::MutationTest::Pass::Operators
  def initialize(root)
  def to_a # .mutineer.yml operators + TIER_TWO_OPERATORS, unique
end

class OsSdlc::MutationTest::Pass::Command
  def operators # Operators.new(@root).to_a
end

class OsSdlc::MutationTest::Pass::Packet::Command
  def initialize(root, sources, tests)
  def to_s # adds extra: ["--operators", Operators.new(@root).to_a.join(",")]
end

Origin

  • Trigger: whole mutation pass 3, 2026-11-11 (docs/research/mutation-testing/2026-11-11-third-pass/README.md, commit 6f6dfa75b)
  • Improvised this session: none
  • Chain: pass 2 survivors survive again ← packets did not create tier-2 mutants ← TIER_TWO_OPERATORS added only to the whole-pass command in #1636 ← DESIGN (lib/os_sdlc/mutation_test/pass/command.rb)
  • Root candidate: none found; searched packet, tier-2, TIER_TWO_OPERATORS, operators, mutation-pass
  • Where: lib/os_sdlc/mutation_test/pass/packet.rb, lib/os_sdlc/mutation_test/pass/command.rb
  • Session: 2677aa00-1973-4df6-b9c5-e317088f3ea3
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/2677aa00-1973-4df6-b9c5-e317088f3ea3.jsonl

VERDICT: CREATE (ticket-skeptic, 2026-11-11): no open ticket has the same mechanism.

## Problem `os-sdlc-mutation-pass packet` does not create the mutants of the tier-2 operators (`condition_true`, `condition_false`, `negation_removal`). The whole pass creates them. The survivor loop closes a ticket when each packet shows 0 unruled survivors, so a ticket can close while its tier-2 survivors stay alive. Whole pass 3 found 155 pass 2 survivors that survived again for this reason, after their tickets closed. ## Observed `MutationTest::Pass::Command#operators` returns the operators of `.mutineer.yml` plus `TIER_TWO_OPERATORS` (`lib/os_sdlc/mutation_test/pass/command.rb`, line 25, since #1636 on 2026-11-08). The packet command uses only the operators of `.mutineer.yml`. One cop file, one test: | Run | Mutants | Survivors | |---|---|---| | `mutineer run` with the whole-pass options | 53 | 14 | | `os-sdlc-mutation-pass packet` | 37 | 9 | The 5 extra survivors are all tier 2, for example `f14935dfb65b`, line 27, `return [] unless root` to `return [] unless (true)`. ## Reproduce ``` cd plugins/os-sdlc bundle exec bin/os-sdlc-mutation-pass packet . --source lib/os_sdlc/cops/dry/duplicated_statement_sequence.rb --test test/cops/duplicated_statement_sequence_test.rb ``` The first line prints `# 9 survivors of 37`. The whole pass reports 53 mutants and 14 survivors for the same file. ## Approach The packet and the whole pass take their operator list from one owner, so the two lists cannot differ. Required work: none beyond the acceptance criteria. ## Acceptance criteria 1. Given a source file that has a tier-2 survivor, when `os-sdlc-mutation-pass packet` runs on it, then the packet lists that survivor. 2. Given the same source and tests, when the packet and the whole pass run, then both create the same number of mutants for that source. ## Owner sketch `MutationTest::Pass::Packet::Command#to_s` passes no `--operators` flag today, so Mutineer uses only the `.mutineer.yml` list. One object owns the operator list of a pass; the whole-pass `Command` and the packet `Command` both read it. ```ruby class OsSdlc::MutationTest::Pass::Operators def initialize(root) def to_a # .mutineer.yml operators + TIER_TWO_OPERATORS, unique end class OsSdlc::MutationTest::Pass::Command def operators # Operators.new(@root).to_a end class OsSdlc::MutationTest::Pass::Packet::Command def initialize(root, sources, tests) def to_s # adds extra: ["--operators", Operators.new(@root).to_a.join(",")] end ``` ## Origin - Trigger: whole mutation pass 3, 2026-11-11 (`docs/research/mutation-testing/2026-11-11-third-pass/README.md`, commit 6f6dfa75b) - Improvised this session: none - Chain: pass 2 survivors survive again ← packets did not create tier-2 mutants ← `TIER_TWO_OPERATORS` added only to the whole-pass command in #1636 ← DESIGN (`lib/os_sdlc/mutation_test/pass/command.rb`) - Root candidate: none found; searched packet, tier-2, TIER_TWO_OPERATORS, operators, mutation-pass - Where: lib/os_sdlc/mutation_test/pass/packet.rb, lib/os_sdlc/mutation_test/pass/command.rb - Session: 2677aa00-1973-4df6-b9c5-e317088f3ea3 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/2677aa00-1973-4df6-b9c5-e317088f3ea3.jsonl VERDICT: CREATE (ticket-skeptic, 2026-11-11): no open ticket has the same mechanism.
Author
Owner

Work started on branch ticket-1700 through /os-sdlc:implement (poodr-ticket-implementation).

Work started on branch ticket-1700 through /os-sdlc:implement (poodr-ticket-implementation).
Author
Owner

Resolution

Done: Pass::Operators owns the operator list; the whole-pass and packet commands both read it, so a packet now creates the tier-2 mutants.

Evidence: Commits 4da4baf82 (implementation 280, all gates pass, survivors gate exit 0) and 5bfa52a35. Reproduction re-run: packet on cops/dry/duplicated_statement_sequence.rb now prints '# 14 survivors of 53', the same as the whole pass (before: 9 of 37).

Follow-ups: none

## Resolution **Done:** Pass::Operators owns the operator list; the whole-pass and packet commands both read it, so a packet now creates the tier-2 mutants. **Evidence:** Commits 4da4baf82 (implementation 280, all gates pass, survivors gate exit 0) and 5bfa52a35. Reproduction re-run: packet on cops/dry/duplicated_statement_sequence.rb now prints '# 14 survivors of 53', the same as the whole pass (before: 9 of 37). **Follow-ups:** none <!-- os-backlog kind: resolution -->
jared closed this issue 2026-11-11 23:21:02 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1700
No description provided.