Skip to content

feat(resets): refuse a Codex credit when cached limits show nothing to reset - #174

Merged
rexdotsh merged 2 commits into
fix/refresh-after-resetfrom
feat/codex-reset-guard
Oct 22, 2026
Merged

rexdotsh merged 2 commits into
fix/refresh-after-resetfrom
feat/codex-reset-guard

Conversation

@rexdotsh

@rexdotsh rexdotsh commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Stack 2/4 · resets UX redo. Server only.

Problem

An owner kept using a Codex credit on an account at 6% (5h) and 12% (7d), with limitReached: false. Every consume answered {"code":"nothing_to_reset","windowsReset":0}. Codex only spends a credit when a limit is hit.

When a Codex credit is usable

In openai/codex (94350a9), whether a credit can be used is decided only on the server:

  • The CLI offers Redeem reset whenever available_count > 0 and never checks the limits first (codex-rs/tui/src/chatwidget/usage.rs:33-41, :137-139).
  • The only signal is the consume outcome. NothingToReset means "No current rate-limit window is eligible for a reset" (codex-rs/app-server-protocol/src/protocol/v2/account.rs:426-427; codes in codex-rs/backend-client/src/types.rs:104-118, mapped in codex-rs/app-server/src/request_processors/account_processor/rate_limit_resets.rs:81-94). The TUI then says "Your usage does not need a reset right now." (usage.rs:380-381).

So the rule comes only from nothing_to_reset. Kleis uses the most conservative reading of the data it caches: nothing to reset only when the usage endpoint's last full read is under 10 minutes old and shows rate_limit_reached_type null, and the main limit and every additional limit have allowed not false, limit_reached exactly false, and every window under 100% (or past its reset). Anything unknown or older (a null limit_reached, a missing window percent, an old full read) leaves the decision to Codex. Freshness comes from statusObservedAt, which only the full poll sets. Traffic updates some windows and the quota's fetchedAt, so it can't make old limit flags count as fresh, and steady traffic can't keep refusing a usable credit. A window that traffic reports at 100% still lets the spend through.

Change

  • redeemCodexResetCredit refuses with 409 before anything is sent: "Nothing to reset yet: Codex only uses a credit once you hit a limit. No credit was spent." It uses cached data only, with no new upstream calls, so a stale UI can't keep calling consume.
  • /admin/accounts and /admin/accounts/quotas report the same check as codexResets: { unavailable: "requires_limit" | null }, using the same reason code as Claude. The UI uses it in feat(admin): one reset model for Codex and Claude, with honest results #175.
  • The 10-minute freshness window is now shared with Claude's offer check (READ_MAX_AGE_MS).

Tests

Failing first, through redeemCodexResetCredit with mocked wham/usage and an in-memory DB:

  • fresh limits with nothing hit → 409 and no POST
  • table: a credit is still sent when a window is used up, when Codex reports a limit reached, when limit_reached is unknown, or when the limits are over 10 minutes old (each row fails against a guard that skips that check)
  • traffic 11 minutes after the last full read doesn't make the old limits count as fresh, so the credit is sent

bun lint, bun typecheck, bun run build pass. bun test: 318 pass, 12 skip, 0 fail. --randomize passes (one run hit the known 5s flake in the isolated proxy scenarios, which also happens on main).

…o reset

Codex only spends a reset credit when a limit is hit and otherwise answers nothing_to_reset. With limits read in the last 10 minutes that show every limit clear, the spend gets a 409 and nothing is sent; anything unknown or older is left to Codex. The admin accounts views report the same state as codexResets.unavailable = requires_limit.
Traffic refreshes some windows and the quota's fetchedAt, while the limit flags stay as last polled. The guard now needs the usage endpoint's last full read to be under 10 minutes old, so steady traffic can't keep refusing a usable credit.
@rexdotsh
rexdotsh force-pushed the feat/codex-reset-guard branch from 795971d to 879900b Compare October 22, 2026 13:32
@rexdotsh
rexdotsh merged commit f5eaed6 into main Oct 22, 2026
10 checks passed
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