Skip to content

0.3.10: a shard matches its own package, and zero matches says so - #69

Merged
KayhanB21 merged 2 commits into
masterfrom
0310-shard-lookup
Oct 3, 2026
Merged

KayhanB21 merged 2 commits into
masterfrom
0310-shard-lookup

Conversation

@KayhanB21

@KayhanB21 KayhanB21 commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

A coverage_map shard written from its package directory now matches, and a coverage report that matches no scanned file says so. This is PR 2 of 0.3.10, based on master after #68.

The findings

These were reproduced on c1f64d4. The fixture is a monorepo with packages/a and packages/b, a [tool.riskratchet.coverage_map] table with one coverage file (a shard) per package, and each shard written by pytest --cov run from the package directory.

  • The shards never match. A shard's keys are src/amod.py. MultiCoverageData.lookup picks the shard by prefix and then asks it for packages/a/src/amod.py. Every function scores 0% line coverage.
  • Nothing says so. coverage_status reads present whenever a report loads. check prints No risk regressions detected., the PR comment shows 0% on every row, and only stderr has one warning per file. doctor reports the case as coverage-overlap, and no other command does.

Together these mean a monorepo that follows the README has a gate that ignores coverage: a PR that deletes every test moves no score.

The fix

  • MultiCoverageData.lookup tries the repository-relative path first, then the path relative to the shard's prefix. The second lookup is exact (CoverageData.lookup_exact), so packages/alpha/core.py can't pick up an unrelated vendored/core.py by basename.
  • RiskReport gains coverage_checked_files and coverage_unmatched_files: the Python files with at least one function that the engine looked up in a present report, and how many had no entry. DiffReport carries the resulting note, because the check renderers get the diff and never the report.
  • When no file matched, scan, baseline, check, diff, explain, and the pytest plugin print one warning. The scan summary line, the markdown report's **Coverage:** line, and the Baseline: line of every check format carry coverage matched 0 of N scanned files.
  • A partial match prints nothing, so no existing snapshot moves.

What changes for an adopter

Scores drop in a repository whose shards matched nothing: coverage_gap and branch_gap fall to their real values. check reports those functions as improved, so no gate fails. The CHANGELOG states this under Changed and says to regenerate the baseline after upgrading.

The following don't change: the gate verdict, exit codes, SCORING_MODEL_VERSION, and all nine JSON schemas. coverage_status stays present, and the two new fields aren't serialized. The note is a count, so it's safe under redaction.

Tests

18 new tests in tests/test_coverage.py, tests/test_coverage_overlap.py, and tests/test_pytest_plugin.py. Against the v0.3.9 src/, 5 fail directly and the 10 in tests/test_coverage_overlap.py fail at import. The other 3 guard behavior that must stay the same: the repository-relative key wins, a path outside every prefix doesn't match, and an empty prefix has no second spelling. The suite has 1391 passed and 2 skipped, with 93.28% coverage.

Baseline bump rationale

regenerate-baseline.yml regenerated the baseline in CI (run 37102003419, commit fdb19bd). It goes from 567 to 574 entries, and the scoring block doesn't change. 29 of the 567 existing entries change score. The largest rise is +3.25, under fail_regression_above = 5.

  • New (7 functions): all score 5.8 or less. The highest is models.py::coverage_overlap_note at 5.8.
  • Churn only (23 entries): this PR shifts spans in cli.py, engine.py, models.py, and summary.py.
  • Edited here (6 functions): MultiCoverageData.lookup +3.25 (one more branch for the second lookup), engine.py::analyze +3.12 (the two counters), pytest_sessionfinish +2.39, diff +1.25, _summary_line +0.32, and baseline_line +0.19.
  • This repository uses one coverage file, so the lookup change moves none of its own scores.

🤖 Generated with Claude Code

A coverage_map shard written by `pytest --cov` from its package directory
keys its files as `src/m.py`. The lookup asked the shard for
`packages/alpha/src/m.py` only, so the per-package setup the map exists for
scored every function at 0% coverage.

- MultiCoverageData.lookup tries the repository-relative path first, then
  the path relative to the shard's prefix, as an exact match.
- RiskReport counts the Python files it looked up in a present coverage
  report and how many had no entry. When none matched, scan, baseline,
  check, diff, explain, and the pytest plugin print one warning, and the
  scan summary line, the markdown report, and the Baseline: line of every
  check format carry "coverage matched 0 of N scanned files".

Scores drop where a shard matched nothing before. The verdict, the scoring
model version, and all nine JSON schemas don't change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

riskratchet

