Mutineer cannot mutate Sequel dataset_module methods; their mutants show as false survivors #1691
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#1691
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
Mutineer cannot apply a mutant to a method defined inside a Sequel
dataset_module do ... endblock. 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 underlib/os_sdlc/usedataset_module.Observed
Mutineer 1.8.1 runs with
--strategy redefine(lib/os_sdlc/mutation_test/mutineer_invocation.rb). Its parser treats onlyClass.new,Module.new,class_methods,includedandprependedblocks as block owners (mutineerproject.rb:323,isolation.rb:252-282). It attributes adefinsidedataset_moduleto the model's instance method, for exampleOsSdlc::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) makestest_for_session_selects_only_the_rows_of_that_sessionfail:The packet still lists the same mutant (
991a62f2edff) as a survivor. Undertest/mutation_helper.rb, after the redefine,Implementation.for_session('x')still returns aSequel::Dataset::_Subclass, andImplementation.instance_method(:for_session).ownerisOsSdlc::Implementation.16 survivors in #1662 are blocked:
lib/os_sdlc/implementation.rblines 28-44 (6),lib/os_sdlc/implementation/reroute.rblines 12-18 (9),lib/os_sdlc/implementation/edge_spend.rbline 9 (1).Reproduce
The packet lists the
Reroute#applied,#pendingand#cap_reached?mutants as survivors, thoughreroute_test.rbfails when each mutant is applied by hand.Approach
Investigation. Decide one of two routes and record the decision on this ticket:
dataset_moduleblock owns itsdefs. Mutineer is a third-party gem (github davidteren/mutineer). A report or patch there needs Jared's approval before anything is posted..claude/rules/os-sdlc-architecture.mdSequel rule 4.Required work: none beyond the acceptance criteria.
Acceptance criteria
lib/os_sdlc/implementation/reroute.rbwith its paired test, then thedataset_modulemutants are killed or the packet reports them as not applied, not as survivors.Origin
dataset_moduleas a block owner ← UPSTREAM-TOOL (mutineer 1.8.1)VERDICT: CREATE
The #1664 verify packet (2026-11-11, all 16 groups) found 4 more survivors of the same kind, all inside dataset_module blocks:
#1664 does not rule them. This ticket owns them with the 16 from #1662.
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 freezein Db.bind!. test/mutation_prelude.rb:9 connects withfreeze: falsebecause mutineer redefines model methods in the forked child, so no test reaches the freeze branch. The mutant changes behavior; it is not equivalent.Route chosen (2026-11-11): route 2, option 1. Move each
dataset_module do ... endblock into a named module passed todataset_module(the formDispatch::QueriesandDb::OrderedByIdalready 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.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.
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.