Skip to content

Two reports from the tray: a spent quota and two limits that read alike - #25

Merged
nowrap merged 3 commits into
mainfrom
dev
Aug 19, 2026
Merged

nowrap merged 3 commits into
mainfrom
dev

Conversation

@nowrap

@nowrap nowrap commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Two things visible in one screenshot of the details fly-out: Claude paused on a rate limit, and two Codex rows both reading Codex · Woche, one of them stuck at 0 %.

The usage endpoint was asked 40 times an hour

refreshSeconds was the only thing deciding how often a provider was reached. The windows being watched are 5 hours and 7 days wide, and the tray is not the only client spending the account's request quota — so most of those requests could not learn anything, and the 429 they earned cost the display a quarter of an hour at a time.

A provider now states its own floor (IUsageProvider.MinInterval) and PollScheduler enforces it on top of the backoff — whichever pause is longer wins. Claude's floor is claude.minIntervalSeconds, 300 s by default and exposed in the settings dialog; Codex keeps zero, being a local process with nothing to spend. Reset() clears the spacing along with the backoff, so the menu's refresh command still reaches everything at once.

Two weekly windows that read alike

The prolite plan reports a second bucket, codex_bengalfox, carrying limitName: "GPT-5.3-Codex-Spark" — a per-model weekly limit, the exact analogue of claude.weekly_scoped.fable. AddWindows ignored the name, so both rows rendered from the window alone and the model's limit — 0 % until that model runs — looked like the account's limit had stopped filling.

The named bucket now reads Week · GPT-5.3-Codex-Spark, the shape Claude's per-model window already uses. The service prefix is dropped there on purpose: Codex · Week · GPT-5.3-Codex-Spark measures 248 px against a 222 px label column, so the ellipsis would have eaten the only part that tells the rows apart. Only the account-wide window keeps the active dot — whether a model is the one currently running is not something the server says.

Verification

  • 235 unit tests pass, 13 of them new: minimum spacing (skipped cycles are not failures, an explicit refresh beats the floor, the floor survives a reset because it is configuration, a longer backoff still wins) and the Codex labelling (named, unnamed fallback, active only on the account-wide window).
  • --once against the live account confirms the new bucket parses as codex.codex_bengalfox.primary at 0 % without the active marker.

🤖 Generated with Claude Code

nowrap and others added 3 commits August 19, 2026 08:57
A plan with a per-model quota reports two weekly windows — the account's
and the model's — and both rendered as "Codex · Week". The model's own
limit sits at 0 % until that model runs, so the pair read as one limit
that had stopped filling.

The bucket's limitName is the name for it; the label follows the shape
Claude's per-model window already uses ("Week · <model>"), without the
service prefix because "Codex · Week · GPT-5.3-Codex-Spark" measures
248 px against a 222 px column and the ellipsis would eat the one part
that tells the rows apart.

Only the account-wide window keeps the active dot. Whether a model is
the one currently running is not something the server says, and a dot on
both windows costs the one that is true its meaning.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The refresh interval was the only thing deciding how often a provider is
reached, so a 90 s tray was making 40 requests an hour against an
endpoint with a request quota it does not own alone — for numbers whose
windows are five hours and seven days wide. The 429 that earned then
froze the display for a quarter of an hour at a time, which is the
symptom that gets reported.

A provider now states its own floor and the scheduler enforces it, on
top of the backoff rather than instead of it: whichever pause is longer
wins. Claude's floor is claude.minIntervalSeconds, 300 s by default and
in the settings dialog; Codex keeps zero, being a local process with
nothing to spend. Reset() clears the spacing along with the backoff, so
the menu's refresh command still reaches everything at once — someone
asking for numbers now is entitled to the request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`rateLimitsByLimitId` repeats the bucket `rateLimits` already carried, so
one of them has to be skipped, and the check matched the literal "codex".
That is the id this account reports today; it has reported "premium" too.
Under that name neither branch would have skipped anything and the one
limit would have been listed twice — as two rows a person cannot tell
apart, which is the failure this file just spent a commit fixing.

The payload states the id. Use it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nowrap
nowrap merged commit 311f87f into main Aug 19, 2026
5 checks passed
@nowrap nowrap mentioned this pull request Aug 19, 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