Skip to content

Quota readings: lapsed windows, CLI reset times, Codex refusals, external switches - #1943

Merged
BarganConstantin merged 7 commits into
developmentfrom
fix/quota-readings
Oct 4, 2026
Merged

BarganConstantin merged 7 commits into
developmentfrom
fix/quota-readings

Conversation

@BarganConstantin

Copy link
Copy Markdown
Owner

What changes

  • A quota window that has reset is no longer shown at its old reading. The server no longer serves a claude-swap row, or a cached answer, whose reset time has passed as the current reading: it reads the window again (claude-swap first, then its own sources on their usual floor) and holds the old reading, marked stale, only when nothing newer comes. The Usage panel's bars say "reset · waiting for a new reading" for a window past its reset, with no fill, no pace and no past "resets …" time; the accounts panel's open row draws such a lane as a record and says "reset" where the countdown was.
  • A CLI reset time with no date ("4pm") is today or tomorrow, not 1 January of next year, so the 5-hour bar counts down hours and keeps its pace note on the claude --print /usage path.
  • Codex: a refresh token OpenAI has refused is not sent again on every poll; it is remembered by hash until auth.json holds a different one.
  • Codex: the cooldown after a refused login ends as soon as codex login writes a new credential, so ↻ works right after logging in. A 429 keeps the last good lanes on screen, marked stale; with none, the panel says the deck is waiting instead of asking for a ↻ the cooldown refuses.
  • An account switch made outside the deck (cswap switch, claude-swap's auto-switch, claude /login) no longer keeps the previous account's quota on screen: readings carry the account they were read for, and a held one for another account is dropped and re-read.
  • API-key, Bedrock and Vertex installs configured in Claude Code's settings (user or managed env, apiKeyHelper, values like true) are recognised as having no subscription window, instead of drawing two zero bars.
  • The accounts panel prints whole percentages on the open row's bars, the rest line and the fold's peek.

Verification

  • npm run typecheck: clean.
  • Full suite (--maxWorkers=3 --minWorkers=1): 905 files, 11803 tests passed, exit 0.
  • New regression tests, each run first against the unchanged code and seen failing for the reason described:
    • quota-lapsed-reading.test.ts (server: lapsed store row, lapsed cached answer) and quota-bar-rolled-over.test.ts (QuotaBar and the accounts row's lane past their reset)
    • quota-cli-reset-time.test.ts
    • codex-refused-credential.test.ts (refused refresh token not re-POSTed; cooldown ends at login; 429 keeps lanes) and codex-quota-hint-rate-limited.test.ts
    • quota-external-switch.test.ts
    • quota-no-subscription-settings.test.ts
    • accounts-whole-percent.test.ts
  • Existing tests repointed, none removed: three quota suites' store fixtures carried fixed August reset dates, now ahead of the clock; one Codex test now expects rate_limited for a 429 with no reading held.
  • Browser, on an isolated deck with faked /api/quota, /api/codex-quota and /api/claude-accounts: a held 100% 5-hour reading 30 minutes past its reset shows "reset · waiting for a new reading" beside a normal 7-day bar; the Codex section shows the rate-limit sentence, and a Codex lane past its reset shows the same reset state; the live account's lapsed 5h lane is muted with "reset"; the open row, rest line and peek read 86%, 13%, 59% free and 14% free for fractional inputs.

A reading whose reset time has passed was still served and drawn as the
current window: the server trusted a claude-swap row up to 45 minutes old
and cached answers for a minute without checking the reset, and the Usage
panel printed the past reset time, the old percentage and a pace measured
against a window that was over. The server now re-reads a lapsed reading
(and holds it as stale only when nothing newer comes), and the bars say the
window reset and a new reading is awaited.
claude --print /usage prints the session window's reset as a bare time
when it falls today, and the parser appended a year to it, which reads as
1 January: the 5-hour bar counted down months and lost its pace. A bare
time is now the next time that clock reading comes round.
Once the access token had expired, every 60-second quota poll asked for a
refresh, and a refresh token the endpoint had already refused for good was
POSTed to auth.openai.com again each time, for as long as the panel stayed
open. The deck now remembers the refused token by hash and does not spend
it again until auth.json holds a different one.
… through a 429

After a refused login the five-minute cooldown answered the refresh button
with the cached refusal even once codex login had written a new credential;
it now ends as soon as auth.json holds a different one. A 429 replaced the
last good lanes with a failure and the panel asked for a retry the cooldown
refused; the lanes are now held, marked stale, and with none the panel says
the deck is waiting.
…deck

A switch made in a terminal, by claude-swap's own auto-switch or by
claude /login reached none of the deck's invalidation, so the self-poll
floor served the previous account's last reading for up to five minutes.
Readings now carry the account they were read for (claude-swap's active
account, or the credentials-file token without one), and a held reading
for another account is dropped and re-read on the refresh floor.
…ings

Whether the CLI's silence means a just-reset window or no window at all was
decided from the deck's own environment and only the literal 1, so an
install configured through the env block of Claude Code's user or managed
settings, an apiKeyHelper, or a value like true was read as a subscription
and drew two zero bars. Those are now read the way Claude Code reads them.
claude-swap keeps utilisation unrounded and headroom is 100 minus it in
floating point, so the open row's bars printed 85.555555% beside the shut
row's 86%, and the rest line and the fold's peek printed float noise like
14.444445000000002% free. They now round as the shut row and the Usage
panel do.
@BarganConstantin
BarganConstantin merged commit 2699494 into development Oct 4, 2026
10 checks passed
@BarganConstantin BarganConstantin mentioned this pull request Oct 5, 2026
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