test(architecture): add bounded mutation pilot#2241
Conversation
Add an exact-function mutmut lane for four stable canonical owners, ratchet reviewed survivors, and schedule advisory nightly evidence. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 42596632-8392-4c21-a8e4-ea562251e900
There was a problem hiding this comment.
Pull request overview
Establishes a bounded, advisory mutation-testing pilot (mutmut pinned to 3.6.0) targeting four “canonical owner” seams, with a checked-in survivor baseline, deterministic JSON reporting, and a nightly/manual GitHub Actions workflow. This adds guardrail tests around the pilot runner plus several new unit tests that close survivor-proven behavioral gaps.
Changes:
- Add
scripts/run_mutation_pilot.pyrunner to execute allowlisted mutmut patterns, normalize outcomes to deterministic ASCII JSON, and enforce a reviewed survivor baseline. - Add an advisory nightly/manual workflow with an exact-input cache and artifact upload for the JSON report.
- Pin mutmut in dev dependencies and add baseline + unit tests to ratchet survivor behavior.
Show a summary per file
| File | Description |
|---|---|
uv.lock |
Adds mutmut and its dependency graph (e.g., Textual/Rich) to the locked dev environment. |
pyproject.toml |
Pins mutmut==3.6.0, adds [tool.mutmut] allowlist config, and sets pytest default testpaths = ["tests"]. |
scripts/run_mutation_pilot.py |
New mutation pilot runner: executes mutmut for exact patterns, reads metadata, compares to baseline, writes deterministic JSON. |
tests/mutation/baseline.json |
New checked-in baseline schema with explicit owner contract and accepted survivors list. |
tests/unit/scripts/test_run_mutation_pilot.py |
New unit tests validating runner behavior (pattern generation, name normalization, baseline validation/ratchet semantics). |
tests/unit/test_skill_subset_persistence.py |
Adds subset-selection behavior tests to kill survivors (blank entries, invalid skill names). |
tests/unit/test_dep_targets_persistence.py |
Adds target suggestion/validation tests and edit-distance contract assertions. |
tests/unit/install/test_plan.py |
Adds update-plan behavioral assertions for completeness, ordering, display identity, and unresolved-branch behavior. |
tests/unit/policy/test_cache_merged_effective.py |
Adds policy serialization coverage for sparse bin_deploy shapes. |
tests/unit/compilation/test_link_resolver_phase3.py |
Adds link-resolver projection edge case tests (directories, preserved roots, external source package projection). |
docs/src/content/docs/contributing/development-guide.md |
Documents how to run the pilot, how caching works, and how to treat the reviewed baseline. |
CONTRIBUTING.md |
Adds contributor-facing instructions for running and interpreting the bounded mutation pilot. |
.gitignore |
Ignores mutants/ cache directory generated by mutmut. |
.github/workflows/mutation-pilot.yml |
New scheduled/manual workflow to run the pilot with cache + report artifact upload. |
Review details
- Files reviewed: 12/14 changed files
- Comments generated: 1
- Review effort level: Low
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 2 | 3 | Sound single-runner design; keep the runtime owner map and mutmut config parity guard load-bearing. |
| CLI Logging Expert | 0 | 1 | 2 | Output is consistent; cached and fresh execution should be distinguishable in logs. |
| DevX UX Expert | 0 | 0 | 3 | Contributor flow is pragmatic; improve recovery hints and mode discoverability. |
| Supply Chain Security Expert | 0 | 1 | 1 | Read-only, secret-free workflow is bounded; action pinning is a repo-wide follow-up. |
| OSS Growth Hacker | 0 | 0 | 1 | Contributor onboarding is solid; mirror the anti-baseline-gaming warning in the docs site. |
| Doc Writer | 0 | 2 | 2 | Document --report-only and reduce future drift between the two contributor surfaces. |
| Test Coverage Expert | 0 | 2 | 1 | Scenario tests pass; credential stripping and execution modes need direct runner tests. |
| Performance Expert | 0 | 3 | 1 | Add fixture inputs to the cache key and fail closed on every incomplete mutmut status. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
- [Test Coverage Expert] Add a unit test for
_sanitized_environment-- focused proof prevents a credential-strip regression from silently reaching mutant subprocesses. - [Performance Expert] Hash
tests/conftest.pyandtests/unit/install/conftest.py-- fixture-only changes must invalidate cached mutation metadata. - [Performance Expert] Treat mutmut
skippedandtype_checkedresults as incomplete/fatal -- the checked population must not silently shrink. - [Test Coverage Expert] Add runner tests for
--report-onlyand--reuse-cache-- both documented modes need direct branch-level regression traps. - [CLI Logging Expert + Doc Writer] Document
--report-onlyand announce cached versus fresh execution -- the contributor and hosted logs should state which mode ran.
Architecture
classDiagram
direction LR
class Owner {
<<ValueObject>>
+key str
+source str
+functions tuple
+test_seams tuple
+module str
+patterns tuple
}
class RunMutationPilot {
<<Module>>
+OWNERS tuple
+FATAL_STATUSES frozenset
+_run_mutmut() float
+_owner_report() dict
+_validate_baseline() dict
+_compare_with_baseline() tuple
+main() int
}
class RatchetBaseline {
<<SharedModule>>
+load_baseline()
+write_baseline()
}
class PilotError
class BaselineError
RunMutationPilot *-- Owner : owns four scopes
RunMutationPilot ..> RatchetBaseline : delegates baseline IO
RunMutationPilot ..> PilotError : fails execution
RatchetBaseline ..> BaselineError : rejects corrupt baseline
class Owner:::touched
class RunMutationPilot:::touched
classDef touched fill:#fff3b0,stroke:#d47600
flowchart TD
A["Nightly, manual, or local invocation"] --> B{"Report only?"}
B -- No --> C["mutmut run with exact function patterns"]
B -- Yes --> D["Reuse existing metadata"]
C --> E["Read per-mutant metadata"]
D --> E
E --> F["Normalize names and group by owner"]
F --> G["Validate explicit baseline contract"]
G --> H{"New survivor or incomplete result?"}
H -- Yes --> I["Write deterministic regression report and exit nonzero"]
H -- No --> J["Write deterministic accepted report and exit zero"]
Recommendation
All five bounded panel follow-ups, the Copilot signal fix, and the coordinator's tie-break plus metadata/report boundary follow-up are folded. Keep the exact-head-green PR frozen for maintainer review; do not merge.
Full per-persona findings
Python Architect
- [recommended] The runtime
OWNERStuple and pyproject mutmut lists are two required representations of scope.
The passing parity test is load-bearing. A static boundary check would provide defense in depth, but is not necessary for this bounded lane. - [recommended]
--update-baselinewrites the reviewed allowlist without a dry-run preview.
The explicit flag and documented review rule bound the risk; a future preview mode could improve governance without adding interactivity. - [nit] Pass
--max-children 2explicitly in the workflow so its cost decision is visible. - [nit] Include
scripts/ratchet_baseline.pyin the cache key. - [nit] Add a tool-version mismatch unit test beside the schema-version check.
CLI Logging Expert
- [recommended]
--reuse-cachehas no script-level announcement.
Cached and fresh runs currently produce the same summary, so an operator cannot tell which path ran at the point of use. - [nit] Use
[i]rather than[!]when accepted survivors become killed. - [nit] Echo the workflow cache decision beside the runner invocation.
DevX UX Expert
- [nit] The
mutmut show <id>recovery hint does not map directly from the report's canonical mutant name. - [nit] The default report path differs from every documented explicit
--outputexample; always printing the report path would remove ambiguity. - [nit]
--report-onlyis discoverable only through--help.
Supply Chain Security Expert
- [recommended]
astral-sh/setup-uv@v6is a mutable third-party action tag.
This matches existing workflows and the new lane is read-only, scheduled/manual, secret-free, and usespersist-credentials: false; repo-wide SHA pinning is the coherent follow-up. - [nit] The explicit credential-name strip can miss future secret variable names.
Keep the known-name list documented and guard it with a focused unit test rather than broadly stripping environment suffixes that tests may require.
OSS Growth Hacker
- [nit] The docs-site guide omits CONTRIBUTING's "Do not update it to make a run green" warning.
Auth Expert -- inactive
The PR touches workflow/test tooling and contributor docs only; no AuthResolver, token precedence, host classification, downloader, authorization, or remote-fallback behavior changed.
Doc Writer
- [recommended]
--report-onlyis implemented but absent from both contributor docs. - [recommended] The two contributor sections are near-duplicates and can drift.
A concise cross-reference should clarify the canonical detailed procedure without removing either required contributor surface. - [nit] Show the explicit
--update-baselinecommand. - [nit] Align the baseline reference/link style across both files.
Test Coverage Expert
- [recommended]
_sanitized_environmenthas no focused unit test.
Proof (missing at):tests/unit/scripts/test_run_mutation_pilot.py::test_sanitized_environment_removes_all_sensitive_names-- would prove mutation subprocesses do not receive live credentials. [secure-by-default] - [recommended]
--report-onlyand--reuse-cachehave no direct branch tests.
Proof (missing at):tests/unit/scripts/test_run_mutation_pilot.py::test_report_only_skips_mutmut_execution-- would prove metadata-only mode does not launch mutmut. [devx,oss] - [nit] The tool-version mismatch validation branch lacks a sibling test.
Proof (missing at):tests/unit/scripts/test_run_mutation_pilot.py::test_validate_baseline_rejects_tool_version_mismatch-- would prove stale tool baselines fail closed. [governed-by-policy]
Performance Expert
- [recommended] Captured mutmut output leaves little live evidence before the hosted timeout.
A concise start/mode announcement is a lower-noise bounded improvement; full streaming can remain a later operational decision. - [recommended] The cache key omits
tests/conftest.pyandtests/unit/install/conftest.py.
Both affect selected tests, so cached metadata can become stale after fixture-only changes. - [recommended]
skippedandtype_checkedare not fatal statuses.
A killed mutant can move into either bucket without becoming a new survivor, reducing checked behavior while the report remains accepted. - [nit]
timeout_constant = 1.0may be tight on cold shared runners.
Keep the measured zero-timeout value for the pilot and tune only if hosted evidence shows noise.
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
Docs sync advisoryVerdict: no_change * Pages affected: 0 * LLM calls: 1/15 * Took: not recorded SummaryNo documentation changes are needed beyond the contributor guidance already included in this PR. The L0 path gate found no product user-surface changes: the PR changes mutation tooling, tests, workflow/configuration, and contributor documentation only. The Starlight contributor page is a meta/contributor surface outside the consume/produce/govern narrative, and the PR already updates it alongside How this advisory was produced
This is an advisory comment from the Re-run by removing and re-applying the |
Fold review findings for fail-closed outcome handling, cache completeness, direct runner tests, and contributor mode guidance. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 42596632-8392-4c21-a8e4-ea562251e900
Track SIGKILL outcomes separately from segmentation faults and keep both fail-closed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 42596632-8392-4c21-a8e4-ea562251e900
…tests
Folds two coordinator-accepted follow-ups from panel review:
1. Add a regression test pinning the exact tie-break contract in
_closest_target: 'coade' ties at edit distance 2 with both real
KNOWN_TARGETS 'claude' and 'codex' (no other target is closer),
and production must deterministically pick the lexicographically-
first match encountered during the scan ('claude'). This kills
the two previously-accepted survivors _closest_target__mutmut_12
(strict < swapped to <=, which would pick the last tied candidate)
and _mutmut_13 (distance compared against the candidate name
instead of the running-best distance).
2. Add direct tests for the mutation-pilot runner's real metadata and
report boundary: _load_exit_codes against corrupt JSON, a missing
exit_code_by_key map, wrong-type exit code values, no matching
mutants, a malformed canonical name (real fnmatch-reachable via
the trailing wildcard), and the defensive duplicate-canonical-name
guard; plus _write_report's atomic, deterministic, ASCII-safe
output and its failure-cleanup path.
Ran a fresh (non-cached) mutmut execution: 480 mutants total, 32
survivors (down from 34), with both target mutants now killed and
zero unexpected survivors or fatal outcomes. Updated
tests/mutation/baseline.json to drop exactly the two resolved
survivors. No production source changed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eef94cf3-d677-4802-8391-978b41797444
Pin deterministic target suggestion ties and harden the mutation metadata/report trust boundary with direct parser and atomic-write tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 42596632-8392-4c21-a8e4-ea562251e900
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 42596632-8392-4c21-a8e4-ea562251e900
add(test-architecture): establish a bounded mutation pilot
TL;DR
This adds an advisory mutmut 3.6.0 lane for four stable canonical owners, with exact-function execution, an explicit survivor ratchet, deterministic JSON evidence, and a nightly/manual workflow. Survivor analysis strengthened only observed behavioral gaps, reducing the measured baseline from 101 to 32 reviewed survivors without changing production owner behavior. The final cold run checks 480 mutants in 41.3 seconds and has no timeout, suspicious, skipped, type-checked, unchecked, or no-test outcomes.
Important
This workflow is intentionally not required PR CI. It runs nightly or by manual dispatch, has a 20-minute hosted budget, and must not be added to the merge gate during the pilot.
Problem (WHY)
Why these matter:
Approach (WHAT)
Implementation (HOW)
.github/workflows/mutation-pilot.ymlmutants/cache, frozen install, 20-minute cap, and 14-day report artifact.scripts/run_mutation_pilot.pypyproject.toml/uv.locktests/.tests/mutation/baseline.jsontests/unit/scripts/test_run_mutation_pilot.pytests/unit/test_skill_subset_persistence.py/tests/unit/test_dep_targets_persistence.pytests/unit/install/test_plan.pytests/unit/policy/test_cache_merged_effective.pybin_deployshapes survive canonical policy serialization.tests/unit/compilation/test_link_resolver_phase3.pyCONTRIBUTING.md/ development guide.gitignoreProduction owner files are mutation inputs only; this PR does not modify them.
deps/lockfile.py, ADO surfaces,critical_suite.toml, TM002, README, and CHANGELOG remain untouched.Diagrams
Legend: The yellow block is the new advisory execution path; review the hard fail boundary between normalized metadata and the uploaded report.
sequenceDiagram participant W as Nightly or manual workflow participant C as Exact-input cache participant R as run_mutation_pilot.py participant M as mutmut 3.6.0 participant A as Report artifact rect rgb(255, 247, 200) W->>C: restore mutants cache C-->>W: hit or miss W->>R: run with optional --reuse-cache R->>M: exact owner function patterns M-->>R: per-mutant metadata R->>R: normalize and compare survivor allowlist R-->>A: sorted timestamp-free JSON end Note over R,A: New survivors or incomplete outcomes fail the advisory jobTrade-offs
Benefits
Validation
scripts/run_mutation_pilot.py:Repository validation evidence
uv run --extra dev pytest -q tests/unit tests/test_console.py -x:uv run --extra dev pytest -p no:cacheprovider -q tests/quality:uv run --extra dev pytest tests/spec_conformance -p no:randomly --no-header -q:uv run apm audit --ci:Canonical lint and owner boundaries:
Exact-head hosted checks at
c2d27eccac0a7780666552b6e68c5ac3cb88b178:Scenario Evidence
tests/unit/test_skill_subset_persistence.py::TestSkillSubsetFilterTokens::test_blank_name_does_not_hide_later_namestests/unit/test_dep_targets_persistence.py::test_parse_targets_suggests_name_at_edit_distance_boundarytests/unit/test_dep_targets_persistence.py::test_parse_targets_tie_break_prefers_lexicographically_first_matchtests/unit/install/test_plan.py::TestBuildUpdatePlan::test_entries_preserve_full_state_and_canonical_action_ordertests/unit/install/test_plan.py::TestBuildUpdatePlan::test_non_default_host_display_names_preserve_action_contexttests/unit/policy/test_cache_merged_effective.py::test_policy_serializer_preserves_sparse_bin_deploy_shapestests/unit/compilation/test_link_resolver_phase3.py::TestResolveInPackageAssetLink::test_link_inside_preserved_source_root_stays_bundle_relativetests/unit/compilation/test_link_resolver_phase3.py::TestResolveInPackageAssetLink::test_external_source_package_projects_into_base_dirtests/unit/compilation/test_link_resolver_phase3.py::TestResolveInPackageAssetLink::test_directory_candidate_returns_nonetests/unit/scripts/test_run_mutation_pilot.py::test_load_exit_codes_raises_on_non_object_roottests/unit/scripts/test_run_mutation_pilot.py::test_load_exit_codes_raises_on_wrong_type_exit_code_valuetests/unit/scripts/test_run_mutation_pilot.py::test_write_report_preserves_existing_file_when_atomic_replace_failsHow to test
uv sync --frozen --extra dev-- the locked mutmut 3.6.0 environment installs without resolving new versions.uv run --frozen --extra dev python scripts/run_mutation_pilot.py --output mutation-pilot-report.json-- it reports 480 total mutants, 32 reviewed survivors, and zero fatal outcomes.mutation-pilot-report.json--statusisaccepted, owner names are sorted, and no timestamp or machine-specific path appears.--reuse-cacheand compare reports -- the JSON is byte-identical.uv run --extra dev pytest -q -n0 tests/unit/scripts/test_run_mutation_pilot.py-- all runner contract tests pass.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com