Regressions: 2 · New above threshold: 0 · Regressed: 0 · Existing above threshold: 0 · Component regressed: 2 · Above threshold: 0
Since the baseline: Changed: 2 · New: 7 · Ambiguous renames: 0 · Improved: 0 · Moved: 0 · Removed: 0
Baseline: 567 entries · 567 compared · 0 not seen this run

Kind Function Before After Delta Reason
component_regressed src/riskratchet/coverage.py::MultiCoverageData.lookup 7.2 10.5 +3.2 churn grew by +20.0 (from 10.0 to 30.0); component tolerance is +15.0
component_regressed src/riskratchet/coverage.py::MultiCoverageData.from_map 1.2 3.2 +2.0 churn grew by +20.0 (from 0.0 to 20.0); component tolerance is +15.0
New functions (7)
Status Function Before After Delta Reason
new src/riskratchet/models.py::coverage_overlap_note n/a 5.8 n/a function is absent from baseline with score 5.8
new src/riskratchet/coverage.py::_prefix_relative_hit n/a 3.5 n/a function is absent from baseline with score 3.5
new src/riskratchet/cli.py::_warned_about_unmatched_coverage n/a 2.2 n/a function is absent from baseline with score 2.2
new src/riskratchet/models.py::RiskReport.coverage_warning n/a 2.2 n/a function is absent from baseline with score 2.2
new src/riskratchet/reporting/markdown.py::_coverage_note_suffix n/a 2.2 n/a function is absent from baseline with score 2.2
new src/riskratchet/coverage.py::CoverageData.lookup_exact n/a 1.0 n/a function is absent from baseline with score 1.0
new src/riskratchet/models.py::RiskReport.coverage_note n/a 1.0 n/a function is absent from baseline with score 1.0
Unchanged functions (565)
Status Function Before After Delta Reason
unchanged src/riskratchet/cli.py::diff 37.5 37.5 +0.0 risk unchanged at 37.5
unchanged src/riskratchet/typescript.py::_anon_class_name 33.8 33.8 +0.0 risk unchanged at 33.8
unchanged src/riskratchet/cli.py::scan 32.5 32.5 +0.0 risk unchanged at 32.5
unchanged src/riskratchet/pytest_plugin.py::_guarded_typescript_identity 31.5 31.5 +0.0 risk unchanged at 31.5
unchanged src/riskratchet/cli.py::explain 27.5 27.5 +0.0 risk unchanged at 27.5
unchanged src/riskratchet/diagnostics.py::Diagnostics.to_lines 26.8 26.8 +0.0 risk unchanged at 26.8
unchanged src/riskratchet/config.py::_resolve_coverage 23.8 23.8 +0.0 risk unchanged at 23.8
unchanged src/riskratchet/reporting/markdown.py::render_report_pr_comment 23.8 23.8 +0.0 risk unchanged at 23.8
unchanged src/riskratchet/config.py::_load_config_strict 23.5 23.5 +0.0 risk unchanged at 23.5
unchanged src/riskratchet/cli.py::baseline 23.2 23.2 +0.0 risk unchanged at 23.2
unchanged src/riskratchet/pytest_plugin.py::_typescript_override 23.0 23.0 +0.0 risk unchanged at 23.0
unchanged src/riskratchet/pytest_plugin.py::_report_or_fail 22.8 22.8 +0.0 risk unchanged at 22.8
unchanged src/riskratchet/git.py::_normalize_diff_path 22.5 22.5 +0.0 risk unchanged at 22.5
unchanged src/riskratchet/typescript_engine.py::_count_lines 22.5 22.5 +0.0 risk unchanged at 22.5
unchanged src/riskratchet/cli.py::_should_run_baseline 22.2 22.2 +0.0 risk unchanged at 22.2
unchanged src/riskratchet/baseline/io.py::runtime_scoring_block 21.0 21.0 +0.0 risk unchanged at 21.0
unchanged src/riskratchet/baseline/io.py::_weight_reasons 20.8 20.8 +0.0 risk unchanged at 20.8
unchanged src/riskratchet/baseline/regressions.py::regressions_from_diff 20.6 20.6 +0.0 risk unchanged at 20.6
unchanged src/riskratchet/baseline/compare.py::compare 20.1 20.1 +0.0 risk unchanged at 20.1
unchanged src/riskratchet/cli.py::config_show 20.0 20.0 +0.0 risk unchanged at 20.0
... 545 more hidden.

@KayhanB21
KayhanB21 merged commit da588ed into master Oct 3, 2026
18 checks passed
KayhanB21 added a commit that referenced this pull request Oct 3, 2026
…gates (#70)

Bump the version to 0.3.10 in pyproject.toml, uv.lock, init.py ACTION_REF,
and tests/test_release_integrity.py. Update the README pins and move the
CHANGELOG entries for #68 and #69 under [0.3.10] - 2026-10-02.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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