Skip to content

CI mutation runs one job per project #311

Closed

Description

@bitbiter-dev

Requirement

The CI Mutation job runs one Stryker job per mutation target project in parallel, so a pull request waits for the slowest project instead of one solution-wide sweep. The latest master run took 15:24, of which 6:35 was five identical coverage captures (Stryker 5.0 repeats the capture over every test for each project in solution mode; see ADR 0010).

Acceptance criteria:

  • A first step lists the target projects with scripts/mutation-targets.sh (from the scoped-run issue), so a new project is mutated without a workflow edit and none can be left out silently (fix(ci): Restore before Stryker and fail when it drops a project #306).
  • One matrix job per project runs scripts/stryker-sweep.sh --project <Name>.csproj at the configured concurrency: 4 and fails below the 100% thresholds. CI gate keeps needs: mutation.
  • Each job posts its own markdown summary and uploads its JSON report.
  • A merge job combines the reports with a new scripts/mutation-report.sh merge. It refuses (non-zero exit) when a source file appears in two reports, a test id maps to two names, or the schema versions differ or are unknown. Each refusal is pinned by a fixture test.
  • The badge and Pages are built from the merged JSON. Pages serves our own small page loading the merged JSON into a pinned mutation-testing-elements viewer, not Stryker's generated HTML.
  • mutation-guard.sh accepts --project only in the matrix job and mutation-changed.sh.
  • The weekly divergence check and the documented local full sweep stay single solution-mode runs.

First caller

The CI Mutation job on every pull request and master push, and publish / deploy-pages, which consume the merged report.

Design

Design note: #311 (comment), approved in #311 (comment). Rationale: ADR 0010 (docs/adr/0010-scoped-mutation-runs-per-project.md; the decision's "ADR 0009" became 0010 because 0009 was taken).

Activity

  1. bitbiter-dev commented on Oct 24, 2026

    @bitbiter-dev
    OwnerAuthor

    Design note

    Problem and constraints: Stryker 5.0's solution mode captures per-test coverage once per mutated project, and each capture runs all 1,620 tests of all 6 assemblies, ignoring the project. Five captures took 6:35 of a 15:24 CI run and recorded identical coverage. The gate (100%), the badge, the Pages report and the job summary need one result per run. The project list must not drift from the solution: #306 showed a project can drop out of mutation with only a warning.

    Options:

    1. Keep the solution sweep. No work. Every new project and test adds five times its coverage cost.
    2. Per-project runs, one after another, in one job. Measured locally: 7:13 against 6:21 for the solution sweep, because each run repeats the build and the initial test run. Slower.
    3. Per-project matrix jobs in parallel (recommended). Wall time is the slowest job: Core, 2:57 locally (every test project reaches Core); estimated about 7 min in CI. Costs: a report merge, a project-list step, guard changes, and about the same runner minutes as today.
    4. coverage-analysis: all. Captures coverage once, but every mutant then runs every test: 4:57 against 1:08 on Infrastructure alone.
    5. Patch Stryker upstream. Not pursued (maintainer's decision).

    Recommendation: option 3. It is the only option that shortens CI, and the merge is mechanical: the reports follow the public mutation-testing-report-schema v2, source files are disjoint across projects (0 overlap in all pairs), and test ids are content hashes, identical across reports (0 conflicts across 4,336 shared ids).

    First caller: the Mutation job; publish and deploy-pages read the merged JSON, so the merge's output format is that schema, unchanged.

    Hard to reverse: nothing; reverting to option 1 is a workflow change.

  2. bitbiter-dev commented on Oct 24, 2026

    @bitbiter-dev
    OwnerAuthor

    Decision

    2026-10-24, the maintainer, in a /grill-with-docs session:

    • Option 3, one matrix job per target project, in its own PR after the scoped-run issue.
    • The project list comes from the IsMutationTarget MSBuild convention through scripts/mutation-targets.sh, the same list the scoped run uses, so local and CI agree.
    • concurrency stays pinned at 4, so each job uses its whole runner and a local run measures like CI.
    • The merge refuses loudly on any inconsistency, and Pages serves our own viewer page instead of splicing Stryker's HTML.
    • The weekly divergence check and the local full sweep stay solution-mode runs.
    • ADR 0009 records the rationale.
  3. added
    ready-for-agentFully specified, ready for an AFK agent
    and removed
    needs-decisionWaiting on a decision from the maintainer
    on Oct 24, 2026
  4. added
    needs-triageMaintainer needs to evaluate this issue
    and removed
    ready-for-agentFully specified, ready for an AFK agent
    on Oct 24, 2026
  5. github-actions commented on Oct 24, 2026

    @github-actions

    Moved back to needs-triage: docs/agents/design-process.md ("What every issue carries") requires these parts before ready-for-agent:

    • Design: a "## Design" section or "Design:" field with a link to the approved design note (an #issuecomment link) or an ADR, or "Design: not needed because "

    Add them, then label the issue ready-for-agent again.

  6. added
    ready-for-agentFully specified, ready for an AFK agent
    and removed
    needs-triageMaintainer needs to evaluate this issue
    on Oct 25, 2026
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

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions