Skip to content

fix(codex): accept monthly-classified snapshots and probe-owned refresh generations (#955 review) - #967

Closed
Yuxin-Qiao wants to merge 1 commit into
lidge-jun:codex/915-cooldown-recovery-probefrom
Yuxin-Qiao:fix/codex-cooldown-recovery-review-followup
Closed

fix(codex): accept monthly-classified snapshots and probe-owned refresh generations (#955 review)#967
Yuxin-Qiao wants to merge 1 commit into
lidge-jun:codex/915-cooldown-recovery-probefrom
Yuxin-Qiao:fix/codex-cooldown-recovery-review-followup

Conversation

@Yuxin-Qiao

Copy link
Copy Markdown

Summary

Follow-up to #955 that addresses both unresolved Codex review threads on the cooldown early-recovery probe:

1. Duration-classified monthly snapshots were never accepted (P1). isCompleteCodexQuotaRecoverySnapshot() required weeklyPercent for every non-Go/Free plan based on the plan name. But parseUsageQuota() classifies windows by duration: a Team plan response whose primary window is explicitly monthly (e.g. limit_window_seconds: 2628000, no secondary) parses to monthlyPercent only. The probe therefore rejected every successful fresh read for those accounts and they stayed cooled until the predicted expiry — the same "cooled forever" defect this work exists to fix, reintroduced for monthly-window plans.

2. The probe's own token refresh was treated as a replacement (P2). getValidCodexToken() refreshes a near-expiry access token inside the probe fetch and advances the credential generation by exactly one before WHAM completes. settleCodexQuotaRecoveryProbe() required the claim-time generation to match exactly, so a successful fresh reading under the new live generation was rejected and recovery waited another probe interval. saveCodexAccountCredentialIfGeneration() preserves replacedAt while saveCodexAccountCredential() stamps a new one, so replacedAt fences the +1 transition against external replacement.

Tests

  • New: Team account recovers from a duration-classified monthly snapshot
  • New: probe-owned token refresh (generation +1, same lineage) still recovers
  • Updated: plan-matrix test now accepts any window the parser actually wrote; Go/Free still require monthly-only
  • Existing replacement/429/concurrency/lease tests unchanged and passing

Verification

  • bun x tsc --noEmit — exit 0
  • tests/codex-cooldown-recovery.test.ts — 20 pass (both new tests were red before the fix)
  • tests/rate-limit-reset-credits.test.ts — 31 pass
  • tests/codex-routing.test.ts tests/codex-pool-rotation.test.ts tests/codex-main-rotation.test.ts — 143 pass
  • Full suite: 7720 pass, 8 skip, 11 fail — the 11 failures (10 provider-management validation, 1 crash-guard timeout) reproduce identically on the unmodified stack 5/7: probe reset-derived cooldowns without waiting to be selected (#915) #955 head branch and are unrelated to this change.

…ned refresh generations

Addresses the two unresolved Codex review threads on lidge-jun#955:

- isCompleteCodexQuotaRecoverySnapshot() required weeklyPercent for every
  non-Go/Free plan by plan name, but the parser classifies windows by
  duration: a Team response with an explicitly monthly primary window
  parses to monthlyPercent only, so those accounts could never recover
  early and stayed cooled until their predicted expiry.
- settleCodexQuotaRecoveryProbe() required the claim-time credential
  generation to match exactly. A probe-owned token refresh inside
  getValidCodexToken() advances the generation by one before WHAM
  completes, so a successful fresh reading was rejected and the account
  waited another probe interval. replacedAt is preserved by refresh and
  stamped by external replacement, so it fences the +1 transition.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 59c9caf7-15d0-485c-9b00-01f7245d17f2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Aug 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79d2164e26

ℹ️ 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".

Comment thread src/codex/routing.ts
Comment on lines +525 to +527
: proofGeneration === claim.credentialGeneration + 1
&& currentRecord?.replacedAt === claim.credentialReplacedAt
&& isCodexAccountGenerationLive(claim.accountId, proofGeneration));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept all later generations from the same refresh lineage

When another caller such as the token guardian is already refreshing this cooled account, the probe can join that getValidCodexToken() flight; the owner persists generation N+1, then the same-account join path at src/codex/account-store.ts:406-422 persists the returned credential again and reports N+2. The fresh WHAM result is therefore proven under a live, unchanged-replacedAt generation but this exact +1 check rejects it, leaving the cooldown in place for at least another probe interval. Since replacedAt already distinguishes external replacement, accept any later live generation with the same claim-time lineage (or avoid the redundant same-account generation bump).

Useful? React with 👍 / 👎.

Comment thread src/codex/quota.ts
if (codexQuotaWindowForPlan(plan) === "monthly") {
return typeof quota.monthlyPercent === "number" && Number.isFinite(quota.monthlyPercent);
}
return hasKnownQuotaValue(quota);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Distinguish monthly primaries from tertiary-only snapshots

For a weekly plan such as Team, parseUsageQuota() can produce monthlyPercent from a tertiary window even when the weekly primary/secondary window is entirely absent; tests/codex-routing.test.ts:1199-1205 explicitly preserves that tertiary-only representation. This fallback now treats such a response as complete recovery evidence, so a low optional 30-day reading can clear a reset-derived shared cooldown without any fresh reading for the weekly quota that caused it, immediately routing traffic back to an account that may still be exhausted. Preserve whether the monthly value came from an explicitly monthly primary and accept monthly-only recovery for weekly-named plans only in that case, rather than accepting every parsed monthly value.

Useful? React with 👍 / 👎.

@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Please put your Pull-Request on Ready for Review, once you are finished.

@lidge-jun

Copy link
Copy Markdown
Owner

Carried into #973 (stack 6/6). You found two real defects in my #955 code and both reproduce — thank you for reviewing it properly rather than rubber-stamping it.

Verified before carrying:

$ bun run .tmp/probe_967.ts          # on the #955 head, BEFORE your fix
parsed        = {"monthlyPercent":12,"monthlyResetAt":1900000000}
recoverable?  = false      <-- Team monthly account could never recover

Your diagnosis is exact on both counts. P1: I picked the required window from the plan name while parseUsageQuota() picks it from the window duration, so a Team account with an explicitly-monthly primary stayed cooled until predicted expiry — the same "cooled forever" failure #915 exists to fix. P2: getValidCodexToken() really does advance the credential generation mid-probe, and replacedAt is the right discriminator, since a CAS refresh preserves it while a real replacement stamps a fresh one.

One correction on top, which is why the carried commit is not the last word. Your P1 remedy — accept whichever window the parser wrote — is too permissive in the other direction. A tertiary-only response also writes monthlyPercent, and it describes a different period than the weekly quota that actually gates a Team account:

tertiary-only    {"monthlyPercent":7,"monthlyResetAt":1900000000}    <- not evidence
explicit-monthly {"monthlyPercent":12,"monthlyResetAt":1900000000}   <- evidence

Those two shapes were indistinguishable, so no amount of tightening at the predicate could separate them. #973 adds provenance at the source instead: parseUsageQuota() records monthlyIsPrimaryWindow when the value came from an explicitly-monthly primary window, and recovery requires it before accepting monthly-only evidence for a weekly-quota plan. Go/Free are unchanged.

An independent audit then found the flag was being dropped at three further copy sites — setAccountQuotaFromParsed(), updateAccountQuota(), and the credits-only branch — which would have made the guard decorative. All fixed and pinned; ablating any of them fails a test.

Your commit is carried with cherry-pick -x, authorship intact, patch-id verified identical. Closing this since it targeted the #955 branch directly and now lives in #973.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants