Tasks: Per-PR Sonar reuses CI Modules coverage

Mission: sonar-per-pr-coverage-reuse-01M2FR32 · Issue: #4334 Branch: issue-4334-sonar-reuse-shard-coverage · Created: 2026-09-14 Inputs: spec.md, plan.md, research.md, data-model.md, contracts/, quickstart.md Squad evidence: work/4334-sonar-coverage-reuse/squad-post-spec.md, work/4334-sonar-coverage-reuse/squad-post-plan.md

Lanes

LaneWork packagesCharacter
A — measurementWP01 → WP02Changes what is measured. Touches the matrix runner and the test inventory.
B — topology & enforcementWP03 → WP04 → WP05 → WP06Changes who reads the measurement. Touches the gate model, the reporting workflow, and the retirement.

The two lanes own disjoint file sets and can run concurrently. Within a lane the order is strict — each WP consumes its predecessor's observable output.

The one ordering that is not negotiable: WP04 (the regression battery) must land before WP06 (the retirement), so the battery's strongest mutation — "reintroduce the retiring step" — is proven red against live source rather than a synthetic fixture. Retiring first would leave only a fixture, reproducing the exact blindness the battery exists to close.

Preconditions (outside this mission's control)

Verified live: the project currently holds no coverage metric at all. This blocks the mission's publication claims only; every other requirement is independently verifiable.

  • #4350 — SonarCloud Automatic Analysis must be disabled before FR-015/SC-010 can be satisfied.

Subtask Index

IDDescriptionWPParallel
T001Red-first gate: every slice measures the full top-level package setWP01
T002Broaden the coverage-target derivation in the matrix runnerWP01
T003Measure and record aggregate cost: runner-minutes, artefact bytes, parse durationWP01
T004Record the per-file coverage baseline artefactWP01
T005Derive and commit the marker-mismatch exception set (FR-016)WP01
T006Prove no per-file coverage regression outside the declared exception setWP01
T007Commit a reproducible shard-timings capture scriptWP02
T008Measure durations for the two new directories; record the producing runWP02
T009Add the two inventory rows plus matching recognised-group entriesWP02
T010Wire the router filter groups for the two new rowsWP02
T011Re-freeze the non-vacuity floor and run the inventory gate batteryWP02
T012Prove both directories collect >0 tests and balancing uses fresh dataWP02
T013Red-first: the gate model cannot see indirect suite invocationsWP03
T014Resolve run: steps transitively to a suite invocationWP03
T015Reconcile the workflow allowlist with the widened detectionWP03
T016Rewrite the non-blocking rationale to describe what is actually detectedWP03
T017Register any new architectural test file in the shard mapWP03
T018Battery scaffold with a non-vacuity floor over discovered workflowsWP04
T019Mutations 1-3 proven RED against the live retiring stepWP04
T020Mutation 4: a net-new workflow file forces directory enumerationWP04
T021Mutations 5-6: removal of the non-blocking and same-origin declarationsWP04
T022Assert the unmutated live tree is cleanWP04
T023Red-first: behavioural tests for each execution-condition conjunctWP05
T024Extract source-tree delivery and identity resolution into a tested scriptWP05
T025Add the reporting job to the aggregation workflowWP05
T026Add the skipped-tolerant terminal verdict job and widen permissionsWP05
T027Re-check the downstream parse timeout against the enlarged artefact setWP05
T028Commit the pinning-rule derivation, its artefact, and a re-derive gateWP06
T029Retire the duplicate-measurement jobWP06
T030Execute every disposition in the derived inventoryWP06
T031Update the governance documents in lockstepWP06
T032Record issue-matrix verdicts and the post-integration observationWP06

WP01 — Broaden coverage measurement

Lane: A · Dependencies: none · Prompt: tasks/WP01-broaden-coverage-measurement.md · 236 lines

Goal: Make each matrix slice measure the full top-level package set, so a test that exercises another module's code stops having its measurement silently dropped. Establish the baselines every later acceptance claim reads.

Priority: P1 — everything downstream measures against this.

Independent test: Run one slice before and after; covered statements outside that slice's own module go from zero to substantial, and no file's covered set shrinks.

Why this is not a tidy-first step: broadening changes which files appear in coverage reports, which changes the blocking change-coverage gate's absent set and its denominator. This WP changes merge-blocking behaviour for every pull request in the repository. Treat it as the riskiest package, not the safest.

Included subtasks: T001 T002 T003 T004 T005 T006

Risks: the aggregate cost could exceed the saving (measured +9.9% on one slice — must be confirmed across the matrix); artefact size grows ~6×, and the set is downloaded twice.


WP02 — Declare the undeclared test directories

Lane: A · Dependencies: WP01 · Prompt: tasks/WP02-declare-test-directories.md · 174 lines

Goal: Give tests/unit and tests/specify_cli/runtime a declared home in the test inventory, so retiring the duplicate step does not remove their per-change execution.

Priority: P2 — they retain scheduled coverage, so nothing becomes wholly unrun.

Independent test: the inventory names both directories; a pull request runs their tests; the balancing guard passes against freshly measured data.

Included subtasks: T007 T008 T009 T010 T011 T012

Risks: a length mismatch between measured durations and collected tests silently degrades balancing to a test-count split and no gate notices; the original capture tooling no longer exists in-tree, so it must be re-authored rather than re-run.


WP03 — Teach the gate model to see indirect suite invocations

Lane: B · Dependencies: none · Prompt: tasks/WP03-detector-substrate.md · 150 lines

Goal: The duplicate-detection substrate is documented as structurally blind to the retiring step's invocation form. Close that blindness while the step still exists, so the regression battery can be proven against reality.

Priority: P1 — a battery built on the blind substrate is vacuous by construction.

Independent test: the model resolves a suite invocation reached through a make target or a script, and the workflow that carries the retiring step enters the detected set.

Included subtasks: T013 T014 T015 T016 T017

Risks: widening detection pulls a new workflow into a set-equality invariant, cascading into the coherence model — that cascade is the point, but it must be landed deliberately.


WP04 — Permanent regression battery

Lane: B · Dependencies: WP03 · Prompt: tasks/WP04-regression-battery.md · 168 lines

Goal: A permanent in-tree fault-injection battery that fails on every way the duplicate could return, and on removal of the two declarations that keep the report advisory.

Priority: P1 — this is the mission's close-the-defect-class-by-construction obligation.

Independent test: each mutation is demonstrated red; the unmutated live tree is clean; the rule's workflow set is asserted non-empty.

Included subtasks: T018 T019 T020 T021 T022

Risks: a battery fed only synthetic fixtures reproduces the blindness it was written to close — mutations 1-3 must run against the live tree while the retiring step is still present.


WP05 — The reporting job

Lane: B · Dependencies: WP04 · Prompt: tasks/WP05-reporting-job.md · 201 lines

Goal: Add the per-change reporting job to the aggregation workflow, consuming the measurement the matrix already produced, under an execution condition whose every conjunct is independently asserted.

Priority: P1 — the mission's functional core.

Independent test: the extracted condition evaluator refuses each of the five ways the job must not run, driven by synthetic payloads, before the job exists.

Included subtasks: T023 T024 T025 T026 T027

Risks: the analysis configuration must resolve from trusted content — an earlier design that placed sources in a subdirectory was refuted because the configuration file is located relative to the analysis base directory. The production trigger cannot execute before integration; that limit is declared, and the first post-integration run is observed, not assumed.


WP06 — Retire the duplicate, execute dispositions, move the documents

Lane: B · Dependencies: WP05 · Prompt: tasks/WP06-retire-and-disposition.md · 181 lines

Goal: Remove the duplicate-measurement job, and relocate, rewrite or deliberately retire every rule that pinned the old arrangement — via a derived inventory, never a transcribed one.

Priority: P1 — this is where the cost saving is realised.

Independent test: re-running the derivation reproduces the committed inventory; every rule carries a disposition; the duplicate no longer runs.

Included subtasks: T028 T029 T030 T031 T032

Risks: rules that go red are found by CI; rules that go greener by deletion are found by nobody. The earlier hand-built inventory was ~4× understated with four false positives — which is exactly why this WP derives rather than transcribes.


Parallelisation

manufacture parallelism that does not exist.

WP03 + WP04 alone deliver the regression guard. The cost saving needs the full Lane B chain.

  • Lane A and Lane B run concurrently. Disjoint file sets; neither reads the other's surfaces.
  • Within lanes the order is strict. The mission is genuinely mostly sequential and the plan does not
  • MVP scope: WP01 alone delivers the measurement-accuracy half and is independently valuable.

Recording progress

Subtask completion is event-sourced. Use:

spec-kitty agent tasks mark-status T001 T002 --status done

There are no checkboxes to tick; the reduced event log is the authority.