Fix Codex and Spark quota-window detection and automatic recovery - #12
Fix Codex and Spark quota-window detection and automatic recovery#12motonari728 wants to merge 15 commits into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Research notes: quota API fields and the verification approachCan one API response identify an unstarted window?The quota response inspected on 2026-09-07 had no explicit window-start field. It provides usage and reset timing, but at 0% usage those values can describe either an unstarted window or one that has just started. The windows under
The app-server schema generated in the earlier investigation likewise exposed Why compare observations one minute apart?What we need to learn is whether the reset is counting down or sliding forward. For example, with usage still at 0%:
Waiting a minute separates this movement from small timestamp variations. This PR uses that comparison to infer the start state and leaves inconsistent observations unconfirmed. A completed ping is followed by a quota check so CLI completion and window activation remain separate results. What other OSS implementations doThe 2026-09-06 source review found several useful approaches:
These implementations provide precedents for quota checks and recovery, but none directly handles the distinction this PR needs: a fixed reset at 0% versus a sliding reset at 0%. That is why this proposal adds comparison across observations rather than adopting one of their classifiers unchanged. A codex-lb user report describes the same practical concern: a successful-looking warmup followed by a still-sliding Pro weekly reset. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2eda9cb9bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Summary
Improve quota-window detection and automatic recovery for Codex and Spark. Claude behavior and the Codex TUI transport are unchanged.
Problem
Codex can report 0% usage for both started and unstarted quota windows. A single response can look identical in both cases.
For example, consider two observations of a five-hour window (illustrative times):
In the first case, the reset stays at 15:00: the remaining time decreases from five hours to four hours and 59 minutes. In the second, the reset moves forward by the same minute as the observation: it remains five hours away rather than counting down toward a fixed boundary.
Treating both as inactive because usage is 0% can cause unnecessary pings or incorrect scheduling. The same ambiguity matters for weekly windows, including on plans with no five-hour limit.
A successful CLI request also does not guarantee that a quota window has started. Previously, the watcher reported a new window immediately after a successful request, without verifying it or providing timely recovery when it remained unstarted.
Solution
pingresponsive: no retry sleeps or one-minute verification wait. HTTP 401 still permits immediate credential reload/refresh and re-request. When further observation is needed, suggestlimitping status, which reads usage without sending a ping.watchandbgrecheck automatically. If a window is confirmed unstarted after a ping, retry with increasing delays, capped at four automatic attempts per rolling hour per account and quota bucket. Unconfirmed results or API errors do not trigger immediate model retries.Retry-After. This also applies when the send-time precheck fails repeatedly. Restarting the watcher clears these read-retry delays after access is restored.reset_buffer; an unknown previous reset adds no buffer wait.start_stateand optionalverification_due_atfor Codex/Spark.Reset-time comparison is an empirical inference, not an explicit API start flag. The original Plus-plan scenario has not been revalidated on the current plan.
See Codex/Spark window verification for a brief explanation. This PR is independent of #11.
Local state
State is stored in
$XDG_CONFIG_HOME/limitping/state/codex/, or~/.config/limitping/state/codex/whenXDG_CONFIG_HOMEis unset.This is a bounded working snapshot, not an accumulating API history: observations are overwritten, and automatic-attempt timestamps older than one hour are removed on subsequent observation or attempt updates. No credentials or raw API responses are stored. Storage stays roughly constant for the same accounts and quota buckets; unused account/model entries are not automatically removed.
Missing state is recreated from fresh quota API reads. For ambiguous 0% usage, the first read establishes a baseline and another read at least 60 seconds later can restore the start-state determination.
watch/bgperforms that recheck automatically; interactive users runlimitping statusagain. No model request is needed just to reconstruct the observations.State also retains automatic-ping retry cooldowns and in-progress ping coordination, so deleting it loses those safeguards as well as cached observations. It should not be cleared while a watcher or ping is running. Manual pings bypass automatic cooldowns and attempt budgets, but retain the existing in-progress duplicate protection. API-read backoff is watcher-local and is not persisted.
Checks
gofmt -l .prints nothinggo build ./...go vet ./...go test ./...(also tested with-race)Safety