fix(usage-gate): fail closed when usage cannot be read - #274
Conversation
An unreadable endpoint with no fallback reading now pauses the tick (exit 10, one skip row) instead of running it. The pause reason — 'PAUSE: no usage reading — endpoint unreachable and no fallback reading set — cannot pace blind, failing closed' — shares no wording with the pace or ceiling pauses, so a blind pause is diagnosable from a single log or skip-row line. --force remains the operator's one-tick way past it, like any other POLICY pause. Prose describing the retired inert behavior is updated in the runners, cron.env.example, the gate's doc comments and the integration-test preamble; the unit test flips to pin the Pause verdict and its reason shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
WalkthroughThe usage gate now fails closed when usage cannot be read and no fallback exists. Rust logic returns a diagnostic pause, runner scripts preserve the pause, and documentation and tests describe the behavior. ChangesUsage gate fail-closed behavior
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.45.1)pr-review-report-rs/src/main.rsast-grep timed out on this file 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 |
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 `@pr-review-report-rs/src/main.rs`:
- Around line 10322-10332: Update no_reading_fails_closed_and_pauses to assert
that usage_gate_decide returns the UsageVerdict::Pause variant and compare its
complete reason string against the specified exact pause reason, replacing the
current starts_with and contains assertions while preserving the existing code
check.
🪄 Autofix
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: ASSERTIVE
Plan: Pro Plus
Run ID: d122464a-dee7-4e9e-89d3-c84cb335204f
📒 Files selected for processing (5)
campaign-run.shcron.env.examplepr-review-report-rs/src/main.rspr-review-report-rs/tests/usage_gate_skip.rsreview-run.sh
CodeRabbit on #274: fragment asserts let wording drift past the test and never proved the variant. Assert UsageVerdict::Pause and the exact reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Reviewed a8d906e: ready — single behavioral change is the None-reading arm of usage_gate_decide flipping Run→Pause (exit 10) per #273; shell/config/doc changes are prose-only; all unreadable shapes collapse to the one pause; mutation evidence in QA kills both the base-arm revert and a garbled reason; suite green in-build at this head; CodeRabbit thread addressed and resolved; CI fully green. |
|
Reviewed a8d906e: ready — single behavioral change is the None-reading arm of usage_gate_decide flipping Run→Pause (exit 10) per #273; shell/config/doc changes are prose-only; all unreadable shapes collapse to the one pause; mutation evidence in QA kills both the base-arm revert and a garbled reason; suite green in-build at this head; CodeRabbit thread addressed and resolved; CI fully green. |
Closes #273
The usage-gate's
None-reading arm now returnsUsageVerdict::Pause(exit 10 → one skip row, tick skipped) instead ofRun. The pause line —PAUSE: no usage reading — endpoint unreachable and no fallback reading set — cannot pace blind, failing closed— shares no wording with the pace or ceiling pauses, so a blind pause is diagnosable as an endpoint problem from a singlecampaign.log/review.log/ skip-row line.--forceremains the operator's one-tick way past it, like any other POLICY pause; the config REFUSAL path (exit 2) is untouched.No shell logic changes: the runners already branch on exit 10 and record the gate's own line verbatim on the skip row. Prose describing the retired inert behavior is rewritten to the fail-closed behavior in
campaign-run.sh,review-run.sh,cron.env.example, the gate's doc comments (usage_gate_decide,oauth_token,fallback_reading,fetch_usage, the #97 five-hour rationale) and theusage_gate_skip.rspreamble.Motivating incident: 2026-08-10T23:00Z, the only scheduled tick in 24h that executed did so at 39% used against a ~21% linear pace — every measurable tick that night paused, and the one tick the gate could not measure ran.
QA
no_reading_fails_closed_and_pauses(replacesno_reading_is_inert_and_runs) — fails on base, verified by restoring the base's exactNonearm as mutant 1 below: the mutant IS the pre-change code, and the test fails against it exactly as it would against a base checkout.usage_gate_decideNonearm →UsageVerdict::Pause(...)reverted to the base'sUsageVerdict::Run("OK: … gate inert")→ killed:no_reading_fails_closed_and_pausesFAILED, exit 101, panic at theassert_eq!(v.code(), 10)assertion, 1 test run / 1277 filtered. (2) Reason garbled toUsageVerdict::Pause("PAUSE: x")(right verdict, wording gone) → killed: same test FAILED, exit 101, thePAUSE:-prefix assert passes and thecontains("no usage reading")reason assert fires — the test pins the diagnosable wording, not just the exit code. Both mutants reverted; full suite green on the unmutated head:cargo testexit 0, incl. all 24usage_gate_skipintegration tests and 17usage_gate_testsunit tests.UsageVerdict::code()'s documented runner interface, and the reason-shape requirements from the issue's observability acceptance — none recomputed from the implementation.cron.env.examplefallback block,usage_gate_skip.rspreamble — found by a repo-wide sweep for the inert wording).unreadable_usage_shapes_never_produce_a_readingalready pins that malformed/expired/unparseable shapes collapse toNone, so the single flipped arm covers the whole failure category.Summary by CodeRabbit
Bug Fixes
--forceinvocation can bypass an exit-10 pause.Documentation