Skip to content

fix(codex): refresh expired pool credits before account selection - #7290

Closed
Hylouis233 wants to merge 1 commit into
lidge-jun:devfrom
Hylouis233:fix/refresh-spendable-credits
Closed

Hylouis233 wants to merge 1 commit into
lidge-jun:devfrom
Hylouis233:fix/refresh-spendable-credits

Conversation

@Hylouis233

@Hylouis233 Hylouis233 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Pool accounts with exhausted included usage can be incorrectly dropped five minutes after a successful credit observation: inference headers renew the aggregate quota timestamp without renewing the separate spendable-credit timestamp. The cache then looks fresh while routing sees stale credits, producing repeated local refusals despite available funds.

  • Refresh expired or absent spendable-credit evidence before selecting an opted-in, unpaused exhausted pool account. Bypass usage-only cache freshness and coalesce metadata refreshes through existing priming; dispatched failed observations are paced per credential generation.
  • Keep the five-minute credit validity boundary. A failed observation never authorizes spending stale funds, inference, or reset-credit redemption. Exact-account refresh is limited to its selected account, and cancelled selection does not start discovery.
  • Do not fall back to the observed native-main caller credential while that main account is paused. Unrelated caller-owned Direct credentials retain their behavior.

Verification

  • Seven added regression cases exercise expiry across prime/selection/cache/exact-account paths, fresh negative evidence and opt-outs, cancellation, and failed-observation pacing; the paused-main fallback has separate coverage. The first five regression cases were also run against unchanged source and all failed, then passed with the fix.
  • Bun 1.4.3 focused suite: 159 passed, 0 failed across quota priming, auth context, pool/main credits-after-limit, and credit publication.
  • bun run typecheck, bun run structure:check, bun run privacy:scan, and bun scripts/file-size-ratchet.ts passed.
  • Documentation: cd docs-site && bun install --frozen-lockfile && bun run build passed (577 pages).
  • bun run test:changed was started with the pinned Bun runtime but interrupted before a terminal result. It is not reported as passing. Full-suite validation remains outstanding; this PR is draft pending broader checks.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • Required local validation passed; commands, results, and any full-suite exception are documented.

  • I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions github-actions Bot added bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed labels Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/codex/auth-context.ts.

@github-actions

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • hygiene: unsponsored_surface.

What to do

  • Fix unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/codex/auth-context.ts.
  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ Required local validation passed; commands, results, and any full-suite exception are documented.
  • ⬜ I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.
@Hylouis233 Tick the boxes once required local validation has passed with commands, results, and any full-suite exception documented, your branch is on the latest dev commit, and every correct Codex and CodeRabbit finding is resolved.

@Ingwannu

Copy link
Copy Markdown
Owner

Intake at b64f0a3054. The latest hygiene job's annotation is specifically PR hygiene failed: unsponsored_surface; this is not evidence that the quota regression test failed. Sponsorship is an owner/maintainer boundary decision, not a contributor instruction to bypass the gate. Keep Draft pending that review and exact-head functional CI. The evidence needed is refresh coalescing/dispatch pacing by credential generation, zero inference or reset redemption during metadata refresh, no stale-credit spending after failure, exact-account isolation and native-main pause behavior. I did not redeem credits, probe accounts or run a scan.

@lidge-jun

Copy link
Copy Markdown
Owner

Closing as a duplicate of #7381, which renews pool credit evidence on the existing sweep. Your paused-main regression test will be carried into the pool/hard-lock bundle with Co-authored-by credit. Thank you for the fix.

@lidge-jun lidge-jun closed this Oct 19, 2026
lidge-jun added a commit that referenced this pull request Oct 19, 2026
…locks, and scope Direct fallback to the locked main (#7393)

* fix(codex): renew opted-in pool credit evidence on the existing sweep

* test(codex): assert renewal performs no native-main access

* fix(codex): limit Direct hard-lock fallback to the locked account

Use the original route's credential ownership and observed-main caller check before applying Direct hard-lock fallback. Preserve binding, missing-bearer and disabled-route behavior; document and cover the boundary.

* fix(responses): break hard-lock caller-check import cycle

Move the caller-check factory into core-auth, which owns the credential-domain helpers, and update its request-preparation import. Keep the function body and routing/authentication policy unchanged.

* fix(codex): revalidate hard-lock fallback targets before request rewriting

* fix(codex): clear failure windows when account identities are removed

Forget account-scoped failure samples and pinned degradation warnings during deletion and physical main-account replacement. Preserve sibling account windows and cover both lifecycle paths.

* fix(codex): fence late failure-window samples by dispatch identity

Carry the main dispatch proof and stored Pool credential generation into terminal outcomes. Reject stale success and transient samples after identity or credential replacement, while preserving existing health and probe handling. Cover main identity ABA, same-ID Pool recreation, context reuse, and valid-current controls.

* fix(codex): revalidate tagged failure samples after credential replacement

Retain each stored Pool sample's dispatch credential generation and filter it against one current store snapshot per insertion or lookup. Reset retired-generation degradation, recovery timers and pin warnings without clearing a replacement on stale arrival. Cover both unlocked-read boundaries, derived state and the 512-sample snapshot bound.

* fix(codex): preserve failure windows when credential snapshots fail

Distinguish failed reads or parsing from valid absent/empty credential stores. Preserve retained samples and derived failure-window state while evidence is unknown, keeping existing boolean liveness callers compatible. Cover fixture read/parse failures, recovery and empty-store retirement without adding snapshot reuse optimization.

* fix(codex): reject non-object credential snapshot shapes

Require a non-null, non-array object before normalizing store rows. Preserve failure-window evidence for invalid top-level arrays or scalars while keeping missing files and empty objects valid. Cover six top-level shapes without changing row normalization.

* refactor(responses): keep passthrough-dispatch within its line budget

* fix(codex): keep a paused main caller out of Pool fallback

Carries the paused-main regression from #7290 and the production guard it proves is needed, plus a per-generation credit-renewal pacing test requested at intake.

Co-authored-by: Hylouis233 <88263959+Hylouis233@users.noreply.github.com>

* fix(codex): recheck a predicted-reset main hard lock with metadata (#7312)

A predicted reset now arms the capped WHAM recovery schedule instead of suppressing it, so an early reset redeemed outside opencodex is learned from verified metadata. The schedule never runs past the predicted reset, skips paused and needs-reauth mains, and a predicted reset still never lifts the lock by itself.

* test(codex): await the async hard-lock fallback override in #7340 cases

#7328 made applyMainHardLockFallbackOverride async for the Direct caller check; the #7340 revalidation cases were written against the synchronous form and asserted on a Promise once both landed.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(codex): recheck Direct hard-lock ownership synchronously before rewriting

The async caller check now resolves to a synchronous ownership proof that is evaluated after the last await, so a main identity or bearer change during the policy-binding wait cannot reroute the original caller onto the fallback.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(codex): keep predicted-reset hold rechecks paced across transitions (#7312)

Track the first hold per identity/credential separately from the backoff reservation so transient 401/403 retries are not re-armed, arm a hold that appears during token preparation instead of reading immediately, pull the due time in when the predicted reset moves earlier or passes, and keep hold reads at least one base interval apart.

* fix(codex): honor a revised reset that elapses between hold sweeps (#7312)

Recovery scheduling now reads the latest blocking reset from current evidence even after it has passed, so a reset revised earlier and expired before the next sweep releases the old reservation. It remains scheduling evidence only; admission still requires verified metadata.

---------

Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
Co-authored-by: Epinephrine <luvs01@hanmail.net>
Co-authored-by: Hylouis233 <88263959+Hylouis233@users.noreply.github.com>
Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Follow-up: as promised, the paused-main regression test (and the two auth-context.ts paused-main guards it proved necessary) landed on dev in #7393 (squash d3dafd3) with a Co-authored-by trailer. Thanks!

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

Labels

bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants