fix(bench): fail the band-check closed when nothing was measured (#1160) - #1200
Conversation
`summarize` returned PASS whenever no leaf was FAIL or WARN. Ignoring an individual SKIP is correct and ratified (#479) — one uncomputable metric is not a regression — but the rollup ignored every SKIP, so a run in which nothing could be computed reported success. That is the shape a failed dataset download on the runner takes: every adapter exits, every leaf becomes SKIP, counts are {pass:0, warn:0, fail:0, skip:12}, and the verdict was PASS. An empty check list returned PASS too. PASS is a claim that something was measured and stayed in band, so it now requires at least one leaf that actually passed; otherwise the verdict is NO_DATA. #479 is preserved exactly — a SKIP beside any real PASS still rolls up to PASS, and FAIL/WARN still dominate, because a leaf compared to its band is evidence that measurement happened. NO_DATA is a separate verdict rather than FAIL because the two demand different responses: a regression means read the diff, no data means fix the runner. Leaf counting also moved to `.get` so an unexpected verdict is reported rather than raising KeyError inside the gate meant to report it. Refs #1160.
The step exited 0 for anything that was not 'fail', so the new NO_DATA verdict would have been inert and the gate would still have reported a zero-measurement run as success. Exits 1 on 'fail' or 'no_data'; 'warn' stays non-blocking, unchanged. Refs #1160.
Covers the all-SKIP run, the empty check list, and the #479 invariant this change could most easily have broken (a SKIP beside a real PASS still passes). Also pins WARN-with-no-PASS as WARN rather than NO_DATA, since a leaf compared to its band is evidence measurement happened, and asserts the workflow accounts for 'no_data' — without that the verdict is inert and the gate fails open exactly as before. Mutation-verified: reverting the rollup fails 3 of these; reverting the workflow exit line fails the inertness test. Refs #1160.
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: 29 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 (4)
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 GuideIntroduce a NO_DATA verdict for band-check rollups that have no passing measurements, update the nightly workflow to fail on NO_DATA, and add tests ensuring summarize and the workflow treat absent data as a closed failure while preserving prior SKIP behavior (#479). Sequence diagram for bench-canonical workflow handling NO_DATA verdictsequenceDiagram
actor Runner
participant BenchCanonicalWorkflow
participant tolerance_summarize as tolerance.summarize
participant sys as sys
Runner->>BenchCanonicalWorkflow: run bench-canonical.yml
BenchCanonicalWorkflow->>tolerance_summarize: summarize(checks)
tolerance_summarize-->>BenchCanonicalWorkflow: overall=NO_DATA, counts
alt overall.value in ('fail','no_data')
BenchCanonicalWorkflow->>sys: sys.exit(1)
else overall.value in ('pass','warn')
BenchCanonicalWorkflow->>sys: sys.exit(0)
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
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 |
|
[claim:review:Setr:2026-07-30T16:44:52Z] |
|
Approve. Verified independently rather than on the PR body's word. The defect reproduces. The tests are load-bearing. Reverting #479 is intact. The 28 pre-existing Blast radius confirmed. Mechanics: 4 signed commits, FF on One residual, for the umbrella rather than this PRThe band-check step is gated That is the same acceptance criterion — absent data, gate open — approached from the other side. This PR closes the all-SKIP shape; the crash shape is untouched. Not a reason to hold the merge (it's pre-existing and orthogonal to the rollup logic), but it should be tracked, and I've noted it on #1160. Related, same area: the closing step at line 168 looks unreachable. Nit, not worth a respin
On the deferred halfDeclining to infer band direction from metric names is the right call. A metric assigned the wrong direction is blind to regressions in the real one, which is a worse failure than the false alarm it would prevent. Leaving it for an explicit table is correct. |
|
[release:review:Setr:2026-07-30T16:49:17Z] |
|
merge-train: merged 4e66e15 → |
Fourth acceptance criterion of #1160 — "Band-check fails closed on absent data." Does that half; the one-sided-bands half is deliberately left open, with reasons below. The umbrella stays open.
The defect
tolerance.summarizereturned PASS whenever no leaf was FAIL or WARN. Ignoring an individual SKIP is correct and ratified (#479) — one uncomputable metric is not a regression — but the rollup ignored every SKIP. Measured on main:That first row is exactly what a failed dataset download on the runner produces: every adapter exits because its data dir is absent, every leaf becomes SKIP, counts read
{pass: 0, warn: 0, fail: 0, skip: 12}— and the verdict was PASS.bench-canonical.ymlthen exited 0 on anything that was notfail, so the nightly went green having measured nothing.The fix
PASS is a claim that something was measured and stayed in band, so it now requires at least one leaf that actually passed. Otherwise the rollup is a new
NO_DATAverdict, and the workflow exits 1 on it.#479 is preserved exactly. A SKIP beside any real PASS still passes, and FAIL/WARN still dominate — a leaf that was compared to its band is evidence measurement happened, which is why WARN with no PASS stays WARN rather than becoming
NO_DATA.NO_DATAis a distinct verdict rather than reusing FAIL because the two demand different operator responses: a regression means read the diff, no data means fix the runner. Reusing FAIL would also repeat this umbrella's own complaint about reporting a structural zero as if it were a measurement.Blast radius checked before touching the vocabulary:
summarizehas exactly one production consumer (bench-canonical.yml:109-114) plus two test modules.benchmarks/badge.pyconsumes no verdicts, and the otherVerdictclasses in the repo (reason.py,relationship_detector.py, …) are unrelated types.Leaf tallying also moved to
.get, so a leaf carrying an unexpected verdict is counted and reported rather than raisingKeyErrorinside the gate whose job is to report it.Verification
6263 passed, 69 skipped(7 new).test_bench_tolerance*.pytests pass unchanged — that was the check that mattered, since [v2.1] Bench dispatcher exit-code 3-state contract (ok / skipped / error) #479 is the invariant most at risk.summarizealone leaves the verdict inert and the gate still fails open.Not done: one-sided bands
The same criterion also asks for one-sided bands so an improvement is not classified as a failure. I have not done that, and the reason is not effort.
It needs a per-metric direction table — quality metrics improve upward, latency metrics downward. Inferring direction from metric names would be a guess, and a metric assigned the wrong direction becomes blind to regressions in the real direction — the exact class of defect this umbrella exists to remove. Trading a false failure for a silent blind spot is a bad trade to make implicitly, so it wants a deliberate decision on the direction of each canonical metric. Happy to build the table if you want to specify it, or to propose one for review.
Summary by Sourcery
Ensure the nightly reproducibility band-check fails closed when no metrics were measured by introducing a NO_DATA verdict and treating it as a failing outcome in the canonical bench workflow.
Bug Fixes:
Enhancements:
Documentation:
Tests: