Skip to content

Kill mutation-testing runner, graphql retry, and matrix survivors (#336, #340, #341, #342) - #355

Open
leynos wants to merge 9 commits into
mainfrom
kill-mutation-survivors
Open

Kill mutation-testing runner, graphql retry, and matrix survivors (#336, #340, #341, #342)#355
leynos wants to merge 9 commits into
mainfrom
kill-mutation-survivors

Conversation

@leynos

@leynos leynos commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses #342, #341, #336, and #340 by killing the highest-value
mutation-testing survivors on the estate's mutation-testing helper
scripts. The two mutation runners come first: they produce every
downstream survivor table, so gaps there corrupt the whole estate's
triage. The graphql_client retry logic (which guards dependabot
auto-merge estate-wide) and the change-detection matrix contract follow.

Each module was killed red-green with mutmut apply/revert and
confirmed with scoped mutmut run 'workflow_scripts.<module>*'
re-runs. Equivalent and accepted-boundary mutants are annotated with
# pragma: no mutate (or suppressed structurally where a trailing pragma
cannot reach the mutated node) with one-line justifications. Response
bulk beyond the named boundaries is deferred to the issues, noted below,

Before / after survivor counts (scoped mutmut run)

Module (scope) Before After Residual
mutation_run_cargo (#342) 4 0
mutation_summarize_cargo (#342) 46 0
mutation_run_mutmut (#341) 64 2 2 equivalent (deferred)
graphql_client retry/backoff + request_graphql (#336) 15 1 1 equivalent (deferred)
mutation_detect_changes matrix contract (#340) 18 0

"Residual" mutants are genuine equivalents that share a source line with
a killable mutant, so a pragma would mask the killable one; they are left
to their issue rather than suppressed.

Review walkthrough

  • mutation_run_cargo.py /
    tests:
    shard-count boundary (1 and 2) and exact exit-code contract meanings
    (including the unknown-code fallback).
  • mutation_summarize_cargo.py /
    tests:
    survivor-placeholder extraction and defaults, timeout/unviable counts,
    root-first ordering, skipped/missing/invalid diagnostics, the
    non-halting scan past a leading foreign directory, and the exact
    job-summary Markdown. UTF-8 read annotated.
  • mutation_run_mutmut.py /
    tests:
    module-glob prefix handling, noise rejection, status accumulation,
    exact survivor/summary Markdown, the uv run --with argv contract for
    both the run and results sub-invocations, the command/exit-code/counts
    diagnostics, and the stderr failure path. The equivalent partition/rpartition method choice, streaming-only
    RETCODE(FG=...), and UTF-8 encoding values are isolated on separately
    suppressed expressions, leaving the result delimiter and summary append
    mode under mutation.
  • graphql_client:
    the strict attempt < max_retries boundary, the exponential backoff
    schedule (previously no-tests), the retry-or-fail decision, and the
    request loop's attempt budget with argument forwarding.
  • mutation_detect_changes.py /
    tests:
    full-run extra-crate shape, scoped target directories, sorted
    matrix-JSON keys, and the skip-summary base-ref/window. The root-first sort key was extracted into _root_first_key; only its
    equivalent alphabetical-key variant is suppressed, leaving the
    root-ranking predicate under mutation.

Deferred (per issue)

Red-green evidence (one transcript per module family)

graphql_client._should_retry (mutant 1, <<=):

$ mutmut apply workflow_scripts.graphql_client.x__should_retry__mutmut_1
$ pytest ...::TestShouldRetry::test_final_attempt_does_not_retry
FAILED  (RED)
$ git checkout workflow_scripts/graphql_client.py
$ pytest ...::TestShouldRetry::test_final_attempt_does_not_retry
1 passed  (GREEN)

mutation_run_cargo.interpret_exit_code (mutant 6, unknown-code default
XXunexpected exit codeXX) and mutation_run_mutmut.count_statuses
(mutant 2, += 1= 1) were verified the same way (RED under the
applied mutant, GREEN after revert).

Validation

  • make check-fmt, make lint, make typecheck: pass.
  • make test: 1041 passed / 14 skipped; 4 snapshots passed.
  • Property-based tests cover the strict retry budget, exponential backoff,
    and root-first/alphabetical ordering invariants.
  • Fixed the release-to-pypi-uv test loader to resolve co-located scripts
    independently of ambient GITHUB_ACTION_PATH; its polluted-environment
    two-worker xdist reproduction passes all 92 focused tests.
  • Final scoped mutation reruns killed 159 mutants in mutation_detect_changes;
    the critical root-order != to == mutant is killed, while 20 broader
    issue-deferred survivors remain.
  • Final mutation_run_mutmut scope: 157 killed and the two documented
    equivalent _module_glob_for survivors remain.

Act-gated workflow tests were left untouched.

References

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary

  • Strengthened mutation-testing coverage across cargo runners and summarisers, mutmut integration, GraphQL retries, and change-detection matrix contracts.
  • Added boundary, exit-code, parsing, diagnostics, Markdown, retry/backoff, matrix ordering, JSON structure, and skip-summary assertions, including Hypothesis properties.
  • Removed ambient GITHUB_ACTION_PATH dependence from release workflow test loading and ignored generated coverage data.
  • Annotated equivalent or accepted-boundary mutants where appropriate.

Validation

  • Scoped mutation survivors reduced to zero for cargo execution, cargo summarisation, and change detection.
  • Mutmut retains two equivalent survivors; GraphQL retains one equivalent survivor.
  • Formatting, linting, type checking, and tests pass: 1041 passed, 14 skipped, and four snapshots passing.

Walkthrough

Strengthen mutation workflow contracts with targeted tests for ordering, boundaries, parsing, diagnostics, encoding, and Markdown output. Add GraphQL client coverage for retry limits, exponential backoff, exhausted retries, and persistent connection failures.

Changes

Mutation workflow contracts

Layer / File(s) Summary
Matrix ordering and summary encoding
workflow_scripts/mutation_detect_changes.py, workflow_scripts/tests/test_mutation_detect_changes.py, workflow_scripts/tests/test_mutation_properties.py
Enforce root-first matrix ordering and UTF-8 skip-summary output; validate matrix shapes, sorted keys, target directories, retry properties, and skip-summary details.
Mutmut execution and rendering contracts
workflow_scripts/mutation_run_mutmut.py, workflow_scripts/tests/test_mutation_run_mutmut.py
Mark stable mutation-testing paths, then verify module-glob handling, result parsing, status accumulation, exact Markdown rendering, command arguments, diagnostics, and stream separation.
Cargo report parsing and execution boundaries
workflow_scripts/mutation_summarize_cargo.py, workflow_scripts/tests/test_mutation_summarize_cargo.py, workflow_scripts/tests/test_mutation_run_cargo.py
Read report content explicitly as UTF-8 and test malformed outcomes, survivor placeholders, shard boundaries, exit-code meanings, merged counts, ordering, diagnostics, and exact summaries.

GraphQL retry coverage

Layer / File(s) Summary
GraphQL retry and backoff behaviour
workflow_scripts/tests/test_graphql_client.py
Verify retry boundaries, exponential backoff, exhausted-budget failure output, and four total attempts for persistent connection errors while preserving request arguments.

Supporting repository updates

Layer / File(s) Summary
Test environment and documentation updates
.github/actions/release-to-pypi-uv/tests/_helpers.py, .gitignore, .rules/*, docs/*
Resolve test paths relative to the helper file, ignore coverage output, and reformat Python, workflow, and planning examples.

Possibly related issues

  • leynos/shared-actions issue 340 — Covers mutation change-detection ordering, matrix contracts, UTF-8 output, and skip-summary objectives.
  • leynos/shared-actions issue 341 — Covers mutmut survivor tests and no mutate annotations.
  • leynos/shared-actions issue 342 — Covers Cargo mutation-testing survivor gaps.

Suggested labels: Issue

Poem

Sort the root before the rest,
Pin each retry to its quest.
Parse the noise and count the slain,
Write UTF-8 through every lane.
Test each boundary, crisp and bright—
Let surviving mutants take flight.

🚥 Pre-merge checks | ✅ 20
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main mutation-testing survivor work and includes the linked issue numbers.
Description check ✅ Passed The description clearly tracks the same survivor-killing changes and test coverage as the PR.
Docstring Coverage ✅ Passed Docstring coverage is 98.25% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Testing (Overall) ✅ Passed Tests cover the changed retry, sorting, parsing, Markdown, and command-argument behaviour with concrete failure cases.
User-Facing Documentation ✅ Passed PASS: The touched code is internal workflow tooling/tests; docs/users-guide.md is absent, and no end-user-facing behaviour was added or changed.
Developer Documentation ✅ Passed Docs already cover the mutation helper scripts and conventions; no new public API or architectural boundary needed extra documentation, and the execplan is current.
Module-Level Documentation ✅ Passed Approve it: every touched Python module carries a top-level docstring and describes its role or test relationship.
Testing (Unit And Behavioural) ✅ Passed Approve it: the suite mixes focused unit/property tests with CLI-level app tests (filesystem, git, fake uv), so it exercises the right boundaries.
Testing (Property / Proof) ✅ Passed PASS: The PR adds Hypothesis property tests for strict retry, exponential backoff, and root-first ordering invariants; no proof obligation is introduced.
Testing (Compile-Time / Ui) ✅ Passed This PR touches only Python/docs, so no trybuild-equivalent is needed; UI-adjacent Markdown/JSON outputs are covered by focused exact-layout and shape assertions.
Unit Architecture ✅ Passed PASS: The patch keeps side effects at the CLI boundaries, adds a pure root-first sort helper, uses explicit UTF-8 I/O, and adds seam-driven tests without hiding fallible work.
Domain Architecture ✅ Passed PASS: The PR only touches workflow/infrastructure scripts, tests, and docs; no domain model code changed, and HTTP/Git/filesystem concerns stay at adapter boundaries.
Observability ✅ Passed Operational paths already emit structured key=value diagnostics and error exits; this PR only tweaks ordering, encoding, and tests, so observability is not regressed.
Security And Privacy ✅ Passed Only tests, comments, encoding, and sort-order tweaks landed; no secrets, auth bypasses, permission broadening, or unsafe sinks were added.
Performance And Resource Use ✅ Passed PASS: changes are comment/pragma tweaks plus a tiny sort-key helper; retries stay bounded at 4 attempts and no new unbounded loops, copies, or I/O appear.
Concurrency And State ✅ Passed PASS: The patch keeps state local and deterministic, adds explicit root-first and retry-boundary tests, and introduces no new async, locking, or shared-mutable-state paths.
Architectural Complexity And Maintainability ✅ Passed PASS: the only new abstraction is a tiny local root-first sort key; it isolates an existing invariant, and the rest is tests, pragmas, or I/O tweaks.
Rust Compiler Lint Integrity ✅ Passed Only .coverage changed in HEAD; no Rust files or lint suppressions/clones were introduced, so the Rust lint integrity check is not applicable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kill-mutation-survivors

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos leynos changed the title Kill mutation-testing runner, graphql retry, and matrix survivors Kill mutation-testing runner, graphql retry, and matrix survivors (#336, #340, #341, #342) Jul 17, 2026
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review July 24, 2026 02:11

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai coderabbitai Bot added the Issue label Jul 24, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5142c7a80d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

falls out of a plain alphabetical sort; the sort-key variants are
equivalent and cannot be distinguished by dir-prefixed file paths.
"""
return (item[0] != ".", item[0]) # pragma: no mutate

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the root-ordering predicate under mutation

Do not suppress this entire return expression: the line-wide pragma also excludes non-equivalent mutants such as changing != to ==, which would place every non-root target before root. The new root-first test would kill that regression, but mutmut now skips it and therefore overstates the matrix contract's mutation coverage; isolate only the genuinely equivalent constant/key variant on a separately suppressed line.

Useful? React with 👍 / 👎.

Comment thread workflow_scripts/mutation_run_mutmut.py Outdated
"""
name, separator, status = line.partition(": ")
# pragma: result lines carry a single ": ", so rpartition matches partition.
name, separator, status = line.partition(": ") # pragma: no mutate

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the result delimiter under mutation

Do not suppress the whole parsing statement merely to ignore the partition/rpartition equivalent: the pragma also excludes non-equivalent mutations to the ": " delimiter. Such a regression would silently discard every mutmut result and publish an empty-results summary, while the added parsing tests would otherwise detect it; restructure the statement so only the equivalent method choice is suppressed.

Useful? React with 👍 / 👎.

results = parse_results(results_text)
with Path(summary_path).open("a", encoding="utf-8") as handle:
# pragma below: encoding is a locale-independent UTF-8 codec alias.
with Path(summary_path).open("a", encoding="utf-8") as handle: # pragma: no mutate

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Leave the summary append mode mutable

Do not apply a line-wide suppression to this open call: although the comment justifies excluding the encoding variant, the same pragma also hides non-equivalent mutations to the "a" file mode, which can make summary publication fail or stop appending. Move the deliberately ignored encoding value onto a separately suppressed expression so mutation runs continue checking the output-mode contract.

Useful? React with 👍 / 👎.

codescene-access[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@workflow_scripts/tests/test_graphql_client.py`:
- Line 15: Annotate the TEST_TOKEN constant with the explicit str type in the
test module, preserving its existing value and behavior. This maintains full
static type coverage as required by the project’s Pyright typing guidelines.
- Around line 86-99: Update the retry assertions in the test around fake_execute
and request_graphql to validate every recorded call, not only seen[0]. Keep the
expected four-call count, then assert that all entries in seen match the
unchanged TEST_TOKEN, query string, and variables for each retry attempt.

In `@workflow_scripts/tests/test_mutation_run_cargo.py`:
- Around line 104-125: Add failure messages to both assertions in
test_informative_codes_succeed and test_fault_codes_fail, including the tested
code and expected contract classification so mismatches clearly identify the
failing context.

In `@workflow_scripts/tests/test_mutation_run_mutmut.py`:
- Around line 291-299: Update test_failing_baseline_propagates_exit_code by
removing the unused fake_uv parameter and adding
pytest.mark.usefixtures("fake_uv") alongside the existing decorator, preserving
the fixture setup side effects without triggering Ruff ARG002.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b11869cd-15aa-4530-a5b6-262a29978d81

📥 Commits

Reviewing files that changed from the base of the PR and between dae5d42 and 5142c7a.

📒 Files selected for processing (8)
  • workflow_scripts/mutation_detect_changes.py
  • workflow_scripts/mutation_run_mutmut.py
  • workflow_scripts/mutation_summarize_cargo.py
  • workflow_scripts/tests/test_graphql_client.py
  • workflow_scripts/tests/test_mutation_detect_changes.py
  • workflow_scripts/tests/test_mutation_run_cargo.py
  • workflow_scripts/tests/test_mutation_run_mutmut.py
  • workflow_scripts/tests/test_mutation_summarize_cargo.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/polythene (auto-detected)

from workflow_scripts import graphql_client

# Test-only constant (not a real credential)
TEST_TOKEN = "test-token" # noqa: S105

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Annotate the test token constant.

Declare TEST_TOKEN: str to maintain the required full static type coverage.

Proposed fix
-TEST_TOKEN = "test-token"  # noqa: S105
+TEST_TOKEN: str = "test-token"  # noqa: S105

As per coding guidelines, “Enable and maintain full static type coverage using Pyright type-checking. Use typing everywhere.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TEST_TOKEN = "test-token" # noqa: S105
TEST_TOKEN: str = "test-token" # noqa: S105
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workflow_scripts/tests/test_graphql_client.py` at line 15, Annotate the
TEST_TOKEN constant with the explicit str type in the test module, preserving
its existing value and behavior. This maintains full static type coverage as
required by the project’s Pyright typing guidelines.

Source: Coding guidelines

Comment on lines +86 to +99
def fake_execute(token: str, query: str, variables: dict[str, object]) -> None:
seen.append((token, query, variables))

monkeypatch.setattr(graphql_client, "_execute_graphql_attempt", fake_execute)
monkeypatch.setattr(graphql_client, "_backoff_sleep", lambda *_: None)
variables: dict[str, object] = {"n": 1}
with pytest.raises(SystemExit):
graphql_client.request_graphql(TEST_TOKEN, "query {}", variables)
assert len(seen) == 4, (
"one initial attempt plus three retries should run before failing"
)
assert seen[0] == (TEST_TOKEN, "query {}", variables), (
"the token, query, and variables should reach each attempt unchanged"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert arguments for every retry attempt.

Replace the first-call-only assertion. It passes when later retries alter the token, query, or variables, leaving the stated retry contract untested. Snapshot each call and assert all four calls match the expected arguments.

Proposed fix
-        seen: list[tuple[object, object, object]] = []
+        seen: list[tuple[str, str, dict[str, object]]] = []
 
         def fake_execute(token: str, query: str, variables: dict[str, object]) -> None:
-            seen.append((token, query, variables))
+            seen.append((token, query, variables.copy()))
@@
-        assert seen[0] == (TEST_TOKEN, "query {}", variables), (
-            "the token, query, and variables should reach each attempt unchanged"
+        expected_call = (TEST_TOKEN, "query {}", {"n": 1})
+        assert seen == [expected_call] * 4, (
+            "the token, query, and variables should reach every attempt unchanged"
         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def fake_execute(token: str, query: str, variables: dict[str, object]) -> None:
seen.append((token, query, variables))
monkeypatch.setattr(graphql_client, "_execute_graphql_attempt", fake_execute)
monkeypatch.setattr(graphql_client, "_backoff_sleep", lambda *_: None)
variables: dict[str, object] = {"n": 1}
with pytest.raises(SystemExit):
graphql_client.request_graphql(TEST_TOKEN, "query {}", variables)
assert len(seen) == 4, (
"one initial attempt plus three retries should run before failing"
)
assert seen[0] == (TEST_TOKEN, "query {}", variables), (
"the token, query, and variables should reach each attempt unchanged"
)
seen: list[tuple[str, str, dict[str, object]]] = []
def fake_execute(token: str, query: str, variables: dict[str, object]) -> None:
seen.append((token, query, variables.copy()))
monkeypatch.setattr(graphql_client, "_execute_graphql_attempt", fake_execute)
monkeypatch.setattr(graphql_client, "_backoff_sleep", lambda *_: None)
variables: dict[str, object] = {"n": 1}
with pytest.raises(SystemExit):
graphql_client.request_graphql(TEST_TOKEN, "query {}", variables)
assert len(seen) == 4, (
"one initial attempt plus three retries should run before failing"
)
expected_call = (TEST_TOKEN, "query {}", {"n": 1})
assert seen == [expected_call] * 4, (
"the token, query, and variables should reach every attempt unchanged"
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workflow_scripts/tests/test_graphql_client.py` around lines 86 - 99, Update
the retry assertions in the test around fake_execute and request_graphql to
validate every recorded call, not only seen[0]. Keep the expected four-call
count, then assert that all entries in seen match the unchanged TEST_TOKEN,
query string, and variables for each retry attempt.

Comment on lines +104 to +125
def test_informative_codes_succeed(self, code: int, meaning: str) -> None:
"""Informative codes succeed and report their exact contract meaning."""
success, actual = run_cargo.interpret_exit_code(code)
assert success
assert meaning
assert actual == meaning, (
f"code {code} should map to the exact meaning {meaning!r}"
)

@pytest.mark.parametrize("code", [1, 4, 70, 99])
def test_fault_codes_fail(self, code: int) -> None:
"""Usage errors, failing baselines, and unknowns are faults."""
success, meaning = run_cargo.interpret_exit_code(code)
@pytest.mark.parametrize(
("code", "meaning"),
[
(1, "usage error"),
(4, "baseline tests failing"),
(70, "internal error"),
(99, "unexpected exit code"),
],
)
def test_fault_codes_fail(self, code: int, meaning: str) -> None:
"""Faults report their exact meaning; unknown codes fall back verbatim."""
success, actual = run_cargo.interpret_exit_code(code)
assert not success
assert meaning
assert actual == meaning, (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add messages to both boolean assertions.

Include failure context when the success classification differs from the contract.

Proposed fix
-        assert success
+        assert success, f"code {code} should be an informative success"
...
-        assert not success
+        assert not success, f"code {code} should be classified as a fault"

As per coding guidelines, “Use assert …, "message" over bare asserts”.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_informative_codes_succeed(self, code: int, meaning: str) -> None:
"""Informative codes succeed and report their exact contract meaning."""
success, actual = run_cargo.interpret_exit_code(code)
assert success
assert meaning
assert actual == meaning, (
f"code {code} should map to the exact meaning {meaning!r}"
)
@pytest.mark.parametrize("code", [1, 4, 70, 99])
def test_fault_codes_fail(self, code: int) -> None:
"""Usage errors, failing baselines, and unknowns are faults."""
success, meaning = run_cargo.interpret_exit_code(code)
@pytest.mark.parametrize(
("code", "meaning"),
[
(1, "usage error"),
(4, "baseline tests failing"),
(70, "internal error"),
(99, "unexpected exit code"),
],
)
def test_fault_codes_fail(self, code: int, meaning: str) -> None:
"""Faults report their exact meaning; unknown codes fall back verbatim."""
success, actual = run_cargo.interpret_exit_code(code)
assert not success
assert meaning
assert actual == meaning, (
def test_informative_codes_succeed(self, code: int, meaning: str) -> None:
"""Informative codes succeed and report their exact contract meaning."""
success, actual = run_cargo.interpret_exit_code(code)
assert success, f"code {code} should be an informative success"
assert actual == meaning, (
f"code {code} should map to the exact meaning {meaning!r}"
)
`@pytest.mark.parametrize`(
("code", "meaning"),
[
(1, "usage error"),
(4, "baseline tests failing"),
(70, "internal error"),
(99, "unexpected exit code"),
],
)
def test_fault_codes_fail(self, code: int, meaning: str) -> None:
"""Faults report their exact meaning; unknown codes fall back verbatim."""
success, actual = run_cargo.interpret_exit_code(code)
assert not success, f"code {code} should be classified as a fault"
assert actual == meaning, (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workflow_scripts/tests/test_mutation_run_cargo.py` around lines 104 - 125,
Add failure messages to both assertions in test_informative_codes_succeed and
test_fault_codes_fail, including the tested code and expected contract
classification so mismatches clearly identify the failing context.

Sources: Coding guidelines, Path instructions

Comment on lines 291 to +299
@POSIX_SHIMS_ONLY
def test_failing_baseline_propagates_exit_code(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, fake_uv: Path
self,
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
fake_uv: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
"""A non-zero mutmut run fails the step with mutmut's own code."""
"""A non-zero mutmut run fails the step and logs to stderr."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 --type=py 'def fake_uv\b|`@pytest`\.fixture' workflow_scripts/tests

Repository: leynos/shared-actions

Length of output: 2083


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '180,320p' workflow_scripts/tests/test_mutation_run_mutmut.py

Repository: leynos/shared-actions

Length of output: 6044


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
path = Path("workflow_scripts/tests/test_mutation_run_mutmut.py")
for start, end in [(190, 220), (285, 305)]:
    print(f"--- {start}-{end} ---")
    for i, line in enumerate(path.read_text().splitlines(), start=1):
        if start <= i <= end:
            print(f"{i:4d}: {line}")
PY

Repository: leynos/shared-actions

Length of output: 2467


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n '\bfake_uv\b' workflow_scripts/tests/test_mutation_run_mutmut.py

Repository: leynos/shared-actions

Length of output: 470


Activate fake_uv via usefixtures. Remove the unused fake_uv parameter from test_failing_baseline_propagates_exit_code and add @pytest.mark.usefixtures("fake_uv") so the setup side effects stay in place without Ruff ARG002.

🧰 Tools
🪛 Ruff (0.15.21)

[warning] 296-296: Unused method argument: fake_uv

(ARG002)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workflow_scripts/tests/test_mutation_run_mutmut.py` around lines 291 - 299,
Update test_failing_baseline_propagates_exit_code by removing the unused fake_uv
parameter and adding pytest.mark.usefixtures("fake_uv") alongside the existing
decorator, preserving the fixture setup side effects without triggering Ruff
ARG002.

Sources: Coding guidelines, Linters/SAST tools

codescene-access[bot]

This comment was marked as outdated.

leynos added 8 commits July 24, 2026 12:59
Address the mutation-testing survivors harvested in #342, covering the
cargo-mutants runner (`mutation_run_cargo`) and the shard-report
summariser (`mutation_summarize_cargo`). These scripts render the
estate's survivor tables, so gaps here corrupt downstream triage.

- Add shard-count boundary cases (1 and 2) so the ``--shard`` emission
  threshold is pinned, and assert exact exit-code meanings so the
  contract's meaning strings and unknown-code fallback are covered.
- Cover the summariser's survivor extraction placeholder path, its
  default field values, the timeout and unviable counts, root-first
  ordering, diagnostic emissions for skipped/missing/invalid artefacts,
  the non-halting scan past a leading foreign directory, and the exact
  job-summary Markdown layout.
- Annotate the locale-independent UTF-8 read as ``# pragma: no mutate``.
Address the mutation-testing survivors harvested in #341 for the mutmut
runner (`mutation_run_mutmut`), which parses mutmut output into the
survivor tables the estate relies on.

- Pin the module-glob translation: a file outside the strip prefix keeps
  its full module path, and only the trailing separator is stripped.
- Reject spaced names as noise, accumulate repeated status counts, and
  assert the exact survivor-table and summary Markdown layouts.
- Pin the ``uv run --with`` argument prefix, and assert the run and
  results sub-invocations both pin the version and carry the results
  arguments, that a clean run reports its command/exit-code/counts
  diagnostics under their exact keys, and that a failing baseline logs
  its error diagnostic to stderr with mutmut's own exit code.
- Annotate the partition/rpartition equivalence, the streaming-only
  RETCODE(FG=...) toggle, and the locale-independent UTF-8 writes as
  ``# pragma: no mutate``.

Two rstrip-variant mutants on the prefix-strip line are genuine
equivalents (PurePosixPath normalises trailing separators); they share a
line with a killable variant, so they are left to #341 rather than
masking the killable mutant with a pragma.
Address the retry and backoff boundary survivors harvested in #336. This
retry logic guards the dependabot auto-merge estate, so a weak boundary
here silently degrades transient-failure handling everywhere it is used.

- Pin the strict ``attempt < max_retries`` retry boundary and the
  exponential ``base * 2**attempt`` backoff schedule (previously
  no-tests) via a monkeypatched sleep.
- Pin the retry-or-fail decision: a remaining retry backs off on the
  current attempt, and an exhausted budget fails with the supplied
  message.
- Pin the request loop's attempt budget (one attempt plus three retries)
  and that the token, query, and variables reach each attempt unchanged.

The response-parsing, rate-limit, and status-code survivors, plus the
unreachable defensive-tail mutants and the equivalent ``range`` upper
bound, remain #336's remit and are deferred there.
Address the matrix-contract survivors harvested in #340 for the
change-detection guard, whose matrix output decides which shard mutates
what.

- Pin the full-run extra-crate entry shape (empty files, shard 0), the
  scoped entries' target directories, the sorted matrix-JSON key order,
  and that the skip summary names the base ref and window.
- Extract the root-first sort key into ``_root_first_key`` so its
  equivalent variants can be suppressed structurally with
  ``# pragma: no mutate`` (a trailing pragma cannot reach a multi-line
  ``sorted`` call), and annotate the locale-independent UTF-8 write.

The changed-files git-argument, slug, and remaining survivors stay
#340's remit and are deferred there to keep the PR within tolerance.
Match the Makefile formatter (unpinned ruff), which collapses the fake
execute signature onto a single line.
Add issue references to the docstrings of the new tests introduced for
the mutation-testing runner, matrix-contract, and summariser survivors,
so each kill can be traced back to #340, #341, and #342.
Keep retry arguments, matrix ordering, result delimiters, and summary
append behaviour observable to mutation testing. Add property coverage for
the retry and ordering invariants, and improve failure diagnostics in the
runner tests.

Resolve formatter drift in Python documentation and make the
`release-to-pypi-uv` test loader independent of ambient
`GITHUB_ACTION_PATH` values under xdist.
Keep validation and stop hooks from leaving the `.coverage` database as
an untracked worktree change.
codescene-access[bot]

This comment was marked as outdated.

Delete the coverage database introduced on `main` so validation cannot
rewrite tracked test state. The existing ignore rule keeps future coverage
runs from recreating a worktree change.
@lodyai
lodyai Bot force-pushed the kill-mutation-survivors branch from b5e5fb7 to 054912a Compare July 24, 2026 11:02
codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No quality gates enabled for this code.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@workflow_scripts/tests/test_mutation_properties.py`:
- Line 12: Update the mock import in test_mutation_properties.py to import mock
without the mock alias, satisfying Ruff PLR0402 while preserving existing mock
usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a5ae523-bfec-4c2d-b01e-71db84b30c66

📥 Commits

Reviewing files that changed from the base of the PR and between 4d696e7 and 054912a.

📒 Files selected for processing (24)
  • .coverage
  • .github/actions/release-to-pypi-uv/tests/_helpers.py
  • .gitignore
  • .rules/python-00.md
  • .rules/python-context-managers.md
  • .rules/python-exception-design-raising-handling-and-logging.md
  • .rules/python-generators.md
  • .rules/python-return.md
  • .rules/python-typing.md
  • docs/cmd-mox-users-guide.md
  • docs/execplans/2-4-5-extend-architecture-enforcement-to-orchestration-code.md
  • docs/execplans/support-cranelift-codegen.md
  • docs/local-validation-of-github-actions-with-act-and-pytest.md
  • docs/python-action-scripts.md
  • docs/scripting-standards.md
  • workflow_scripts/mutation_detect_changes.py
  • workflow_scripts/mutation_run_mutmut.py
  • workflow_scripts/mutation_summarize_cargo.py
  • workflow_scripts/tests/test_graphql_client.py
  • workflow_scripts/tests/test_mutation_detect_changes.py
  • workflow_scripts/tests/test_mutation_properties.py
  • workflow_scripts/tests/test_mutation_run_cargo.py
  • workflow_scripts/tests/test_mutation_run_mutmut.py
  • workflow_scripts/tests/test_mutation_summarize_cargo.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/polythene (auto-detected)


from __future__ import annotations

import unittest.mock as mock

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Import mock without an alias.

Replace the alias to satisfy Ruff PLR0402.

Apply the lint-compliant import
-import unittest.mock as mock
+from unittest import mock
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import unittest.mock as mock
from unittest import mock
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 12-12: Use from unittest import mock in lieu of alias

Replace with from unittest import mock

(PLR0402)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workflow_scripts/tests/test_mutation_properties.py` at line 12, Update the
mock import in test_mutation_properties.py to import mock without the mock
alias, satisfying Ruff PLR0402 while preserving existing mock usage.

Sources: Coding guidelines, Linters/SAST tools

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants