You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(anthropic): show inactive model-scoped quota rows without routing on them - #8186
On the Claude account list, an account whose Fable weekly limit is not the binding one shows no Fable bar, while Claude's own usage page shows it. Claude's usage endpoint reports that row as an inactive weekly_scoped limit and, for current accounts, sends no legacy seven_day_fable key at all. A real Max response (values verbatim, identifiers removed):
anthropicUsageBuckets dropped scoped rows with is_active: false on purpose for routing (#8088), and nothing else published them, so no path could draw the bar.
Inactive scoped rows are now published as display-only ProviderQuota.displayWindows, carrying weeklyAllowancePercent from weekly_scoped_shares. Routing, exhaustion, cooldown recovery, pool assembly and reset ordering still read only customWindows; a test pins that their output is identical with and without display rows, including a 100% display-only Fable row. The binding weekly_all (91%) still routes.
The account bars show Fable's own percent (4%, never the weekly-scale used_percent_of_weekly), its reset time, the line "Up to 50% of your weekly limit" (Claude's wording), and a neutral "Not in effect now" tag. Any Fable bar with a reported share, active or not, now shows the share line. Strings are in all eleven locales.
The decision follows the response, never the plan name: a Fable row shows on Pro, Max 5x, Team or Ultra alike, and a response with no Fable row (the Team/Max shapes in the fixtures) gets no bar. No plan-name logic gates quota windows.
Display-only rows stay on the account card. The GUI projection keeps them in a separate AccountQuota.displayWindows field, so the quota summary headline and severity, tray rows and urgency sorting, which read customWindows, never see them. A routed row with the same label (a later rate-limit header observation) wins.
The Claude CLI provider reads the same usage endpoint, so it gets the same rows; its on-disk snapshot loader now validates displayWindows like customWindows. A weekly share read back from disk (account cache or snapshot) must be a number in (0, 100]. ocx provider quota and ocx account refresh print display-only rows after routed windows through terminalSafeText, e.g. Fable 4% (up to 50% of weekly; not in effect).
Docs: structure/providers/anthropic-account-pool.md, structure/providers/claude-cli-replay.md, and the Claude Code guide.
Refs #8110 (the display half; this PR deliberately leaves routing on inactive scoped rows unchanged).
This carries @junhoi-pk's change (b4fd3f8) and resolves the maintainer hold on it: the display-only rows no longer reach the summary headline or tray, the weekly share is attached, and the screenshot is below. It also merges dev, including #8295 (Mistral budget display), which touched the same quota files; both features are kept.
Before (dev): the same Max account shows only the 5-hour and weekly bars.
After: Fable appears at 4% with its reset, its weekly share, and the not-in-effect tag.
Captured from the real ProviderAccountQuota component with the production CSS bundle (dark theme).
bun test tests/providers/provider-anthropic-usage-windows.test.ts: the owner-response case failed without weeklyAllowancePercent on the display row; an out-of-range cached share was kept. Both pass now.
bun test tests/providers/provider-quota-claude-cli.test.ts: a malformed displayWindows snapshot, and one with an out-of-range share, reached /api/provider-quotas; both are rejected now.
cd gui && bun test tests/provider-quota-model-id.test.ts tests/quota-display-only-window.test.tsx: a display-only-only report became the summary headline, and the bars had no share or tag; both pass now.
bun test tests/cli/cli-headless-parity.test.ts: the CLI line omitted the display row and printed an out-of-range share; both pass now.
Server-side focused set (Anthropic usage windows and adapters, Claude CLI quota and admission, CLI parity and key quota, Mistral budget, layout, Lab boundary, import-cycle, file-size and structure guards): 1,897 pass, 0 fail.
bun run test:changed at 085d25b (1,823 files): 42,635 pass, 108 skip, 31 fail, none in code this PR touches. 16 passed when their files ran alone (load timeouts). 15 Codex launcher cases failed identically on unmodified origin/dev in the same environment and pass on a later rerun after the host's OpenCodex service was restored, so they reflected local host state. Hosted CI covers the final head.
Reviews: independent code and security reviews at 085d25b (PASS WITH NOTES; the low security finding on unbounded disk shares was fixed with tests, the P3 notes were rebutted), then fresh final code and security reviews at 34fe145 (both PASS).
Checklist
Scope stays focused and avoids unrelated cleanup.
Docs or release notes were updated when needed.
Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
Required local validation passed; commands, results, and any full-suite exception are documented.
I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
I resolved all correct Codex and CodeRabbit findings.
Inactive Anthropic model-scoped quota rows are now reported as display-only windows. The CLI and GUI show these windows with their weekly allowance share and inactive status, while quota routing continues to use active windows.
Changes: Anthropic quota window extraction and retention
Layer / File(s)
Summary
Separate inactive rows from routed quota data src/providers/quota-types.ts, src/providers/quota/anthropic-usage-windows.ts, src/providers/quota/account-cache.ts, tests/fixtures/anthropic-usage-shapes.ts, tests/providers/provider-anthropic-usage-windows.test.ts, structure/providers/anthropic-account-pool.md
The Anthropic usage parser returns inactive scoped rows separately and attaches recognized weekly-share data to routed and display-only rows. Cache normalization applies to both window types. Tests check that display-only rows do not affect routing, quota calculations, or reset ordering.
Propagate display windows through quota reports src/providers/claude-cli-usage.ts, src/providers/claude-cli-usage-state.ts, src/cli/account-api.ts, src/cli/account-extended.ts, tests/providers/provider-quota-claude-cli.test.ts, tests/cli/cli-headless-parity.test.ts, structure/providers/claude-cli-replay.md
The Claude CLI parser and quota DTOs carry display-only windows. Snapshot validation checks their rows, and CLI output includes unshadowed display windows with an inactive label and weekly share.
Project and render quota display windows gui/src/codex-quota-utils.ts, gui/src/provider-workspace/report.ts, gui/src/components/QuotaBars.tsx, gui/src/i18n/*.ts, gui/src/styles/provider-quota.css, gui/tests/provider-quota-model-id.test.ts, gui/tests/quota-display-only-window.test.tsx, docs-site/src/content/docs/guides/claude-code.md
The GUI keeps display-only rows separate, gives routed rows precedence when labels match, and renders inactive status, reset details, and weekly allowance shares. Locale strings, styles, documentation, and tests cover the display changes.
sequenceDiagram
participant AnthropicUsageResponse
participant anthropicUsageBuckets
participant ClaudeCliUsageParser
participant ProviderQuota
participant QuotaBars
AnthropicUsageResponse->>anthropicUsageBuckets: provide scoped usage rows
anthropicUsageBuckets->>ClaudeCliUsageParser: return routed and display buckets
ClaudeCliUsageParser->>ProviderQuota: set customWindows and displayWindows
ProviderQuota->>QuotaBars: provide quota readings
QuotaBars->>QuotaBars: render inactive status and weekly share
Loading
Possibly related issues
lidge-jun/opencodex#8110 — The issue describes inactive Anthropic weekly-scoped rows missing from quota displays; this change exposes those rows as display-only windows.
lidge-jun/opencodex#3375 — This change exposes additional per-account Anthropic quota state in displays, but does not address the issue’s separate pool lifecycle requirements.
Possibly related PRs
lidge-jun/opencodex#8469 — Both changes use weeklyAllowancePercent for model-scoped quota rows; this PR also carries the share on inactive display-only rows.
lidge-jun/opencodex#7916 — Both changes use the Claude CLI quota-report flow; this PR adds validation and reporting for display-only windows.
Merge Risk: Low · previous review 085d2
Malformed saved quota data could show an odd account bar, but normal quota reports are unaffected. The change is mergeable with awareness of that narrow display issue.
Pre-merge checks: 4 · 1
Failed checks: 1 inconclusive
Check name
Status
Explanation
Resolution
Docstring Coverage
Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta…
Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed checks: 4 passed
Check name
Status
Explanation
Description Check
Check skipped - CodeRabbit’s high-level summary is enabled.
Title check
The title clearly summarizes the main change: display inactive Anthropic model-scoped quota rows without using them for routing.
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage
Explanation
Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 15 functions across 21 files. (10 skipped: 4 unsupported, 6 too large.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Autofix · 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.
all PR quality gates passed; the review readiness checklist is complete.
Review readiness checklist
✅ Required local validation passed; commands, results, and any full-suite exception are documented.
✅ I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
✅ I resolved all correct Codex and CodeRabbit findings.
✅ My PR is ready for review.
✅ 4/4 boxes ticked.
This pull request has been marked Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently.
Maintainers notified: @lidge-jun@Ingwannu
Maintainer review (contributor-backlog lane), head b4fd3f8cff, trial-merged with dev0fac612a93.
Holding this one. The intent is right and routing really is unchanged: every routing consumer still reads only customWindows, familyEnumeration and rawAnthropicUsageWindows are byte-identical, and the new usage-window tests fail on dev (3) and pass here (36), alongside the JEV/quota-pool neighbours.
What blocks.
Display-only rows escape the account card.quotaFromUnknown (gui/src/provider-workspace/report.ts ~165) merges displayWindows into customWindows, and that merged list feeds buildQuotaSummary (gui/src/quota-summary.ts ~156) and the tray's quotaWindows (gui/src/pages/tray/TrayAccounts.tsx ~18). pickHeadline falls back to any measured window without modelId when no fixed window matches, and these rows never carry modelId. A report whose only measurement is displayWindows: [{label:"Fable", percent:55}] therefore produces the summary headline {"id":"Fable","percent":55}, and the inactive row also gains a tray entry. That is a visible change outside the card this PR describes. Change: mark these rows as display-only through the projection (a displayOnly flag, or set modelId), exclude them from pickHeadline, from summaryWindows severity, and from the tray rows, and add a quota-summary test asserting a display-only row never becomes the headline.
Screenshot. The PR changes gui/, so enforce-target needs a screenshot of the account card in the description (drag it into the editor; please do not commit it).
Small, non-blocking: src/providers/quota/account-cache.ts ~98-113 reads quota[field] while writing result[field]; fine today, but worth a one-line comment.
An inactive Fable row with no legacy seven_day_fable key now keeps its
weekly_scoped_shares allowance, so the dashboard can say how much of the
week Fable may use. Routing still reads only customWindows; a pinned test
shows pool windows, family percents and reset ordering are identical with
and without display rows. The Claude CLI snapshot loader now validates
displayWindows like customWindows.
The projection merged an inactive Anthropic model limit into
customWindows, so a report whose only reading was display-only Fable
became the quota summary headline, and the row reached the tray and
urgency sorting too. AccountQuota now carries those rows in their own
displayWindows field; only QuotaBars draws them, after the routed rows,
tagged "Not in effect now". Every Fable bar with a reported share shows
"Up to 50% of your weekly limit" under its label, in all eleven locales.
ocx provider quota and ocx account refresh print display-only rows the
same way, after routed windows, with the same routed-label precedence.
204-204: Trivial · Maintainability & Code Quality · Low value
Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.
Fix the awkward phrase in the documentation.
"whether or not it binds, regardless of plan name" is redundant. LanguageTool flags it. Shorten it.
Proposed fix
-never read, so Fable shows whether or not it binds, regardless of plan name. The GUI keeps them in+never read, so Fable shows whether it binds or not, whatever the plan name. The GUI keeps them in
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
A hand-edited account cache or Claude CLI snapshot could carry a
weeklyAllowancePercent of 1e9 or a string, which reached the API and
made ocx provider quota print "up to 1000000000% of weekly". The account
cache now drops a share outside (0, 100] and keeps the measurement, the
snapshot loader rejects such a file as it already does other malformed
rows, and the CLI prints only a valid share. Routing never reads this
field from display rows; the GUI already bounded it.
Maintainer judgment (carry lane), merge head 582292ecf0.
Value. Accounts whose Fable limit is not the binding one (Pro, Max 5x, Team, Ultra alike) showed no Fable bar, because Claude reports that row as an inactive weekly_scoped limit with no legacy seven_day_fable. They now show Fable's own percent, its reset, Claude's "Up to 50% of your weekly limit" line and a neutral "Not in effect now" tag. A response with no Fable row still gets no bar, and no plan-name logic decides either way. Refs #8110 (display half).
UX scope. Only the account quota bars (QuotaBars) draw display-only rows. The quota summary headline and severity, tray rows and urgency sorting read customWindows only, which resolves the earlier hold. ocx provider quota and ocx account refresh print the row after routed windows. Copy is in all eleven locales; before/after captures are in the description.
Regression risk for existing users (routing unchanged). Inactive scoped rows go to a separate displayWindows field. Routing, exhaustion, cooldown recovery, pool assembly, reset ordering, usage admission and the decision sink still read only customWindows, and rawAnthropicUsageWindows still takes .buckets only. A test runs the real owner response and shows pool windows, pooled windows, reset ordering and every family percent are identical with no display rows, with the 4% Fable row, and with a forced 100% one. The binding weekly_all 91% still routes. The inactive-session behaviour from #8088 is unchanged. Values read back from disk are validated: a malformed display row is dropped, and a weekly share outside (0, 100] is removed.
Windows. Data handling and React rendering only: no paths, processes or platform APIs. Not run on Windows locally.
Reviews.
At 085d25bb00, independent code and security reviews (anthropic/claude-opus-5-5) both returned PASS WITH NOTES. The low security finding was fixed with red-first tests in d5781b494c. The P3 notes were rebutted, with reasons in the review ledger.
At 34fe145068, after merging dev's Show Mistral API and Vibe budget usage #8295 (Mistral budget display, five overlapping files, both features kept), fresh final code and security reviews (gpt-6.1-terra, xhigh) both returned PASS.
Reviewed head to merge head.34fe145068..582292ecf0 is one dev merge. Its diff, ignoring index and hunk headers, equals dev commit a5d088f1bd (#8482, docs) exactly. The PR's own diff against dev is unchanged line for line. The only overlapping file is docs-site/src/content/docs/guides/claude-code.md, which keeps both the #8482 "Claude OAuth client-version errors" section and this PR's inactive-Fable paragraph.
Checks at 582292ecf0.
Focused server tests (Fable fixture, routing invariance, disk share bounds, Claude CLI, CLI output, Anthropic routing and threshold): 239 pass, 0 fail.
No hosted check has failed on this head; pending CI was not awaited.
NOT RUN: full bun run test (the last local test:changed was at 085d25bb00, with no failures in touched code), a live dashboard against a real Claude account, Windows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On the Claude account list, an account whose Fable weekly limit is not the binding one shows no Fable bar, while Claude's own usage page shows it. Claude's usage endpoint reports that row as an inactive
weekly_scopedlimit and, for current accounts, sends no legacyseven_day_fablekey at all. A real Max response (values verbatim, identifiers removed):{ "kind": "session", "percent": 35, "is_active": false }, { "kind": "weekly_all", "percent": 91, "is_active": true, "severity": "critical" }, { "kind": "weekly_scoped", "percent": 4, "is_active": false, "scope": { "model": { "display_name": "Fable" } } } "weekly_scoped_shares": [{ "limit_index": 2, "used_percent_of_weekly": 2, "allowance_percent_of_weekly": 50 }]anthropicUsageBucketsdropped scoped rows withis_active: falseon purpose for routing (#8088), and nothing else published them, so no path could draw the bar.ProviderQuota.displayWindows, carryingweeklyAllowancePercentfromweekly_scoped_shares. Routing, exhaustion, cooldown recovery, pool assembly and reset ordering still read onlycustomWindows; a test pins that their output is identical with and without display rows, including a 100% display-only Fable row. The bindingweekly_all(91%) still routes.used_percent_of_weekly), its reset time, the line "Up to 50% of your weekly limit" (Claude's wording), and a neutral "Not in effect now" tag. Any Fable bar with a reported share, active or not, now shows the share line. Strings are in all eleven locales.AccountQuota.displayWindowsfield, so the quota summary headline and severity, tray rows and urgency sorting, which readcustomWindows, never see them. A routed row with the same label (a later rate-limit header observation) wins.displayWindowslikecustomWindows. A weekly share read back from disk (account cache or snapshot) must be a number in (0, 100].ocx provider quotaandocx account refreshprint display-only rows after routed windows throughterminalSafeText, e.g.Fable 4% (up to 50% of weekly; not in effect).structure/providers/anthropic-account-pool.md,structure/providers/claude-cli-replay.md, and the Claude Code guide.Refs #8110 (the display half; this PR deliberately leaves routing on inactive scoped rows unchanged).
This carries @junhoi-pk's change (b4fd3f8) and resolves the maintainer hold on it: the display-only rows no longer reach the summary headline or tray, the weekly share is attached, and the screenshot is below. It also merges dev, including #8295 (Mistral budget display), which touched the same quota files; both features are kept.
Before (dev): the same Max account shows only the 5-hour and weekly bars.
After: Fable appears at 4% with its reset, its weekly share, and the not-in-effect tag.
Captured from the real
ProviderAccountQuotacomponent with the production CSS bundle (dark theme).Co-authored-by: junhoipark junhoi.pk@gmail.com
Verification
Failing first, then passing after each fix:
bun test tests/providers/provider-anthropic-usage-windows.test.ts: the owner-response case failed withoutweeklyAllowancePercenton the display row; an out-of-range cached share was kept. Both pass now.bun test tests/providers/provider-quota-claude-cli.test.ts: a malformeddisplayWindowssnapshot, and one with an out-of-range share, reached/api/provider-quotas; both are rejected now.cd gui && bun test tests/provider-quota-model-id.test.ts tests/quota-display-only-window.test.tsx: a display-only-only report became the summary headline, and the bars had no share or tag; both pass now.bun test tests/cli/cli-headless-parity.test.ts: the CLI line omitted the display row and printed an out-of-range share; both pass now.After merging dev (#8295) at 34fe145:
bun run typecheck(2,378 files),bun run typecheck:gui:if-changed,bun run lint:gui,bun run privacy:scan,bun run structure:check: all pass.bun run test:changedat 085d25b (1,823 files): 42,635 pass, 108 skip, 31 fail, none in code this PR touches. 16 passed when their files ran alone (load timeouts). 15 Codex launcher cases failed identically on unmodifiedorigin/devin the same environment and pass on a later rerun after the host's OpenCodex service was restored, so they reflected local host state. Hosted CI covers the final head.Reviews: independent code and security reviews at 085d25b (PASS WITH NOTES; the low security finding on unbounded disk shares was fixed with tests, the P3 notes were rebutted), then fresh final code and security reviews at 34fe145 (both PASS).
Checklist
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
Required local validation passed; commands, results, and any full-suite exception are documented.
I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.