Skip to content

Coverage baseline + CI guardrail (tier-aware pipeline gate) #282

Description

@rucka

Story Statement

As a team maintaining a pair project
I want a coverage baseline plus a CI guardrail that blocks regressions below it, with configurable per-type targets
So that test coverage never silently drops as the codebase grows (R7.3)

Where: a coverage gate consumed by the tier-aware pre-merge pipeline (#258/#210); baseline + targets configured in adoption

Epic Context

Parent Epic: Code Quality & Testing foundations #209
Status: Refined
Priority: P1 (Should-Have)

Status Workflow

  • Refined: Story is detailed, estimated, and ready for development
  • In Progress: Story is actively being developed
  • Done: Story delivered and accepted

Acceptance Criteria

Functional Requirements

Given-When-Then Format:

  1. Given a project that has established a coverage baseline
    When a PR would reduce coverage below that baseline
    Then the coverage gate fails, blocking the merge (R7.3, epic AC3: "PR reducing coverage below baseline fails the gate")

  2. Given a PR that maintains or improves coverage
    When the gate runs
    Then it passes — the guardrail blocks regressions, it does not demand an absolute target be hit on every PR

  3. Given the coverage gate
    When it runs in CI
    Then it runs as a gate consumed by the tier-aware pre-merge pipeline (Tier-aware pre-merge pipeline — reads classification tags only, fail-safe red #258), reading its baseline/targets from adoption — not as a second, parallel CI mechanism independent of the tag-driven gate system (CI/CD gates tag-driven #210)

  4. Given a project with no coverage baseline established yet
    When the gate is first set up
    Then it establishes the current coverage as the baseline (or the developer sets one explicitly) rather than blocking everything at 0 — baseline bootstrapping is part of the story

  5. Given per-type coverage targets configured (e.g. different thresholds for backend vs. frontend vs. shared-lib)
    When the gate evaluates a PR
    Then it applies the target matching the touched code's type, per the configurable targets (R7.3 "gradual targets per type")

Business Rules

  • The guardrail blocks regressions below baseline, not "must hit X% absolute" — incremental improvement (R7.3), not a hard wall that fails a legacy codebase on day one
  • Baseline and per-type targets live in adoption (configurable), with KB-sensible defaults — test stack itself is team choice (the gate reads whatever coverage report the adopted test tooling produces)
  • The gate plugs into the tier-aware pipeline (Tier-aware pre-merge pipeline — reads classification tags only, fail-safe red #258), consuming classification like the other gates — it does not introduce its own separate CI orchestration
  • No obligation for a specific coverage tool — the gate consumes the adopted test framework's coverage output, whatever it is

Edge Cases and Error Handling

  • No coverage report produced (test tooling doesn't emit one, or tests didn't run): gate reports "coverage not measured" — fails safe (blocks) at red tier per the pipeline's fail-safe stance, warns at lower tiers, never silently passes
  • Baseline file missing/corrupt: gate re-establishes baseline from current coverage with a warning, rather than blocking everything
  • New code with genuinely untestable surface (generated files, config): per-type targets / exclusion config handles this, documented in adoption — never a blanket override that disables the guardrail

Definition of Done Checklist

Development Completion

  • All acceptance criteria implemented and verified
  • Coverage gate implemented and wired into the tier-aware pre-merge pipeline (Tier-aware pre-merge pipeline — reads classification tags only, fail-safe red #258)
  • Baseline + per-type target configuration in adoption, with KB defaults documented
  • Unit tests written and passing (gate pass/fail logic: regression blocked, improvement allowed, baseline bootstrapping, per-type target selection)
  • Code review completed and approved
  • Documentation updated — docs site (quality gates / testing section)

Quality Assurance

  • Fixture test: PR reducing coverage below baseline fails the gate; PR maintaining/improving it passes; baseline-bootstrapping path establishes a baseline on first run

Story Sizing and Sprint Readiness

Refined Story Points

Final Story Points: 5 (L)
Confidence Level: Medium
Sizing Justification: real gate logic (baseline storage, regression detection, per-type target resolution) + CI pipeline integration + config; more than a doc change, and coverage-report parsing varies by test tool

Sprint Capacity Validation

Sprint Fit Assessment: yes, single sprint
Total Effort Assessment: fits — Yes

Dependencies and Coordination

Story Dependencies

Prerequisite Stories: #258 (tier-aware pre-merge pipeline — the guardrail is a gate it consumes) / #210 epic; soft-relates to #232 (setup-gates, which provisions the CI gate config)
Dependent Stories: none

Validation and Testing Strategy

Acceptance Testing Approach

Testing Methods: unit tests on the gate's pass/fail logic (regression, improvement, bootstrap, per-type); fixture-repo integration test running the gate in a pipeline context with a seeded baseline
Test Data Requirements: fixture project with a coverage report and a baseline file; PR diffs that raise/lower coverage

Notes and Additional Context

Refinement Session Insights: 2026-07-11 — found during the epic-completeness audit as a planned R7.3 story (epic AC3) that was never created. Confirmed it plugs into the existing tier-aware pipeline (#258) as a gate rather than a standalone CI step, to avoid a second CI mechanism parallel to the tag-driven one.
Documentation Links: Requirements R7.3 · Spec G8 · epic #209 AC3 · pair-requirements-triage.md

Technical Analysis

Implementation Approach

Technical Strategy: a coverage gate that reads the adopted test tooling's coverage output, compares against a stored baseline (+ per-type targets from adoption), and reports pass/fail to the tier-aware pre-merge pipeline (#258). Baseline stored in the project (location per adoption); gate bootstraps a baseline on first run.
Key Components: coverage gate logic (regression detection, per-type target resolution, baseline bootstrap); adoption config for baseline/targets; integration point with #258's pipeline
Integration Points: #258 (tier-aware pipeline — the gate's consumer); adopted test framework's coverage report (input); #232 setup-gates (may provision the gate config)

Technical Requirements

  • Test-tool-agnostic coverage-report ingestion; deterministic pass/fail; fail-safe (block at red) when coverage can't be measured
  • Baseline + per-type targets configurable in adoption with KB defaults

Technical Risks and Mitigation

Risk Impact Probability Mitigation Strategy
Coverage report formats differ per test tool (lcov, cobertura, json, ...) Medium High Support the common formats the adopted stacks actually emit; document the expected format in adoption; degrade to "not measured" (fail-safe) on an unrecognized format
Baseline storage location / commit cadence unclear on a real project Medium Medium Define baseline location + update policy in adoption with a KB default; bootstrap on first run so the guardrail is never dead-on-arrival

Task Breakdown

Checklist

  • T1 — Coverage gate core logic: baseline comparison (regression blocks), per-type target resolution, baseline bootstrap on first run
  • T2 — Adoption config for baseline + per-type targets (KB defaults) + coverage-report ingestion for common formats
  • T3 — Wire the gate into the tier-aware pre-merge pipeline (Tier-aware pre-merge pipeline — reads classification tags only, fail-safe red #258); fail-safe behavior when coverage unmeasured
  • T4 — Unit + fixture tests (regression/improvement/bootstrap/per-type) + docs site update

Dependency Graph

T1 → T2 → T3 → T4

AC Coverage

Task AC1 AC2 AC3 AC4 AC5
T1
T2
T3
T4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions