Repository navigation
Claim Claude usage-limit resets from the resets popover - #1413
Conversation
The Rate Limit Resets row now derives from ClaudeResetGrantStatus, which the reset claim will also read. A block with a malformed grant is rejected whole, like Claude Code does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a shared ResetClaimService seam (Codex conforms unchanged), the Claude
claim POST to /api/organizations/{org}/reset_rate_limits with next_grant_id
and the popover's request id, outcome mapping including unconfirmed and
unavailable-now, and Claude-specific confirm copy. The post-claim forced
refresh loop is shared by both providers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mocked HTTP only. The live dry run (unknown grant id, nothing spent) is skipped unless OPENUSAGE_LIVE_CLAUDE_CLAIM_DRYRUN=1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Important
The claim can target a different account after credential fallback, and a successful claim can leave pre-claim meters and grant counts cached during Anthropic's cooldown. Both should be addressed before merging.
Reviewed changes: Reviewed the Claude reset-claim implementation and its integration with the shared resets popover.
- Grant status: Typed parsing supplies the reset count, claimability, and confirmation text from the hourly grants check.
- Claude claims: Adds the reset POST, credential selection and refresh, response mapping, cache clearing, and post-claim refresh.
- Shared integration: Generalizes the Codex claim service/environment wiring and adds unavailable and unconfirmed popover outcomes.
- Coverage and docs: Adds mocked claim tests, an opt-in live probe, and protocol/provider documentation. Targeted Swift syntax parsing passed; full macOS build, tests, and UI verification were not available on this Linux host.
gpt-6.1-sol | 𝕏
| /// type doc). `nil` makes the timeline read-only (no "Use" affordance). | ||
| var claim: ((_ expiry: Date, _ redeemRequestID: String) async -> ResetClaimOutcome)? | ||
| /// The confirm card's explanation line; `nil` keeps the generic one. | ||
| var confirmDetail: String? |
There was a problem hiding this comment.
Deadline-less grants cannot be claimed
Medium Severity
A claimable Claude grant with no ends_at still increments the available count but is omitted from expiriesAt. The popover then takes the unknown-expiries path, which has no Use control. isClaimable can be true, yet the user has no way to confirm the claim. Claude does not need a deadline to spend next_grant_id, so these resets are stuck at read-only.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 843852c. Configure here.
There was a problem hiding this comment.
Not changed: every cedar_ember grant observed so far, including the one claimed live, carries ends_at, and the popover's claim flow is keyed by expiry instant. Adding an undated claim path is a larger popover change; leaving this open for the owner to decide.
🤖 Addressed by Claude Code
Cache only the grant status and derive the row from it, drop status fields nothing reads, split the claim into candidate, refresh and organization steps, and share one cached-profile lookup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A claim now uses only the login whose grants were read, since grant ids are shared promos another login could also hold. A successful claim clears the cached usage and any 429 cooldown so the post-claim refresh fetches live meters instead of serving the pre-claim snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The popover stops hiding nodes by the clicked deadline once the post-claim refresh has already reconciled the timeline, and fresh expiries replace its local bookkeeping. A grants read that a claim overtook no longer caches the pre-claim status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Important
One race remains in the new grants-cache invalidation: a failed in-flight read can restore spent grants for an hour after a claim.
Reviewed changes: Reviewed the follow-up credential binding, post-claim cache invalidation, and popover reconciliation changes.
- Bound claims to one login: Removed fallback to unrelated credentials and added an auth-rejection regression test.
- Invalidated post-claim usage: Cleared cached usage and the provider cooldown, added a cooldown regression test, and introduced an epoch to reject overtaken grants reads.
- Reconciled the timeline: Reset local claimed-node tracking when fresh expiries arrive and avoided hiding a second node after reconciliation.
- Updated behavior documentation: Documented login binding and immediate refresh during cooldown. Targeted Swift syntax parsing passed; full macOS build, tests, and UI verification were unavailable on this Linux host.
gpt-6.1-sol | 𝕏
Read the grant and its login together before any await, look for the Desktop login the grants check may have used, reuse a claim's request id until Anthropic answers definitively, and recheck the claim counter after the failed-lookup await. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes: Reviewed d600ed72..1c20af2b, focusing on claim identity, unsettled retries, and grants-cache races.
- Captured claim identity: Snapshotted the grant and credential fingerprint together before suspension, and added Desktop-forced credential lookup while retaining exact-login binding.
- Preserved retry identity: Retained request IDs after transport failures, server errors, and unreadable successful responses; added a regression test for retrying after a 502 with the original ID.
- Closed the cache race: Moved the failed-read epoch check after credential validation, keeping it contiguous with the cache write.
- Updated retry documentation: Explained request-ID reuse after reopening the popover. Targeted Swift syntax parsing passed; full macOS build, tests, and UI verification were unavailable on this Linux host.
gpt-6.1-sol | 𝕏
A refused claim (not limited, cooldown) now leaves the cached usage and grants alone, and a usage read that a claim overtook no longer caches its pre-claim meters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes: Reviewed 1c20af2b..160c3a2f, focusing on outcome-specific cache invalidation and in-flight usage-cache writes.
- Narrowed cache invalidation: Preserved caches for refused or temporarily unavailable claims, invalidated grants alone for no-credit responses, and retained full usage invalidation for successful or unconfirmed claims.
- Guarded usage caching: Applied the shared claim epoch to the final successful usage-cache write so an overtaken fetch cannot restore pre-claim meters.
- Added regression coverage: Added a test verifying that
not_limitedpreserves the grants check. Targeted Swift syntax parsing passed; full macOS build, tests, and UI execution were unavailable on this Linux host.
gpt-6.1-sol | 𝕏
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 160c3a2. Configure here.
| case .noCredit: | ||
| forgetGrantsAfterResetClaim() | ||
| case .nothingToReset, .unavailableNow, .failed: | ||
| break |
There was a problem hiding this comment.
Gone grants stay cached in cooldown
Medium Severity
.noCredit now calls forgetGrantsAfterResetClaim() only, so lastGoodUsage and rateLimitedUntil stay. The post-claim refresh still hits the 429 cooldown and serves the old Rate Limit Resets line, even though resetGrantsCheck was cleared and the grant is gone.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 160c3a2. Configure here.
There was a problem hiding this comment.
Not changing: this is display-only. Nothing is spent, the popover already says the reset is no longer available, and the row corrects on the next non-throttled fetch. Not worth more cache surgery.
🤖 Addressed by Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes: Reviewed 160c3a2f..70219129, focusing on idempotency after an unsettled Claude claim.
- Preserved unsettled request IDs: Kept the original request ID across intervening HTTP refusals, so a later retry does not lose the identity of an earlier possibly successful claim.
- Extended regression coverage: Added a 429 between the existing 502 and
already_usedresponses and verified all three attempts use the original request ID. Targeted Swift syntax parsing passed; full macOS build, tests, and UI execution were unavailable on this Linux host.
gpt-6.1-sol | 𝕏



TL;DR: Claude's Rate Limit Resets popover can now claim a usage-limit reset with a "Use" button, the same way Codex's does. The first claim was made live from this build and succeeded.
What was happening
cedar_emberreset grants, but the row was read-only. Using a reset meant going through Claude Code's/limit-reset.What this changes
Typed grants status.
ClaudeResetGrantStatusparses thecedar_emberblock once. The row and the claim both read it, and the hourly grants check caches it.Claiming.
ClaudeProvider+ResetClaim.swiftsendsPOST /api/organizations/{org}/reset_rate_limitswith{program: "cedar_ember", grant_id: next_grant_id, request_id}. It uses the same headers and User-Agent as the grants read and a 25s timeout.request_id, so a retry can't spend twice.Shared claim seam. A
ResetClaimServiceprotocol withresetClaims/resetClaimenvironment keys replaces the Codex-only keys. Codex conforms unchanged. The post-claim refresh retry loop is shared too.Outcomes. Two new popover results:
.unconfirmed(reset_unconfirmed,stamp_indeterminate)..unavailableNow(until:)(cooldown,paused,low_priority_active).resetandalready_usedcount as claimed.not_limitedreads as nothing to reset.ineligible,no_grant,unknown_grantandnot_next_grantread as no longer available.Popover copy. The confirm card names what the grant refills, for example "Refills your session and weekly limits now." "Use" only appears when the account is eligible and
next_grant_idnames a grant with resets left.Docs.
docs/providers/claude.mdcovers claiming.docs/research/claude-reset-grant-claim.mdhas the protocol and both live responses.Closes #1291.
Heads-up
idorresets_leftnow rejects the whole block, the way Claude Code does. The row then reads "No data" instead of skipping that one grant.ClaudeProvider.swiftwas already over 500 lines (656 → 668). The claim itself lives in its own extension file.Tests
swift buildpasses.swift testran 1632 tests: 0 failures, 4 skipped.ClaudeResetClaimTestscover:request_idreuse across an auth fallback.OPENUSAGE_LIVE_CLAUDE_CLAIM_DRYRUN=1. It sent our real request with a grant id that matches no grant. The server answered200 {"result":"ineligible","reason":"unknown_grant","reset":false,...}and spent nothing.200 {"result":"reset","reset":true,"resets_left":0,"cleared":["five_hour","seven_day","seven_day_overage_included"],...}. The forced refresh completed right after.Screenshots
Not captured. The only grant was spent in the live test, so the "Use" button and confirm card can't be shown again until a new grant arrives.
🤖 Generated with Claude Code
Note
High Risk
Adds provider API writes against Anthropic with OAuth credentials and idempotency rules; incorrect claim or cache handling could spend resets or show stale meters after a claim.
Overview
Claude Rate Limit Resets can now be spent from the same timeline popover as Codex: eligible accounts get a Use flow that POSTs Anthropic’s
cedar_emberclaim (next_grant_id+ idempotentrequest_id), scoped to the login that fetched grants, with grant-specific confirm copy and new banners for unconfirmed and cooldown/paused outcomes.Reset claiming is generalized behind a shared
ResetClaimServiceandresetClaims/resetClaimenvironment keys (replacing Codex-only wiring).ClaudeResetGrantStatusparses and caches thecedar_emberblock for both the row and claimability; post-claim cache invalidation (resetClaimEpoch, unsettled request-id reuse) and the forced refresh retry loop live in sharedResetClaimRefresh. Docs andClaudeResetClaimTestscover the protocol and flows.Reviewed by Cursor Bugbot for commit 7021912. Bugbot is set up for automated code reviews on this repo. Configure here.