Skip to content

Mutation gate: RemoveCheckAsync block-removal mutant is reported surviving in about half of the sweeps #312

Closed

Description

@bitbiter-dev

What happens

Stryker reports the block-removal mutant of EfBurstRepository.RemoveCheckAsync's body (Anichron.Core/Data/Repository/BurstRepository.cs, { → {}) as Survived in roughly half of the sweeps. In the other runs it reports it Killed by BurstRepositoryTests.RemoveCheckAsync_CheckedPhoto_DeletesItsRowAtTheNextSaveAsync. With the 100% gate, every sweep that loses this kill fails Mutation and therefore CI gate, whatever the PR changed.

The kill is real. With the body removed by hand, RemoveCheckAsync_CheckedPhoto_DeletesItsRowAtTheNextSaveAsync fails, because the row is still there. A "Survived" means the covering test passed, so the mutant was not active while it ran.

Evidence (2026-10-24)

Covering tests in every run: RemoveCheckAsync_UncheckedPhoto_ChangesNothingAsync and RemoveCheckAsync_CheckedPhoto_DeletesItsRowAtTheNextSaveAsync.

Where Code Mutant reported surviving
Local scoped sweeps (mutation-changed.sh), concurrency 4 master at bdb9e0d, unmodified 2 of 3 runs
Local scoped sweeps, concurrency 4 #308 branch 5 of 9 runs
CI Mutation (full sweep) #308 2 of 2 runs
CI Mutation divergence, run 40077588216 #308 killed at concurrency 4 (1676 detected); survived single-threaded (1675 detected)

What this rules out:

It was first measured on 8547e1d (#297), where RemoveCheckAsync was added, so the flip may be as old as the method.

Not yet known

  • Why this mutant's activation is lost. Candidates:
    • The mutant is in an async method whose only statement awaits FindAsync. Stryker's MTP runner may read the active mutant on a path an async continuation does not see.
    • The test seeds through one AnichronDbContext and asserts through another, built by AnichronDbContexts.InMemory(name) with the cached service provider. CLAUDE.md names EF's cached model as a source of mutants that flip between runs.
  • Whether other mutants flip the same way less often. The divergence job compares counts, and this one moves them by 1.

Workaround in use

#308 reran Mutation until the gate passed, with the maintainer's agreement, because the flip predates it.

Activity

  1. added
    bugSomething isn't working
    needs-triageMaintainer needs to evaluate this issue
    on Oct 24, 2026
  2. bitbiter-dev commented on Oct 25, 2026

    @bitbiter-dev
    OwnerAuthor

    More runs (2026-10-25), same mutant (BurstRepository.cs:132 after #298 moved it), same two covering tests:

    Where Mode Result
    PR #313, CI Mutation, run 40083774678 solution Survived
    PR #313, same run, rerun failed jobs solution Killed
    PR #313 after merging master (5a334d4) solution Survived
    Local --project Anichron.Core.csproj -m BurstRepository.cs per project Killed
    PR #315, CI Mutation — Anichron.Core (master code) per project Killed

    CI now mutates per project (#311, ADR 0010), which changes host reuse and mutant order, but not the cause diagnosed on #305 (EF's process-wide query cache in a reused MTP host). Per-project runs can still flip until #305 lands. The weekly divergence check and the local full sweep still run in solution mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageMaintainer needs to evaluate this issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions