Skip to content

[Feature]: opt-in reset-credit recovery after verified pre-stream quota exhaustion #657

Closed as not planned

Description

@luvs01

Area

Authentication / account pools / quota recovery / request replay

Goal

Allow OpenCodex to recover an unexposed Codex turn after the upstream has positively identified quota exhaustion by optionally consuming one real reset credit and replaying that same logical request.

The trigger is the authoritative upstream rejection.

Displayed quota percentages, local usage estimates, generic rate limits, transient server errors, and authentication failures must not authorize reset-credit consumption.

Automatic redemption must remain opt-in and disabled by default because a reset credit is scarce and irreversible.

Current status

This feature is partially implemented. Two safety foundations are now merged into dev, but the end-to-end automatic redemption and replay path is not wired yet.

Delivered: verified reset-eligible exhaustion classification

PR #866 merged as a088e4b1:

feat(codex): classify reset-eligible quota rejection

OpenCodex can distinguish verified reset-eligible pre-stream exhaustion from unrelated failures.

Only exact structured exhaustion signals such as:

usage_limit_exceeded
insufficient_quota

on an eligible pre-stream 429 / 402 response may be classified as:

reset-eligible-exhaustion

The classifier fails closed for malformed, truncated, oversized, cancelled, ambiguous, or untrusted error bodies.

Generic 429, unverified 402, transient 5xx, 401, 403, and other upstream failures do not become reset-credit eligible merely from status or message text.

This classification boundary must be reused rather than reimplemented.

Delivered: reset-credit recovery safety coordinator

PR #1410 merged as d91ca60c72f70f08c479da174910581657ed783c:

feat(codex): coordinate reset-credit recovery attempts

This adds the coordinator-only safety foundation for the scarce, irreversible reset-credit operation. It proves and enforces the core invariants before runtime wiring:

  • one recovery attempt per logical turn;
  • one stable UUIDv4 operation identity reused across bounded consume retries;
  • process-shared single-flight coordination for the same account and exhaustion generation;
  • generation-bound recovery with revalidation before irreversible dispatch;
  • fail-closed cancellation before dispatch;
  • output-exposure guards that prevent recovery after client-visible output has committed;
  • deterministic handling of reset, already_redeemed, nothing_to_reset, and no_credit;
  • bounded retries, deadlines, waiters, process state, and orphaned adapter execution accounting;
  • monotonic terminal fences so uncertain irreversible outcomes cannot blindly spend another credit;
  • fail-closed handling for malformed adapters, signals, stale generations, contract mismatches, and capacity saturation.

PR #1410 is intentionally not the runtime integration. It does not automatically consume a credit, replay a Responses request, add recovery policy/config, select alternate accounts, or expose UI controls.

Remaining implementation

The next slices should wire the proven coordinator into the real request path without weakening its invariants.

At minimum, the remaining work is:

  • add explicit opt-in policy, defaulting off;
  • define recovery priority such as alternate-first and/or reset-first;
  • invoke the real reset-credit consume path only after verified pre-stream reset-eligible exhaustion;
  • refresh authoritative account/quota state after a successful or idempotent-compatible redemption;
  • replay the same complete logical request at most once;
  • preserve trustworthy replay state, including previous_response_id continuations;
  • reuse the generic account-pool routing/failover contract from [Feature]: Generic OAuth account-pool failover with session affinity and quota-aware selection #695 for alternate-account selection;
  • prevent detached replay after client cancellation, disconnect, or proxy shutdown;
  • add bounded, sanitized recovery diagnostics;
  • expose config/UI management controls only after runtime integration is proven safe.

Existing alternate-account recovery

OpenCodex already has bounded pre-stream alternate-account retry behavior from #584 / #585.

That behavior remains separate from spending a reset credit.

A generic 429 or 402 may still participate in the existing alternate-account retry policy where currently supported, but it must not authorize an irreversible reset-credit operation.

This issue owns the additional recovery step that may occur only after:

verified reset-eligible exhaustion

has been established.

Desired policy

Add an explicit opt-in policy for reset-credit recovery.

Conceptually:

{
  "codexQuotaRecovery": {
    "enabled": true,
    "autoRedeemResetCredit": true,
    "priority": "alternate-first"
  }
}

The exact configuration names may follow existing conventions.

At minimum:

enabled = false

must remain the default.

If automatic redemption is enabled, support an explicit recovery priority such as:

alternate-first
reset-first

Alternate first

account A
  -> verified reset-eligible exhaustion
  -> try eligible alternate account
  -> no alternate succeeds
  -> redeem at most one reset credit for A
  -> replay A once

Reset first

account A
  -> verified reset-eligible exhaustion
  -> redeem at most one reset credit for A
  -> replay A once
  -> if recovery does not succeed, try eligible alternate

The exact candidate-selection mechanics should reuse the account-pool routing contract rather than create an independent failover engine.

Hard safety boundary: pre-stream only

Automatic redemption and replay are allowed only before any model-visible effect has escaped.

Do not auto-replay after OpenCodex has exposed any assistant output, reasoning output, tool call, tool result, Responses output item, terminal event, or other client-visible model event.

Once output is committed, automatic replay could duplicate text or tool side effects.

The recovery contract is:

request admitted
      ↓
upstream rejects before output
      ↓
verified reset-eligible exhaustion
      ↓
recovery may run

not:

partial response emitted
      ↓
failure
      ↓
replay

Replay completeness

The same logical request may be replayed only when OpenCodex still has a complete, trustworthy replay source.

This includes requests whose context depends on:

previous_response_id

If the required continuation/history cannot be expanded or reconstructed safely, fail closed rather than replaying an incomplete conversation.

Recovery must preserve request body semantics, conversation context, model selection, relevant account affinity semantics, tool definitions, and protocol metadata required by the original request.

Do not reconstruct a “similar” request from partial diagnostics.

Redemption outcomes

Use the existing reset-credit mechanism and its normalized outcomes:

reset
already_redeemed
nothing_to_reset
no_credit

reset

Refresh authoritative account/quota state and allow one bounded replay.

already_redeemed

Treat this as idempotent-compatible only when it belongs to the same logical recovery generation. Refresh state before replay.

nothing_to_reset

Do not fabricate success. Continue to the next permitted recovery step or return the original failure.

no_credit

Do not repeatedly retry redemption. Continue to an eligible alternate if policy allows, otherwise return the failure.

Safety invariants

The coordinator from #1410 now provides the foundation for these invariants, and runtime wiring must preserve them:

  • at most one reset credit automatically consumed per logical turn;
  • one idempotency identity reused across retries of the same redemption operation;
  • concurrent requests for the same account/exhaustion generation cannot independently consume several credits;
  • cancellation before redemption dispatch prevents redemption and replay;
  • an uncertain consume result cannot authorize blind second-credit consumption;
  • account/quota state is revalidated immediately before irreversible redemption;
  • recovery is bounded and cannot recurse indefinitely;
  • no replay may occur after output exposure.

Cancellation, disconnect, and shutdown

If the client disconnects or cancels before redemption is dispatched:

do not redeem
do not replay

If redemption has already been sent upstream, OpenCodex must report/record its outcome truthfully and must not assume it can undo the credit consumption.

A cancelled request must never leave behind a background replay that later sends a model request with no attached client.

Proxy shutdown must obey the same rule.

Failure classes that must stay separate

Generic rate limiting

429, Retry-After, or rate_limit_error without verified reset eligibility use the existing rate-limit/failover behavior. Do not spend a reset credit.

Transient provider failure

500, 502, 503, 504, 520, 521, 522, overload, and temporary availability failures use the existing transient retry/failover policy. Do not spend a reset credit.

Authentication / permission

401 / 403 use the appropriate reauthentication or permission behavior. Do not spend a reset credit.

Connection failures

Connection resets, stale sockets, DNS errors, and similar transport failures are not quota evidence. Do not spend a reset credit.

Relationship to generic account failover

#695 owns the generic OAuth account-selection, health, affinity, and failover engine.

This issue should consume that engine for alternate-account selection. It must not introduce a second competing account-pool state machine merely for reset-credit recovery.

Relationship to other quota automation

#822

Tracks proactive time-based redemption before a reset credit expires. Its trigger is credit expiry, not verified upstream quota rejection.

#823

Tracks post-reset activation intended to establish the next quota window. That happens after a natural reset and is separate from failed-turn recovery.

Natural-reset waiting

Waiting minutes for an upcoming natural quota reset while holding the original client request open has a materially different lifecycle contract and remains out of scope here.

Observability

Record bounded, sanitized recovery diagnostics such as:

trigger: reset-eligible-exhaustion
priority: alternate-first
alternateAttempted: true
redemptionAttempted: true
redemptionOutcome: reset
replayAttempt: 1
finalOutcome: success

Do not log email addresses, OAuth tokens, refresh tokens, raw account IDs, raw upstream response bodies, or reset-credit authorization material.

Use safe account references where correlation is required.

Management surfaces

After runtime integration is proven, expose the policy consistently through supported management surfaces.

Users should be able to determine:

  • whether automatic reset-credit recovery is enabled;
  • which recovery priority is selected;
  • that at most one credit may be consumed per unexposed logical turn.

The UI must make clear that enabling the feature permits OpenCodex to consume a scarce reset credit automatically.

Do not bundle runtime safety work, account recovery engine, Dashboard controls, and documentation into one implementation PR.

Out of scope

This issue does not own:

Acceptance criteria

Delivered foundation

  • Reset-eligible pre-stream exhaustion has an explicit semantic classification.
  • usage_limit_exceeded / insufficient_quota are distinguished from generic rate limiting.
  • Generic 429 and unverified 402 do not authorize reset-credit consumption.
  • 5xx, auth, permission, malformed, and ambiguous responses fail closed for reset-credit eligibility.
  • Classification is bounded and does not consume the original upstream response.
  • One recovery attempt is reserved per logical turn.
  • One stable operation identity is reused across bounded consume retries.
  • Concurrent same-account/same-generation attempts are single-flighted.
  • Cancellation before coordinator dispatch fails closed.
  • Output exposure prevents recovery dispatch.
  • Account/exhaustion generation is revalidated before irreversible dispatch.
  • reset, already_redeemed, nothing_to_reset, and no_credit have deterministic coordinator outcomes.
  • Coordinator state, retries, waiters, deadlines, and retained executions are bounded.
  • Focused coordinator concurrency, cancellation, idempotency, generation, failure, and re-entrancy regressions exist.

Remaining runtime integration

  • Automatic reset-credit recovery is explicit opt-in and defaults off.
  • alternate-first and/or reset-first policy is defined explicitly.
  • The coordinator is wired into the real pre-stream Responses/Codex failure path.
  • Real redemption occurs only for verified reset-eligible pre-stream exhaustion.
  • No replay occurs after client-visible output has begun.
  • Replay requires complete trustworthy conversation/request state.
  • previous_response_id replay is expanded/reconstructed safely or fails closed.
  • Successful/idempotent-compatible redemption refreshes authoritative account/quota state before replay.
  • The same logical request is replayed at most once.
  • Client disconnect cannot leave an orphan background replay.
  • Proxy shutdown cannot leave a later detached replay.
  • Alternate-account selection reuses the generic account-pool policy.
  • Runtime recovery attempts are bounded and cannot recurse indefinitely.
  • Diagnostics expose recovery state without credential/account leakage.
  • End-to-end integration tests cover redemption, replay, disconnect/shutdown, alternate policy, and failure classes.
  • Management controls are added only after runtime integration is proven.
  • Accepted end-to-end implementation is merged into dev.

Related

