Skip to content

fix(scoped): a failed refresh keeps the last good model limits - #70

Open
bpavlina wants to merge 2 commits into
leeguooooo:mainfrom
bpavlina:fix/scoped-limits-survive-blip
Open

bpavlina wants to merge 2 commits into
leeguooooo:mainfrom
bpavlina:fix/scoped-limits-survive-blip

Conversation

@bpavlina

@bpavlina bpavlina commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Problem

scoped_usage.refresh() records every failure as limits=[] with a fresh ts, and cached_limits() trusts that for 5 minutes, so a single timeout or keychain hiccup blanks the per-model segment until the next retry, even when good data is seconds old. Repro on 3.44.0: seed a good cache 301s old, make the fetch raise TimeoutError, call refresh() → cached_limits() returns [].

Fix

  • On failure, keep the previous limits and write ok_ts (last successful fetch) alongside ts.
  • cached_limits() applies its existing 10-minute window to ok_ts instead of ts.
  • One blip no longer shows; a real outage still hides the segment after 10 minutes; retry backoff is unchanged (ts still advances on every attempt). Old cache files without ok_ts fall back to ts.
  • Behaviour change: after logout or token revocation, the last limits stay visible for up to 10 minutes instead of vanishing on the first failed refresh.

Tests

  • test_scoped_failed_refresh_keeps_last_good_limits: one failed refresh keeps the data; data past ok_ts + 600s is hidden. Fails on main.
  • test_scoped_successful_refresh_stamps_ok_ts: a mocked successful fetch stamps ok_ts and serves the parsed limits (fails if the ok_ts stamp is dropped).

Full suite: 1246 passed.

Summary by CodeRabbit

  • Bug Fixes
    • Recent usage limits remain available after a failed refresh, but are hidden once the last successful fetch is more than 10 minutes old.
    • Refresh retries continue to be scheduled after failures, and successful refreshes make updated limits available.

refresh() negative-cached every failure as limits=[] with a fresh ts, so a
single timeout or keychain hiccup blanked the per-model segment for the full
5-minute backoff, even with good data seconds old.

Failures now keep the previous limits and record ok_ts, the time of the last
successful fetch. cached_limits() bounds the data by ok_ts (same 10-minute
window as before), so one blip is invisible while a real outage still hides
the segment. Retry backoff is unchanged: ts still advances on every attempt.
The data is now bounded by ok_ts, so a regression that stopped stamping it
would hide the segment for good after ten minutes; nothing covered the
successful fetch before. Behaviour note: after logout or token revocation
the last limits stay visible up to ten minutes instead of vanishing on the
first failed refresh.
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d5325f5f-92d1-44b6-bf0d-4d645cc26178
📥 Commits

Reviewing files that changed from the base of the PR and between b4d99bc and 937c229.

📒 Files selected for processing (2)
  • src/claude_statusbar/scoped_usage.py
  • tests/test_performance_pipeline.py

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.


📝 Walkthrough

Walkthrough

Scoped usage cache refreshes now preserve prior limits when a fetch fails. Cache reads use separate timestamps for retry scheduling and successful-result freshness. Tests cover failed and successful refreshes.

Changes

Scoped usage cache

Layer / File(s) Summary
Cache refresh and freshness
src/claude_statusbar/scoped_usage.py, tests/test_performance_pipeline.py
Failed refreshes retain cached limits and their last successful-fetch timestamp while updating the retry timestamp. Cache reads schedule refreshes using retry age and return limits only when the successful fetch is less than 600 seconds old. Tests cover both failed and successful refreshes.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: leeguooooo

Merge Risk: ⚪ Minimal · up to 937c2

No merge-blocking risk is identified for the scoped-usage cache change; it is mergeable after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 937c2

Previously fetched limits can remain visible after credential failures, but only within the existing ten-minute freshness window. Account checks and bounded freshness remain in place. No security regression was established in the examined paths; broader security coverage remains incomplete.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The established changed exposure is retention of account-specific quota-display metadata through failed refreshes. The identified consumer renders that metadata; the inspected path does not use retained limits to grant authorization or credential authority.

Trust Boundaries and Controls

  • observed — Account identity selects the hashed cache path and is checked before fetch and persistence. The token is read from local credentials or the platform credential store and sent to a fixed HTTPS endpoint with redirects disabled. Persisted state contains timestamps and limits, not the token.

Resilience and Maintainability Implications

  • observed — Repeated refresh failures do not renew an existing successful-result timestamp, preventing failure retries from extending retained-result visibility indefinitely. The added tests demonstrate retention after one failure, expiration beyond 600 seconds, and successful-fetch timestamping.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a failed scoped-usage refresh preserves the last good model limits.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

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