ci: trigger eval-calibration on the whole package (#1160) - #1195
Conversation
There was a problem hiding this comment.
Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Warning Review limit reached
Next review available in: 42 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe pull-request trigger for eval calibration now covers the full ChangesEval calibration gate
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Reviewer's GuideExpands the eval-calibration CI gate’s PR path filter from three specific modules to the entire aelfrice package, and adds tests that derive the dependency set from eval_harness imports to ensure the workflow’s triggers cover all code that affects the calibration metric while keeping the push trigger unconditional. Flow diagram for widened eval-calibration CI path filter and guard testflowchart TD
PR["PR changes files"] --> F1{Files under src/aelfrice/**?}
PR --> F2{Files under benchmarks/posterior_ranking/**?}
PR --> F3{Changes to eval-calibration.yml or pyproject.toml?}
F1 -->|yes| RUN["Run eval-calibration workflow on pull_request"]
F2 -->|yes| RUN
F3 -->|yes| RUN
F1 -->|no| SKIP1["Do not run eval-calibration on PR"]
F2 -->|no| SKIP2["Do not run eval-calibration on PR"]
F3 -->|no| SKIP3["Do not run eval-calibration on PR"]
RUN --> EVAL["Execute aelf eval --json"]
EVAL --> CMP["Compare output to benchmarks/posterior_ranking/baseline.json"]
subgraph Test_guard
TSTART["tests/test_eval_calibration_gate.py"] --> TWALK["ast.walk over eval_harness imports"]
TWALK --> TMODS["Derive reachable modules from retrieve stack"]
TMODS --> TCHECK["Assert all reachable modules are matched by src/aelfrice/** filter"]
end
TCHECK --> TFAIL["Test fails if workflow paths drift from reachable modules"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/test_eval_calibration_gate.py`:
- Around line 122-129: Update the path-matching helper used by
test_pr_filter_covers_every_module_the_metric_depends_on, specifically
_path_is_included, to process globs in declaration order: matching positive
patterns should include the path, while matching negated patterns should exclude
it, allowing later positive matches to re-include it. Replace any
order-insensitive any-based matching while preserving the existing default
behavior for paths with no matches.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 7c00f5dd-dc19-4c3c-b87b-af143091c23b
📒 Files selected for processing (3)
.github/workflows/eval-calibration.ymlCHANGELOG/v4.mdtests/test_eval_calibration_gate.py
|
[claim:review:Setr:2026-07-30T16:53:56Z] |
|
Approve. I re-ran both empirical claims rather than taking the tables on faith; both hold. Hidden coverage — confirmedMutated Byte-different from the pinned baseline. The posterior finding — confirmed, and it is the bigger problemReproduced on Byte-identical across a 0→5 sweep, and identical to the pin. Worth stating more bluntly than the PR does: a gate living at Verified
Notes, none blocking
Merge mechanicsNeeds a rebase — #1200 merged and moved |
The PR path filter named eval_harness.py, calibration_metrics.py and cli.py. But `aelf eval` measures `retrieve()` — eval_harness.py:167 imports it, :182 calls it — so the pinned metric depends on the retrieval and scoring stack that the list omitted. A PR editing only `scoring.py` never triggered the job, merged, and then the unconditional push-to-main trigger re-asserted the baseline and turned main red with no owning PR. The omission hides real regressions, verified by mutation: negating the bm25 term in `scoring.py` moves roc_auc 0.8444 -> 0.7347 and spearman 0.5241 -> 0.3572, which the byte-exact baseline assertion would catch if it ran. Filters on `src/aelfrice/**` rather than a longer enumeration, because the enumeration is what drifted and the coupling is not stable: the harness pins `l1_limit` and passes entity_index_enabled=False, bfs_enabled=False, so which modules are live moves with the call — the `DEFAULT_L1_LIMIT` and `DEFAULT_K1` constants are both unreachable today. Over-triggering costs a 0.45-0.68 s measurement; under-triggering costs a red main. `ci.yml` already filters on the package. Refs #1160.
Walks imports from eval_harness.py and asserts every reachable aelfrice module matches the workflow's pull_request paths filter — 25 modules today, including retrieval, scoring and bm25. Derives the set rather than restating module names, because the enumeration is what drifted into missing them. The walk uses ast.walk, not tree.body: the harness reaches `retrieve` through an import inside a function body, so a top-level-only scan misses precisely the dependency #1160 is about. A vacuity test pins that, since an empty walk would satisfy the coverage assertion for free, and the filter parser refuses to run unless it finds exactly one pull_request trigger. Also pins the push-to-main trigger as unconditional. That asymmetry is what makes a PR-side subset filter dangerous rather than merely incomplete — it converts a skipped check into a red main with no owning PR — so if it ever grows a paths filter the reasoning needs revisiting. Mutation-verified: restoring the three-module list fails the coverage test, narrowing the walk fails the vacuity test, and adding a paths filter to the push trigger fails the third. Refs #1160.
d255507 to
63e715c
Compare
|
Rebased onto Note for whoever picks up #1191: once this lands, any PR touching |
|
[release:review:Setr:2026-07-30T17:08:39Z] |
|
merge-train: blocked 1 review thread(s) are unresolved on these files: tests/test_eval_calibration_gate.py. Resolve them on the PR (click 'Resolve conversation' on each) and re-add the label. The |
GitHub evaluates a `paths:` list in order, so a later `!pattern` excludes a path an earlier positive matched. Matching with `any()` ignored negation, which would have read `src/aelfrice/**` followed by `!src/aelfrice/scoring.py` as covering scoring.py while the job actually skipped it — the guard failing open in the case it exists to catch. The filter carries no negated entry today; this keeps the check correct if one is added. Raised by CodeRabbit on #1195.
|
CodeRabbit's negated-glob finding is valid, so I applied it in It is latent — the filter has no Now matched in declaration order, mirroring how GitHub evaluates Still FF on |
PR-size soft capThis PR is over the advisory size threshold:
Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the |
|
merge-train: merged b0de048 → |
Second acceptance criterion of #1160 — "Path filters include the modules each gate calibrates." PR #1194 did the
ci.ymlhalf; this is theeval-calibration.ymlhalf. The umbrella stays open.The defect
eval-calibration.ymlpinsaelf eval --jsonbyte-for-byte againstbenchmarks/posterior_ranking/baseline.json. Its PR trigger filtered on three modules:But the metric is produced by
retrieve()—eval_harness.py:167imports it,:182calls it — so the whole retrieval and scoring stack sat outside the trigger. 25 modules are reachable from the harness; three were named.The
push:trigger carries nopaths:key, and that asymmetry is what makes this a broken-main bug rather than merely thin coverage:The hidden coverage is real
Measured on main, reverting each mutation after:
roc_aucspearman_rhoscoring.pyThe byte-exact assertion catches that the moment it is allowed to run.
Why
src/aelfrice/**and not a longer list#1160 offered either option. Enumerating is what drifted, and the coupling is not stable enough to enumerate safely — the harness pins
l1_limitand passesentity_index_enabled=False, bfs_enabled=False, so I measuredDEFAULT_L1_LIMIT(50 → 3) andDEFAULT_K1(1.5 → 9.0) as byte-identical no-ops today. Which modules are live moves with the call. Over-triggering costs a 0.45–0.68 s measurement (timed, 3 runs); under-triggering costs a red main.ci.ymlalready filters on the package.The guard derives its expectation
tests/test_eval_calibration_gate.pywalks imports from the harness and asserts every reachable module matches the filter. It usesast.walk, nottree.body, because theretrieveimport sits inside a function body — a top-level-only scan misses exactly the dependency at issue, which is why the mutation test for that is included. Vacuity is pinned too: an empty walk would satisfy the coverage assertion for free.Deliberately a new test module rather than an addition to
tests/test_ci_path_filter.py, so this PR and #1194 can merge in either order without conflicting. Worth consolidating once both land.Verification
6237 passed, 69 skipped(6234 baseline + 3 new).tree.bodyfails the vacuity test; addingpaths:to the push trigger fails the third. No mutation left the suite green.src/aelfrice/**, so I ranaelf evalon the fix(retrieval): demote or exclude superseded beliefs, both arms behind a flag (#1187) #1191 (retrieval.py) and fix(lifecycle): dispose of aelfrice's own artifacts in ~/.aelfrice/ (#1186) #1190 branches: both byte-identical to the pinned baseline. No merge-order landmine.Not fixed here — the same gate is still blind to its own name
benchmarks/posterior_ranking/provides zero coverage of posterior ranking. Measured:Byte-identical, and equal to the pinned baseline.
eval_harness.py:129-131builds every calibration belief atalpha=0.5, beta=0.5, soposterior_meanis constant and the posterior term is a constant offset that cannot reorder anything. Disabling the Bayesian rerank entirely would pass this gate.That is a separate acceptance criterion — it needs varied per-belief posteriors in
default.jsonland therefore a deliberate baseline recut, which is a judgement call I have not made unilaterally. This PR makes the gate run on the right code; it does not make it sensitive to the posterior blend.Summary by Sourcery
Broaden the eval-calibration CI gate to track changes across the whole aelfrice package and add tests that enforce the workflow’s triggers cover all modules contributing to the calibrated metric while keeping the push trigger unconditional.
CI:
Documentation:
Tests:
Summary by CodeRabbit
Bug Fixes
Documentation
Tests