- Fixed a stale-runtime recovery deadlock that returned a permanent
503 "All managed Codex accounts are temporarily unavailable for this runtime request."even when accounts were healthy anddoctorpassed. Per-account transient state (coolingDownUntil,cooldownReason,rateLimitResetTimes) is serialized into the V3 snapshot, sorecoverStaleRuntimeState'sloadFromDisk()restored the same state that had wedged the pool, while the recovery guard refused to run whenever any account's skip reason was"rate-limited"or"cooling-down*". The guard now suppresses recovery only on"policy-blocked"(external, unchanged by a reload), andrecoverStaleRuntimeStatecallsAccountManager.clearAccountTransientState()thenflushPendingSave()before publishing the reloaded manager, so the cleared snapshot survives a restart within the debounce window. The two halves are coupled — each is pinned by a regression test that fails if the other is reverted (#606, #607). - Fixed the missing-
accountIdcooldown branch inrunRotationLoopnot persisting its mutation. WhenresolveAccountIdreturned null the branch calledmarkAccountCoolingDownbut — unlike every sibling cooldown branch (network-error, 429, server-error, 401 invalidation) — never calledsaveToDiskDebounced(), so a restart inside the 30s window dropped the cooldown and immediately re-selected the still-broken account. Added the missing persist (#608). - Fixed the short-retry 429 path in the runtime fetch loop not persisting its rate-limit window. The branch mutated the disk-serialized
rateLimitResetTimesviamarkRateLimitedWithReason, then slept and retried without asaveToDiskDebounced(), unlike the full-rotation branch beside it; a crash during the retry sleep lost the reset time. Added the missing persist (#609).
- Added regression coverage for all three durability fixes:
clearAccountTransientStateunit tests (cooldown clear, rate-limit clear incl. future windows, mixed state, no-op on empty pool, flush-backed persistence); all-cooling-down pool recovers to200while policy-blocked pools still suppress recovery; missing-accountIdand short-retry branches each assertsaveToDiskDebouncedis scheduled. Each fix's test fails if its source change is reverted (verified by mutation).
- Stable release published under the
latestdist-tag (npm i -g codex-multi-auth). - All three fixes share one root-cause class: transient account state is persisted to disk, so any path that mutates it must schedule a write or a restart silently drops it. All mutation sites were audited; these were the gaps.
- The
codex-multi-auth rotation reset-rate-limitscommand remains available as a manual escape hatch. - Promotes the
2.3.0-betaline to stable; all fixes from2.3.0-beta.1→2.3.0-beta.3are included.