Mutineer cannot mutate Sequel dataset_module methods; their mutants show as false survivors #1691

Closed
opened 2026-11-11 07:13:51 +00:00 by jared · 5 comments
Owner

Problem

Mutineer cannot apply a mutant to a method defined inside a Sequel dataset_module do ... end block. The packet then lists the mutant as a survivor, but no test can kill it. Rulings 92 and 106 forbid an ignore list, and the mutants are not equivalent. So these false survivors stay open and lower the true score of every os-sdlc model with scoped queries. 13 files under lib/os_sdlc/ use dataset_module.

Observed

Mutineer 1.8.1 runs with --strategy redefine (lib/os_sdlc/mutation_test/mutineer_invocation.rb). Its parser treats only Class.new, Module.new, class_methods, included and prepended blocks as block owners (mutineer project.rb:323, isolation.rb:252-282). It attributes a def inside dataset_module to the model's instance method, for example OsSdlc::Implementation#for_session, and redefines it there. The tests call the class-level dataset method. Sequel dispatches that call to the dataset module, and the redefine does not change it.

A hand-applied mutant at lib/os_sdlc/implementation.rb:28 (for_session => nil) makes test_for_session_selects_only_the_rows_of_that_session fail:

NoMethodError: undefined method 'unfinished' for nil

The packet still lists the same mutant (991a62f2edff) as a survivor. Under test/mutation_helper.rb, after the redefine, Implementation.for_session('x') still returns a Sequel::Dataset::_Subclass, and Implementation.instance_method(:for_session).owner is OsSdlc::Implementation.

16 survivors in #1662 are blocked: lib/os_sdlc/implementation.rb lines 28-44 (6), lib/os_sdlc/implementation/reroute.rb lines 12-18 (9), lib/os_sdlc/implementation/edge_spend.rb line 9 (1).

Reproduce

bin/os-sdlc-mutation-pass packet . --source lib/os_sdlc/implementation/reroute.rb --test test/os_sdlc/implementation/reroute_test.rb

The packet lists the Reroute#applied, #pending and #cap_reached? mutants as survivors, though reroute_test.rb fails when each mutant is applied by hand.

Approach

Investigation. Decide one of two routes and record the decision on this ticket:

  1. A fix in mutineer, so that a dataset_module block owns its defs. Mutineer is a third-party gem (github davidteren/mutineer). A report or patch there needs Jared's approval before anything is posted.
  2. A change in how os-sdlc defines scoped queries, so that mutineer can reach them. This needs a check against .claude/rules/os-sdlc-architecture.md Sequel rule 4.

Required work: none beyond the acceptance criteria.

Acceptance criteria

  1. Given the chosen route is in place, when a targeted packet runs over lib/os_sdlc/implementation/reroute.rb with its paired test, then the dataset_module mutants are killed or the packet reports them as not applied, not as survivors.

Origin

  • Trigger: survivor loop on #1662, verify step, 2026-11-11
  • Improvised this session: none; the 16 mutants stay listed as survivors
  • Chain: 16 unkillable survivors ← mutant applied to the model instance method, not the dataset module ← mutineer parser does not treat dataset_module as a block owner ← UPSTREAM-TOOL (mutineer 1.8.1)
  • Root candidate: none found; searched dataset_module, dataset module, mutineer, mutant, false survivor
  • Where: lib/os_sdlc/mutation_test/mutineer_invocation.rb; mutineer 1.8.1 project.rb, isolation.rb
  • Session: e04dcfb2-d544-497a-9020-55066ed00123
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-ticket-1663/e04dcfb2-d544-497a-9020-55066ed00123.jsonl

VERDICT: CREATE

## Problem Mutineer cannot apply a mutant to a method defined inside a Sequel `dataset_module do ... end` block. The packet then lists the mutant as a survivor, but no test can kill it. Rulings 92 and 106 forbid an ignore list, and the mutants are not equivalent. So these false survivors stay open and lower the true score of every os-sdlc model with scoped queries. 13 files under `lib/os_sdlc/` use `dataset_module`. ## Observed Mutineer 1.8.1 runs with `--strategy redefine` (`lib/os_sdlc/mutation_test/mutineer_invocation.rb`). Its parser treats only `Class.new`, `Module.new`, `class_methods`, `included` and `prepended` blocks as block owners (mutineer `project.rb:323`, `isolation.rb:252-282`). It attributes a `def` inside `dataset_module` to the model's instance method, for example `OsSdlc::Implementation#for_session`, and redefines it there. The tests call the class-level dataset method. Sequel dispatches that call to the dataset module, and the redefine does not change it. A hand-applied mutant at `lib/os_sdlc/implementation.rb:28` (`for_session` => `nil`) makes `test_for_session_selects_only_the_rows_of_that_session` fail: ``` NoMethodError: undefined method 'unfinished' for nil ``` The packet still lists the same mutant (`991a62f2edff`) as a survivor. Under `test/mutation_helper.rb`, after the redefine, `Implementation.for_session('x')` still returns a `Sequel::Dataset::_Subclass`, and `Implementation.instance_method(:for_session).owner` is `OsSdlc::Implementation`. 16 survivors in #1662 are blocked: `lib/os_sdlc/implementation.rb` lines 28-44 (6), `lib/os_sdlc/implementation/reroute.rb` lines 12-18 (9), `lib/os_sdlc/implementation/edge_spend.rb` line 9 (1). ## Reproduce ``` bin/os-sdlc-mutation-pass packet . --source lib/os_sdlc/implementation/reroute.rb --test test/os_sdlc/implementation/reroute_test.rb ``` The packet lists the `Reroute#applied`, `#pending` and `#cap_reached?` mutants as survivors, though `reroute_test.rb` fails when each mutant is applied by hand. ## Approach Investigation. Decide one of two routes and record the decision on this ticket: 1. A fix in mutineer, so that a `dataset_module` block owns its `def`s. Mutineer is a third-party gem (github davidteren/mutineer). A report or patch there needs Jared's approval before anything is posted. 2. A change in how os-sdlc defines scoped queries, so that mutineer can reach them. This needs a check against `.claude/rules/os-sdlc-architecture.md` Sequel rule 4. Required work: none beyond the acceptance criteria. ## Acceptance criteria 1. Given the chosen route is in place, when a targeted packet runs over `lib/os_sdlc/implementation/reroute.rb` with its paired test, then the `dataset_module` mutants are killed or the packet reports them as not applied, not as survivors. ## Origin - Trigger: survivor loop on #1662, verify step, 2026-11-11 - Improvised this session: none; the 16 mutants stay listed as survivors - Chain: 16 unkillable survivors ← mutant applied to the model instance method, not the dataset module ← mutineer parser does not treat `dataset_module` as a block owner ← UPSTREAM-TOOL (mutineer 1.8.1) - Root candidate: none found; searched dataset_module, dataset module, mutineer, mutant, false survivor - Where: lib/os_sdlc/mutation_test/mutineer_invocation.rb; mutineer 1.8.1 project.rb, isolation.rb - Session: e04dcfb2-d544-497a-9020-55066ed00123 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-ticket-1663/e04dcfb2-d544-497a-9020-55066ed00123.jsonl VERDICT: CREATE
Author
Owner

The #1664 verify packet (2026-11-11, all 16 groups) found 4 more survivors of the same kind, all inside dataset_module blocks:

  • a57692a97f19 correction_round.rb:9 CorrectionRound#for_gate_type return_nil
  • 9517136a7ca8 objection.rb:10 Objection#at_node return_nil
  • 1ea5ded23fec objection.rb:11 Objection#open nil=>true
  • b73299421888 objection.rb:11 Objection#open return_nil

#1664 does not rule them. This ticket owns them with the 16 from #1662.

The #1664 verify packet (2026-11-11, all 16 groups) found 4 more survivors of the same kind, all inside dataset_module blocks: - a57692a97f19 correction_round.rb:9 CorrectionRound#for_gate_type return_nil - 9517136a7ca8 objection.rb:10 Objection#at_node return_nil - 1ea5ded23fec objection.rb:11 Objection#open nil=>true - b73299421888 objection.rb:11 Objection#open return_nil #1664 does not rule them. This ticket owns them with the 16 from #1662.
Author
Owner

Another survivor the harness cannot reach (ticket-skeptic DROP on a separate ticket, 2026-11-11): 4ff3bd77b909, lib/os_sdlc/db.rb:25, statement_removal of Sequel::Model.freeze_descendants if freeze in Db.bind!. test/mutation_prelude.rb:9 connects with freeze: false because mutineer redefines model methods in the forked child, so no test reaches the freeze branch. The mutant changes behavior; it is not equivalent.

Another survivor the harness cannot reach (ticket-skeptic DROP on a separate ticket, 2026-11-11): 4ff3bd77b909, lib/os_sdlc/db.rb:25, statement_removal of `Sequel::Model.freeze_descendants if freeze` in Db.bind!. test/mutation_prelude.rb:9 connects with `freeze: false` because mutineer redefines model methods in the forked child, so no test reaches the freeze branch. The mutant changes behavior; it is not equivalent.
Author
Owner

Route chosen (2026-11-11): route 2, option 1. Move each dataset_module do ... end block into a named module passed to dataset_module (the form Dispatch::Queries and Db::OrderedById already use). It conforms to Sequel rule 4; no cop requires the block form; no ADR pins it. Mutineer should treat the named module as a normal owner (inferred; pilot on reroute.rb first). Route 1 (Mutineer patch) is not taken: no config hook exists, it needs a fork and upstream approval. Scope: 8 files, 28 defs (correction_round, handoff, implementation/edge_spend, implementation, implementation/reroute, objection, step_result, violations). Open question, not checked: mutineer_invocation.rb hard-codes --strategy redefine; --strategy reload may also reach the block. Work on branch ticket-1691.

Route chosen (2026-11-11): route 2, option 1. Move each `dataset_module do ... end` block into a named module passed to `dataset_module` (the form `Dispatch::Queries` and `Db::OrderedById` already use). It conforms to Sequel rule 4; no cop requires the block form; no ADR pins it. Mutineer should treat the named module as a normal owner (inferred; pilot on reroute.rb first). Route 1 (Mutineer patch) is not taken: no config hook exists, it needs a fork and upstream approval. Scope: 8 files, 28 defs (correction_round, handoff, implementation/edge_spend, implementation, implementation/reroute, objection, step_result, violations). Open question, not checked: mutineer_invocation.rb hard-codes --strategy redefine; --strategy reload may also reach the block. Work on branch ticket-1691.
Author
Owner

Follow-up dropped by ticket-skeptic: the Db.bind! freeze mutant (db.rb:25) has a different cause (test/mutation_prelude.rb binds once with freeze: false before any mutant); one survivor is acceptable; it goes to #1683 as a line item or an equivalence ruling.

Follow-up dropped by ticket-skeptic: the Db.bind! freeze mutant (db.rb:25) has a different cause (test/mutation_prelude.rb binds once with freeze: false before any mutant); one survivor is acceptable; it goes to #1683 as a line item or an equivalence ruling.
Author
Owner

Resolution

Done: Route 2 taken: the 8 models with a dataset_module do block (28 methods) now pass a named ::Queries module (/queries.rb), the form Dispatch::Queries already used. Sequel rule 4 in .claude/rules/os-sdlc-architecture.md names the form and the reason. No Mutineer patch; nothing posted upstream.

Evidence: main aed91693a + bd36569be (merged). AC: targeted packet over reroute.rb + reroute/queries.rb went from 9 survivors of 9 to 0 survivors of 9. Packets over all 8 models: 0 survivors (Implementation 0/44, Handoff 0/17, StepResult 0/17, Objection 0/13, EdgeSpend 0/10, Reroute 0/9, Violations 0/4, CorrectionRound 0/2). Suite 4898 runs 0 failures; rubocop 0 offenses.

Follow-ups: Db.bind! freeze mutant (db.rb:25) has a different cause; ticket-skeptic dropped a separate ticket; recorded as a line item on #1683.

## Resolution **Done:** Route 2 taken: the 8 models with a dataset_module do block (28 methods) now pass a named <Model>::Queries module (<model>/queries.rb), the form Dispatch::Queries already used. Sequel rule 4 in .claude/rules/os-sdlc-architecture.md names the form and the reason. No Mutineer patch; nothing posted upstream. **Evidence:** main aed91693a + bd36569be (merged). AC: targeted packet over reroute.rb + reroute/queries.rb went from 9 survivors of 9 to 0 survivors of 9. Packets over all 8 models: 0 survivors (Implementation 0/44, Handoff 0/17, StepResult 0/17, Objection 0/13, EdgeSpend 0/10, Reroute 0/9, Violations 0/4, CorrectionRound 0/2). Suite 4898 runs 0 failures; rubocop 0 offenses. **Follow-ups:** Db.bind! freeze mutant (db.rb:25) has a different cause; ticket-skeptic dropped a separate ticket; recorded as a line item on #1683. <!-- os-backlog kind: resolution -->
jared closed this issue 2026-11-11 16:18:07 +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#1691
No description provided.