Skip to content

ci: add quality gates for coverage and type checking#427

Open
raballew wants to merge 5 commits intojumpstarter-dev:mainfrom
raballew:040-quality-gates-v2
Open

ci: add quality gates for coverage and type checking#427
raballew wants to merge 5 commits intojumpstarter-dev:mainfrom
raballew:040-quality-gates-v2

Conversation

@raballew
Copy link
Copy Markdown
Member

@raballew raballew commented Apr 8, 2026

Summary

  • Enable branch coverage measurement (branch = true in [tool.coverage.run]) to catch untested if/else paths
  • Add diff-cover step to Python Tests workflow that enforces >=80% coverage on new/changed lines in PRs, preventing coverage regression without demanding an arbitrary threshold
  • Add ty type checking to the Linters workflow as a non-blocking job (continue-on-error: true) to surface type errors before merge -- can be made blocking once the 49 existing diagnostics are resolved
  • Add diff-cover to dev dependencies

Rationale

As AI-generated code contributions increase, automated quality gates that prevent regression are essential for maintaining confidence when accepting changes. These three changes are the highest-impact, lowest-effort improvements:

  1. Coverage ratchet via diff-cover: New/changed lines must be >=80% covered. This prevents coverage erosion without penalizing existing uncovered code.
  2. Branch coverage: Catches cases where only one side of an if/else is tested -- common in AI-generated error handling paths.
  3. Type checking visibility: Makes type errors visible on PRs even though they don't block merge yet.

Test plan

  • Python Tests workflow runs successfully with the fetch-depth: 0 and diff-cover step
  • diff-cover step only runs on pull_request events (skipped on push/merge_group)
  • diff-cover correctly reports coverage on changed lines
  • ty type-check job runs and reports results without blocking the workflow
  • Branch coverage data appears in coverage reports

🤖 Generated with Claude Code

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 8, 2026

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit 49fa303
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/69d7c330f6fcd60008e834cb
😎 Deploy Preview https://deploy-preview-427--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

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
📝 Walkthrough

Walkthrough

Added a CI type-check job that runs when Python paths change, added a PR-only diff-cover step to enforce 80% changed-lines coverage, and updated Python dev tooling to include diff-cover and enable branch coverage in coverage config.

Changes

Cohort / File(s) Summary
Workflow: linting / type-check
/.github/workflows/lint.yaml
Added type-check-python job gated on needs.changes.outputs.python == 'true'; checks out repo, installs uv via astral-sh/setup-uv and runs make ty in python/; job is continue-on-error: true.
Workflow: tests / coverage
/.github/workflows/python-tests.yaml
actions/checkout@v4 updated to fetch-depth: 0; Run pytest now exports PYTEST_ADDOPTS="--cov --cov-report=xml"; added PR-only step "Check coverage on changed lines" that finds python/packages/**/coverage.xml and runs uv run diff-cover --compare-branch=origin/${{ github.base_ref }} --fail-under=80.
Tooling / coverage config
python/pyproject.toml
Added diff-cover>=9.2.0 to dev dependencies and enabled branch = true under [tool.coverage.run].

Sequence Diagram(s)

(No sequence diagrams generated — changes are CI/workflow and tooling adjustments.)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • NickCao
  • kirkbrauer
  • bennyz

Poem

🐇 I hopped into CI with a cheerful bite,
Type checks nibble code by starlit night,
Diff-cover counts each changed-line score,
Branch checks hum and watch the coverage door,
Pipelines gleam — the rabbit's work is light.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: adding CI quality gates for coverage (diff-cover) and type checking (ty).
Description check ✅ Passed The description provides a comprehensive summary of changes, detailed rationale for each change, and a test plan directly related to the PR's objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@raballew raballew force-pushed the 040-quality-gates-v2 branch from 2bd2321 to b6c2149 Compare April 8, 2026 10:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In @.github/workflows/python-tests.yaml:
- Around line 103-107: The current workflow step "Check coverage on changed
lines" uses the glob packages/*/coverage.xml which only matches packages that
already emit XML, so changed packages without --cov-report=xml are skipped;
modify the job to first compute the set of changed packages (git diff
--name-only origin/${{ github.base_ref }}...HEAD), then for each changed package
check for packages/<pkg>/coverage.xml and for any missing run that package's
tests to produce an XML report (e.g., run pytest or the package's test script
with --cov-report=xml) before invoking diff-cover, or alternatively fail the job
with a clear message if a changed package cannot produce a coverage XML; update
the step that runs diff-cover to pass only the discovered coverage.xml files
rather than the static glob.
🪄 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: CHILL

Plan: Pro

Run ID: 4c49de4b-965b-4e2a-a494-9ebd5413583d

📥 Commits

Reviewing files that changed from the base of the PR and between b6c2149 and d0a1e4d.

📒 Files selected for processing (1)
  • .github/workflows/python-tests.yaml

raballew and others added 4 commits April 9, 2026 17:00
… checking

Enable branch coverage measurement, add diff-cover to enforce >=80%
coverage on changed lines in PRs, and add ty type checking to the lint
workflow (non-blocking until existing diagnostics are resolved).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Packages without test coverage XML are now surfaced as GitHub Actions
warnings instead of silently skipping. Also handles the case where no
coverage.xml files exist at all.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use PYTEST_ADDOPTS to ensure all packages produce coverage.xml,
not just the ones that inherit the root pytest config. This closes
the gap where changed lines in 30 packages were silently skipped
by diff-cover.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@raballew raballew force-pushed the 040-quality-gates-v2 branch from 8e0664b to 8d6eb29 Compare April 9, 2026 15:00
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
.github/workflows/python-tests.yaml (1)

