Skip to content

fix(codex): retain short-window quota end to end - #2056

Closed
Ingwannu wants to merge 1 commit into
devfrom
ingw/fix-k12-short-window-2047
Closed

fix(codex): retain short-window quota end to end#2056
Ingwannu wants to merge 1 commit into
devfrom
ingw/fix-k12-short-window-2047

Conversation

@Ingwannu

@Ingwannu Ingwannu commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • preserve K12/sub-day quota fields through parsed cache replacement, reset-credit refreshes, partial header updates, and legacy quota updates
  • keep explicit shortPercent: 0 distinct from missing quota
  • include the short window in plan-aware routing usage scores for both weekly and 30-day plans
  • add parse -> cache -> accounts API/DTO -> routing regressions

Fixes #2047.

Verification

  • taskset -c 0,1 bun test tests/codex-routing.test.ts tests/codex-auth-api.test.ts tests/codex-cooldown-recovery.test.ts tests/routing-policy-pool-quota.test.ts tests/rate-limit-reset-credits.test.ts — 376 passed
  • taskset -c 0,1 bun run typecheck
  • taskset -c 0,1 bun run privacy:scan

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. (The existing short-window documentation is accurate; this fixes the missing consumers.)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Bug Fixes
    • Improved quota tracking to preserve short-term and monthly usage during partial updates and account refreshes.
    • Updated usage-based routing to consider short-term quota limits, including free and Go plans.
    • Ensured zero-valued short-term quota data and reset information are recognized correctly.
  • Tests
    • Added coverage for quota preservation, caching, serialization, and short-term usage scoring.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e98e5c0e-85e1-4c95-86ad-15931c82c71a

📥 Commits

Reviewing files that changed from the base of the PR and between a5ec641 and 77ebee3.

📒 Files selected for processing (4)
  • src/codex/quota.ts
  • src/codex/routing.ts
  • tests/codex-auth-api.test.ts
  • tests/codex-routing.test.ts

Included review availability: Your plan includes up to 10 reviews per rolling hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Short-window quota data is now recognized and preserved across account quota updates, cache refreshes, and DTO serialization. Routing scores include short-window usage, and tests cover zero-valued snapshots, partial updates, K12 refreshes, and Go-plan selection.

Changes

Short-window quota handling

Layer / File(s) Summary
Quota recognition and preservation
src/codex/quota.ts, tests/codex-auth-api.test.ts, tests/codex-routing.test.ts
hasKnownQuotaValue and usage detection recognize short-window fields. Partial, credits-only, and legacy updates preserve short-window and monthly data. Tests cover cache updates, K12 refresh projection, and zero-valued short-only snapshots.
Short-window routing score
src/codex/routing.ts, tests/codex-routing.test.ts
computeCodexUsageScore includes shortPercent when selecting maximum usage. Tests cover short-window precedence and Go-plan behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 77ebe

The PR preserves short-window quota data through cache, API, and routing paths so plan-aware routing can use current usage. No actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant K12Upstream
  participant AccountQuotaUpdate
  participant QuotaCache
  participant RefreshDTO
  participant RoutingScore
  K12Upstream->>AccountQuotaUpdate: short-window quota fields
  AccountQuotaUpdate->>QuotaCache: preserve shortPercent, shortResetAt, shortWindowSeconds
  QuotaCache->>RefreshDTO: serialize retained quota fields
  QuotaCache->>RoutingScore: provide weekly, monthly, and short percentages
  RoutingScore-->>RoutingScore: select maximum applicable usage
Loading

Suggested reviewers: lidge-jun, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: retaining short-window Codex quota data end to end.
Linked Issues check ✅ Passed The changes preserve short-window quota fields through parsing, cache and API flows, distinguish zero values, update routing scores, and add regression tests for issue #2047.
Out of Scope Changes check ✅ Passed All production and test changes directly support short-window quota retention, exposure, routing, and regression coverage required by issue #2047.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ingw/fix-k12-short-window-2047

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lidge-jun

lidge-jun commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Independent review before merge found one routing-behavior blocker, so this PR is held as needs-work rather than merged:

Fail-open in computeCodexUsageScore (src/codex/routing.ts:322-334). On current dev, an account whose cached quota carries only shortPercent (no weekly/monthly) scores CODEX_UNKNOWN_USAGE_SCORE (101) and is never preferred. With this PR, shortPercent: 0 alone yields score 0, making an account with unverified long-window quota the "coolest" candidate in pickLowestUsage* helpers (src/codex/routing.ts:1128-1145). Since a short-only WHAM enters the valid cache (tests/codex-routing.test.ts:1280), routing can now prefer accounts whose weekly/monthly state is unknown or exhausted.

Suggested fix: keep the short window as an additional pressure signal only when a governing long window is present — include shortPercent in the max only if weeklyPercent / monthlyPercent (per plan shape) is finite; otherwise return CODEX_UNKNOWN_USAGE_SCORE as before. Please add a regression test for the short-only case (long windows absent, score stays unknown).

Everything else looked good: detail preservation across cache/API/dashboard, regression coverage, no scope creep, CI green on the exact head. Happy to merge once the routing guard lands.

@lidge-jun

Copy link
Copy Markdown
Owner

Update on the hold, with new information: #2062 fixes the same issue (#2047) and carries the same blocker, so this is a shared root cause rather than a reason to prefer one PR.

The blocker on both is the short-only fail-open in computeCodexUsageScore: an account whose cached quota carries only shortPercent scores the burst value instead of CODEX_UNKNOWN_USAGE_SCORE, so shortPercent: 0 makes an account with unverified long-window quota the coolest candidate for pickLowestUsageAmong.

Where the two differ:

So neither supersedes the other. What either needs to land: include shortPercent in the max only when the governing long window (per plan shape) is finite, and otherwise keep returning CODEX_UNKNOWN_USAGE_SCORE — plus the short-only regression test.

If you two want to combine, this PR's preservation handling plus a gated scorer is the shortest path.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 64 / 80

draft 가 아니고 #2047 를 parse → cache → accounts API/DTO → routing 까지 닫으려 합니다. #2062 와 같은 이슈를 고치지만, 이 쪽이 hasKnownQuotaValue / snapshotHasShort / updateAccountQuota 보존과 GET /api/codex-auth/accounts?refresh=1 회귀가 있어 범위가 더 깁니다. 본문은 해당 테스트 376 passed 와 typecheck / privacy:scan 을 적었습니다. package.json 버전 범프는 없습니다.

setAccountQuotaFromParsed 는 credits-only 에서 existing short 튜플을 유지하고, snapshotHasShort(quota) 가 참이면 incoming shortPercent / shortResetAt / shortWindowSeconds 만 덮습니다. 거짓이면 기존 burst 를 유지합니다. snapshotHasShort 는 세 필드 중 하나만 있어도 참입니다. incoming 이 shortResetAt 만 있으면 기존 shortPercent 보존 분기로 안 들어가고, incoming 에 없는 필드는 next 에 안 붙습니다. 부분 헤더 갱신이 reset 만 실어 보내면 0% 데이터가 사라질 수 있습니다. updateAccountQuota(레거시 weekly 갱신)는 existing short 를 스프레드로 남겨 두어 그 경로는 더 안전합니다.

hasKnownQuotaValueshortPercent 가 들어가서 shortPercent: 0 만 있는 스냅샷도 known 입니다. computeCodexUsageScore 는 thirty-day-only 에 monthly+short, 그 외 weekly+monthly+short 를 넣고 max 를 씁니다. #2062 와 달리 monthly 없는 thirty-day 는 burst 만으로 점수가 납니다. 테스트는 shortPercent: 0 → 0, go 플랜에서 short 14 가 monthly 12 를 이깁니다. weekly 만 있는 go 는 여전히 unknown 입니다.

tests/codex-auth-api.test.ts 가 캐시 재구성(부분 short 갱신, updateAccountQuota, credits-only)과 K12 primary_window used 0% 가 accounts DTO 의 shortPercent: 0 으로 나오는지를 봅니다. tests/codex-routing.test.ts 에 파서 단위의 zero-valued short-only WHAM 도 있습니다. 이 경로가 #2047 재현에 가장 가깝습니다.

해결방안: snapshotHasShort(incoming) 이 참이어도 빠진 short 필드는 existing 에서 채우든지, 세 필드가 같이 올 때만 교체하십시오. #2062 와 하나만 머지하십시오. 이 PR 을 쓸 거면 부분 short 갱신(reset only) 케이스를 테스트에 하나 더 넣으십시오.

이 댓글은 grok-bot이 작성했습니다

lidge-jun added a commit that referenced this pull request Aug 19, 2026
parseUsageQuota filled shortPercent and setAccountQuotaFromParsed dropped it, so
the 5-hour burst window never reached the cache, the accounts DTO, the dashboard,
or routing. A saturated short window was invisible to account selection.

Carries @Ingwannu's #2056: shortPercent joins hasKnownQuotaValue, a new
snapshotHasShort keeps a short-only snapshot from reading as empty, partial
weekly/monthly snapshots no longer clobber a known short window, and
updateAccountQuota carries the tuple.

Also fixes the blocker raised in review on both #2056 and #2062: the scorer took
Math.max over every finite window, so a snapshot carrying only shortPercent: 0
scored a flat 0 and made an account whose long windows were never observed look
like the emptiest in the pool - pickLowestUsageAmong would then send every
request to it. The burst window now refines a known long-window position instead
of standing in for one, and returns CODEX_UNKNOWN_USAGE_SCORE until a governing
window is actually observed.

The ported test asserted the old behavior directly
(computeCodexUsageScore({ shortPercent: 0 }) === 0); it is replaced by a case
that pins the corrected contract in both directions.

Closes #2047
@lidge-jun

Copy link
Copy Markdown
Owner

Thanks for this, @Ingwannu — closing as superseded by #2141, which carries your implementation: snapshotHasShort, shortPercent in hasKnownQuotaValue, the partial-snapshot preservation, and the updateAccountQuota carry. Your tests came across including the WHAM refresh → DTO case.

One thing was added on top: the scorer blocker raised in review on this PR. computeCodexUsageScore took Math.max over every finite window, so a short-only snapshot with shortPercent: 0 scored a flat 0 and made an account with unverified long windows look like the emptiest in the pool. The burst window now refines a known long-window position rather than standing in for one, returning CODEX_UNKNOWN_USAGE_SCORE until a governing window is observed.

That required replacing one assertion your branch inherited (computeCodexUsageScore({ shortPercent: 0 })0) with a case pinning the corrected contract in both directions. Your work is credited in the PR description.

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.

2 participants