Activity

  1. github-actions commented on Jul 29, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  2. added
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    toolstool_calls, MCP, web-search / sidecar tools
    on Aug 1, 2026
  3. Ingwannu commented on Aug 1, 2026

    @Ingwannu
    Owner

    Maintainer direction: the rejection-only boundary and safety split in this proposal are the right starting point, but this remains design work rather than an implementation-ready issue.

    The first implementable slice should be semantic classification of reset-eligible pre-stream exhaustion versus generic 429/5xx/auth failures, with no automatic credit spending. Automatic redemption can follow only after idempotency, single-flight, cancellation/disconnect, replay completeness, and one-credit-per-logical-turn invariants are independently proven. Natural-reset waiting should be a separate slice because it keeps a client request attached across time and has different lifecycle failure modes.

    Keeping this open; do not combine all policy, waiting, redemption, GUI, and docs into one PR.

  4. luvs01 commented on Aug 1, 2026

    @luvs01
    CollaboratorAuthor

    The maintainer-requested first implementation slice is now available as PR #866. It adds only bounded, fail-closed semantic classification for structured pre-stream usage_limit_exceeded / insufficient_quota versus generic 429, unverified 402, transient 5xx, and auth failures. Existing #584 alternate-account retry remains unchanged. There is no reset-credit consumption, waiting, recovery policy, GUI, or documentation surface in this PR. Focused classifier/server-auth coverage passes 69/69 on Bun 1.4.0-canary.1 and bundled Bun 1.3.14; typecheck and both privacy scans pass.

  5. added
    roadmapAccepted long-term work item; needs its own project cycle
    on Aug 3, 2026
  6. Ingwannu commented on Aug 4, 2026

    @Ingwannu
    Owner

    Follow-up: the maintainer-requested first slice from PR #866 has merged into current dev as merge commit a088e4b1, with required CI green.

    That merge is intentionally classification-only: it distinguishes structured pre-stream reset-eligible exhaustion from generic 429, unverified 402, 5xx, and authentication failures. It does not spend reset credits, wait across natural reset windows, add recovery policy, or add GUI behavior.

    Keeping this tracking issue open because the broader automatic redemption work is not complete. Any next PR should remain a separate slice and prove idempotency, single-flight behavior, cancellation/disconnect handling, replay completeness, and one-credit-per-logical-turn before it can consume a credit.

  7. Wibias commented on Aug 7, 2026

    @Wibias
    Contributor

    Related quota automation

  8. changed the title [-]Add opt-in rejection-only quota recovery with automatic reset-credit redemption[/-] [+][Feature]: opt-in reset-credit recovery after verified pre-stream quota exhaustion[/+] on Aug 9, 2026
  9. lidge-jun commented on Aug 19, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 72 / 80

    현재 dev에는 안전 기반 두 장이 있습니다. src/codex/quota-rejection.ts는 사전 스트림 usage_limit_exceeded/insufficient_quota만 reset-eligible-exhaustion으로 분류하고, 그 외 429/미검증 402/5xx/인증은 fail-closed입니다. src/codex/reset-credit-recovery.ts는 턴당 한 시도, 안정 UUIDv4, 프로세스 공유 단일 비행, 세대 재검증, 출력 노출 가드를 정의합니다. 요청 경로에서 이 모듈을 import하는 생산 호출은 검색되지 않습니다. 자동 소모와 재실행은 아직 연결되지 않았습니다.

    이슈가 원하는 것은 노출되지 않은 Codex 턴이 업스트림 쿼터 거절로 죽었을 때, 옵트인으로 크레딧 한 장을 쓰고 같은 논리 요청을 다시 보내는 것입니다. 표시 퍼센트, 로컬 추정, 일반 레이트 리밋, 일시 오류, 인증 실패는 소모를 허가하지 않습니다. 기본값은 꺼짐입니다.

    네이티브 신뢰성이 점수입니다. 클라이언트에 토큰이 나간 뒤 재실행하면 중복 출력이 됩니다. isOutputExposed가 참이면 복구를 시작하면 안 됩니다. Windows/서비스 이중 프로세스가 같은 계정·같은 거절 세대에 두 장을 쓰면 안 되므로 조정자가 있습니다. 그 조정자를 우회하는 타이머나 수동 consume 호출은 계약을 깨뜨립니다.

    위생적으로 #822(만료 전 선제 상환)와 트리거를 합치지 마십시오. 이 이슈는 거절이고, 저쪽은 시간입니다. 분류기를 재구현하지 말고 재사용해야 합니다.

    해결방안은 기존 분류와 조정자를 요청 경로에 옵트인으로 연결하고, 노출 가드가 열린 뒤에만 consume+replay 하는 것입니다. 기본 꺼짐을 유지합니다. 패치는 추측하지 않으며, 현재 트리는 분류·조정자까지이고 엔드투엔드 배선은 없습니다.

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

  10. lidge-jun commented on Aug 20, 2026

    @lidge-jun
    Owner

    Codex 쪽에서 리셋권(reset credit) 정책을 없앤 것으로 보임. 이 이슈는 그 전제 위에서 설계된 거라 지금은 구현할 대상이 없음.

    닫음. 나중에 업스트림 정책이 다시 생기면 이 이슈를 다시 열어 주세요.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansenhancementNew feature or requestroadmapAccepted long-term work item; needs its own project cycletoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions