Context
Mutation-testing run against main (b850e2fec11b1966d2b73670ae56b5eff195284e), mutmut 3.6.0, local run 2026-07-10: 1025 mutants, 151 killed, 235 survived, 639 no-tests. All mutants target hooks/post-turn-quality-stop-hook.py.
This issue covers the git/make plumbing helpers: 102 survivors across repo_root, has_uncommitted_changes, get_upstream_ref, has_unpushed_commits, get_make_targets, and compush_check.
Surviving mutants
hooks/post-turn-quality-stop-hook.py:208 — every token of ["git", "rev-parse", "--show-toplevel"] mutates freely in repo_root (x_repo_root__mutmut_6–11); likewise the returncode check, error-string composition, and empty-output message (mutmut_12–23).
hooks/post-turn-quality-stop-hook.py:482-493 — all argv tokens of git diff --quiet, git diff --cached --quiet, and git ls-files --others --exclude-standard in has_uncommitted_changes (x_has_uncommitted_changes__mutmut_1–43, 32 survivors), including run(args, None) argument-wiring mutants and the ' '.join(args) error format.
hooks/post-turn-quality-stop-hook.py:513-518 — the full git rev-parse --abbrev-ref --symbolic-full-name @{u} argv and error literals in get_upstream_ref (x_get_upstream_ref__mutmut_6–25, 16 survivors).
hooks/post-turn-quality-stop-hook.py:537-549 — the git rev-list --count argv in has_unpushed_commits (x_has_unpushed_commits__mutmut_2–17), and notably int(ahead) > 0 → > 1 (x_has_unpushed_commits__mutmut_21, line 549).
hooks/post-turn-quality-stop-hook.py:641-652 — the make -qp --no-print-directory argv, the returncode == 2 sentinel, and error composition in get_make_targets (x_get_make_targets__mutmut_2–22, 18 survivors).
hooks/post-turn-quality-stop-hook.py:1090-1106 — compush_check call wiring (has_uncommitted_changes(None), has_unpushed_commits(None, upstream) and permutations) and the upstream-label literal "origin (upstream not configured)" wrapped as XX…XX (x_compush_check__mutmut_5, 8, 24–27).
Analysis
The test suite (hooks/test_post_turn_quality_stop_hook.py) patches hook.run wholesale and never asserts the argument vectors passed to it, so any mutation of a command list, of the run(args, repo) wiring, or of git-failure messages is invisible. Error-message tests use substring assertions ("fatal: bad" in err), so literal mutations that preserve the substring also survive — the XX…XX wrapping of the compush upstream label survives precisely because the test asserts in rather than equality. The > 0 → > 1 boundary survives because tests use ahead-counts 2 and 0, never 1.
Suggested tests
- In each mocked-
run test, assert mock_run.call_args_list against the exact argv and repo argument (kills the ~85 argv and wiring mutants).
- Add a
has_unpushed_commits case with rev-list output "1" asserting True (kills the > 1 boundary mutant).
- Assert exact error messages (or
== on the blocked-payload reason) for repo_root, get_upstream_ref, get_make_targets, and the compush fallback label (kills the literal-mutation survivors).
- Optionally, one integration test driving
repo_root/has_uncommitted_changes against a real temporary git repository as a belt-and-braces check.
Full worklist: triage worklist.md (rollout Stage A artefact); dataset SHA b850e2f.
Context
Mutation-testing run against
main(b850e2fec11b1966d2b73670ae56b5eff195284e), mutmut 3.6.0, local run 2026-07-10: 1025 mutants, 151 killed, 235 survived, 639 no-tests. All mutants targethooks/post-turn-quality-stop-hook.py.This issue covers the git/make plumbing helpers: 102 survivors across
repo_root,has_uncommitted_changes,get_upstream_ref,has_unpushed_commits,get_make_targets, andcompush_check.Surviving mutants
hooks/post-turn-quality-stop-hook.py:208— every token of["git", "rev-parse", "--show-toplevel"]mutates freely inrepo_root(x_repo_root__mutmut_6–11); likewise the returncode check, error-string composition, and empty-output message (mutmut_12–23).hooks/post-turn-quality-stop-hook.py:482-493— all argv tokens ofgit diff --quiet,git diff --cached --quiet, andgit ls-files --others --exclude-standardinhas_uncommitted_changes(x_has_uncommitted_changes__mutmut_1–43, 32 survivors), includingrun(args, None)argument-wiring mutants and the' '.join(args)error format.hooks/post-turn-quality-stop-hook.py:513-518— the fullgit rev-parse --abbrev-ref --symbolic-full-name @{u}argv and error literals inget_upstream_ref(x_get_upstream_ref__mutmut_6–25, 16 survivors).hooks/post-turn-quality-stop-hook.py:537-549— thegit rev-list --countargv inhas_unpushed_commits(x_has_unpushed_commits__mutmut_2–17), and notablyint(ahead) > 0→> 1(x_has_unpushed_commits__mutmut_21, line 549).hooks/post-turn-quality-stop-hook.py:641-652— themake -qp --no-print-directoryargv, thereturncode == 2sentinel, and error composition inget_make_targets(x_get_make_targets__mutmut_2–22, 18 survivors).hooks/post-turn-quality-stop-hook.py:1090-1106—compush_checkcall wiring (has_uncommitted_changes(None),has_unpushed_commits(None, upstream)and permutations) and the upstream-label literal"origin (upstream not configured)"wrapped asXX…XX(x_compush_check__mutmut_5,8,24–27).Analysis
The test suite (
hooks/test_post_turn_quality_stop_hook.py) patcheshook.runwholesale and never asserts the argument vectors passed to it, so any mutation of a command list, of therun(args, repo)wiring, or of git-failure messages is invisible. Error-message tests use substring assertions ("fatal: bad" in err), so literal mutations that preserve the substring also survive — theXX…XXwrapping of the compush upstream label survives precisely because the test assertsinrather than equality. The> 0→> 1boundary survives because tests use ahead-counts 2 and 0, never 1.Suggested tests
runtest, assertmock_run.call_args_listagainst the exact argv andrepoargument (kills the ~85 argv and wiring mutants).has_unpushed_commitscase with rev-list output"1"assertingTrue(kills the> 1boundary mutant).==on the blocked-payload reason) forrepo_root,get_upstream_ref,get_make_targets, and the compush fallback label (kills the literal-mutation survivors).repo_root/has_uncommitted_changesagainst a real temporary git repository as a belt-and-braces check.Full worklist: triage
worklist.md(rollout Stage A artefact); dataset SHAb850e2f.