Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

A differential run can carry a killed outcome onto a surviving mutant and exit 0

Open
#21 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

@andrewmolyuk is already working on this.

Since Oct 30, 2026.

  • #23 by @andrewmolyuk — open

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Stale
Tech stack
go, python
Domain
devtools, testing

Research direction

Read functions.mutation_id, _select, and _carry_forward, then trace how mutation IDs and form digests are built and reused. Reproduce the reported Go case with the 7-byte comment and compare it with --mutate-all and the 8-byte comment case. Done means the differential run preserves the surviving mutant’s outcome correctly and avoids rerunning unaffected functions; pull request #23 is already open.

Written by the indexing model from the issue text.

Description

A mutation id holds the site's byte offsets in the file. When text above an unchanged function moves it by exactly the distance between two identical sites in it, a site takes the id its neighbour had in the last run. It inherits that outcome without being run.

Repro (mutator a5a0cb8, crapper 6fd92c4, go 1.27.3, macOS 27)

go.mod: module example.com/shop

calc/calc.go:

package calc

func F(a, b, c, d int) (bool, bool) {
	return a > b, c > d
}

calc/calc_test.go (it checks only the second result, so a > b → a >= b survives):

package calc

import "testing"

func TestF(t *testing.T) {
	if _, y := F(0, 0, 1, 1); y {
		t.Fatal("c > d")
	}
}
$ ../mutator/mutator --no-coverage calc/calc.go
SURVIVED  calc/calc.go:4 > -> >=
KILLED    calc/calc.go:4 > -> >=
$ echo $?
3

The snapshot has […,"defn/F",62,63,">",">="] :survived and […,"defn/F",69,70,">",">="] :killed. Now add a 7-byte comment above F, the distance between the two >:

package calc

// ab

func F(a, b, c, d int) (bool, bool) {
	return a > b, c > d
}
$ ../mutator/mutator --no-coverage calc/calc.go
KILLED    calc/calc.go:6 > -> >=
KILLED    calc/calc.go:6 > -> >=
$ echo $?
0

With --verbose, only one mutant runs. The first > is now at 69..70: it takes the killed id and is carried forward. --mutate-all on the same file reports the survivor again and exits 3, as does an 8-byte comment instead of 7.

Cause, as far as I can tell

functions.mutation_id uses site.start and site.end, which are offsets in the file. _select and _carry_forward reuse an outcome when the form's digest is unchanged and the id matches. The digest covers only the function's text, so it does not change when the function moves, while the offsets do.

Why it matters

Used as a gate, the run reports 100% and exits 0 with a surviving mutant. In Go a function often repeats a site, such as err != nil, so an edit above it can line up with a gap between two of them. The same offsets also make every function below an edit rerun all its mutants, which loses most of the gain of a differential run.

Possible fix

Key the id by offsets relative to the start of its form, or by the site's ordinal among identical sites in the form, rather than by offsets in the file.

Dominant language
Python
Stars
55
Forks
7
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from unclebob/mutator

All issues in unclebob/mutator

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.