109-114: ⚠️ Potential issue | 🟠 Major

Changed-package coverage artifacts are still not validated before gating.

At Line 109-114, the step gathers existing coverage.xml files, but it does not verify that each changed package under python/packages/* produced one. That means changed packages missing coverage.xml can still slip through this gate.

Proposed hardening
       - name: Check coverage on changed lines
         if: github.event_name == 'pull_request'
         working-directory: python
         run: |
-            coverage_files=$(find packages -name coverage.xml 2>/dev/null | sort)
-            if [ -z "$coverage_files" ]; then
+            mapfile -t coverage_files < <(find packages -name coverage.xml 2>/dev/null | sort)
+            if [ ${`#coverage_files`[@]} -eq 0 ]; then
               echo "::error::No coverage.xml files found"
               exit 1
             fi
-            uv run diff-cover $coverage_files --compare-branch=origin/${{ github.base_ref }} --fail-under=80
+
+            mapfile -t changed_packages < <(
+              git diff --name-only "origin/${{ github.base_ref }}"...HEAD \
+                | sed -nE 's#^python/packages/([^/]+)/.*#\1#p' \
+                | sort -u
+            )
+
+            missing=0
+            for pkg in "${changed_packages[@]}"; do
+              if [ ! -f "packages/${pkg}/coverage.xml" ]; then
+                echo "::error::Changed package missing coverage.xml: ${pkg}"
+                missing=1
+              fi
+            done
+            [ "$missing" -eq 0 ] || exit 1
+
+            uv run diff-cover "${coverage_files[@]}" --compare-branch=origin/${{ github.base_ref }} --fail-under=80
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/python-tests.yaml around lines 109 - 114, The workflow
currently collects coverage.xml into coverage_files but doesn’t ensure every
changed package under python/packages/* produced one; enhance the step to (1)
compute the set of changed package names (e.g., via git diff against origin/${{
github.base_ref }} for paths under python/packages/), (2) for each changed
package verify a coverage.xml exists (e.g., look for
python/packages/<pkg>/coverage.xml or the path you expect) and fail with a clear
::error:: if any are missing, and only then run the existing uv run diff-cover
$coverage_files --compare-branch=origin/${{ github.base_ref }} --fail-under=80;
reference the variables/commands coverage_files, python/packages/*, coverage.xml
and the diff-cover invocation when locating where to add these checks.
🧹 Nitpick comments (1)
.github/workflows/python-tests.yaml (1)

105-106: Run diff-cover once instead of on every matrix leg.

Line 105-106 currently executes this PR coverage gate in every OS/Python matrix job. Consider limiting it to a single canonical matrix combination to reduce CI time and duplicate noise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/python-tests.yaml around lines 105 - 106, The "Check
coverage on changed lines" step currently runs in every matrix leg; restrict it
to a single canonical matrix combination by adding an additional condition to
its if expression (e.g., require a specific matrix value like matrix.os ==
'ubuntu-latest' and matrix.python-version == '3.11') so the step only executes
for that one leg while keeping the existing github.event_name == 'pull_request'
check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/workflows/python-tests.yaml:
- Around line 109-114: The workflow currently collects coverage.xml into
coverage_files but doesn’t ensure every changed package under python/packages/*
produced one; enhance the step to (1) compute the set of changed package names
(e.g., via git diff against origin/${{ github.base_ref }} for paths under
python/packages/), (2) for each changed package verify a coverage.xml exists
(e.g., look for python/packages/<pkg>/coverage.xml or the path you expect) and
fail with a clear ::error:: if any are missing, and only then run the existing
uv run diff-cover $coverage_files --compare-branch=origin/${{ github.base_ref }}
--fail-under=80; reference the variables/commands coverage_files,
python/packages/*, coverage.xml and the diff-cover invocation when locating
where to add these checks.

---

Nitpick comments:
In @.github/workflows/python-tests.yaml:
- Around line 105-106: The "Check coverage on changed lines" step currently runs
in every matrix leg; restrict it to a single canonical matrix combination by
adding an additional condition to its if expression (e.g., require a specific
matrix value like matrix.os == 'ubuntu-latest' and matrix.python-version ==
'3.11') so the step only executes for that one leg while keeping the existing
github.event_name == 'pull_request' check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a5005dd-e56b-4f4e-b815-8056a169d412

📥 Commits

Reviewing files that changed from the base of the PR and between 6683aff and 8d6eb29.

⛔ Files ignored due to path filters (1)
  • python/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • .github/workflows/lint.yaml
  • .github/workflows/python-tests.yaml
  • python/pyproject.toml
✅ Files skipped from review due to trivial changes (1)
  • python/pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/lint.yaml

The read_pty_output reader could exit (via stop flag) without draining
remaining data from the kernel PTY buffer. This caused flaky test
failures on macOS when coverage overhead changed async task timing.

Also removes redundant PYTEST_ADDOPTS env var from CI since the root
pyproject.toml addopts already includes --cov --cov-report=xml.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@raballew
Copy link
Copy Markdown
Member Author

@ambient-code why is CI failing?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant