Repository navigation
Conversation
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Note Written by Takeover update (head
|
|
Real-account verification, as asked in #27787 (comment): a real ChatGPT Plus codex account was driven to 100% of its 5h window by normal traffic (no artificial burn), then one tiny turn was sent per side. Setup: two stock installs on one Linux box, current nightly What happened (probes at 2026-11-01 20:19 UTC, account already at primary 100%):
Both cards, identical UI:
Why the replay test does not catch this. "recovers resetAt from a fresh rate-limits read when the 429 never primed the snapshot" passes on the exact tree I tested. The harness answers the read instantly, so the backfill's continuation is still alive when it arrives. In the real run the response lands 1.6 s after the failure, and the run has already settled by then. Unproven suspicion (no source instrumented on my side): the forked backfill's continuation is interrupted at settle, so the response has nowhere to land. Worth checking which scope the Happy to re-run with an instrumented build or more scenarios (mid-turn 429, secondary-window exhaustion) if that helps. Everything above is from logs and persisted state, not memory. Verification rig and write-up produced with gpt-6.1-sol in T3 Code. |
|
Correction to my earlier comment: the "forked backfill loses the race" theory was wrong. Real cause: when a run fails, event ingestion stops at settlement, so the backfill enrichment that arrived 1.6s after the failure had zero subscribers. Live verification from today on a genuinely exhausted ChatGPT-plan codex account (natural exhaustion, watcher-confirmed at 95-99%, resetAt 17:22 IST):
Not verified / follow-ups: auto-resume did not fire on its own ~15 min past the boundary (headless rig, no browser tab open at the boundary, so a client-side scheduler would not have been alive — worth confirming whether auto-resume should be server-driven); single account, primary 5h window only; secondary weekly window not exercised; no native/electron client. |
|
Fix pushed for the CodeRabbit finding (verified real): The race is exactly as flagged. The adapter's in-memory guard ( The guard lives in the persistence path itself ( Evidence: regression test in |
# Conflicts: # apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts






Problem
On codex threads, a usage-limit failure that kills a run's FIRST model request carries no
resetAt: the mobile failure card shows a bare "Usage limit reached." and the recovery card says "The provider did not report a reset time", so auto-resume cannot be scheduled. Failures in runs that had prior successful responses do carry the reset time. Tracked in #27773 (with protocol-log evidence of both shapes from one live thread).The adapter derives
failure.resetAtonly from its per-run rate-limit snapshot ref, which is fed solely byaccount/rateLimits/updatednotifications. Those ride successful streaming responses; a 429 that ends a run before any response delivers only an empty non-codexfamily snapshot (upstream codex parses thex-codex-active-limitfamily), whichmergeCodexRateLimitscorrectly ignores. The existing retro-patch of limited items never fires because nocodex-family notification arrives while the account stays exhausted.Change
When a turn terminates as
usage_limitwith noresetAt, the adapter now reads fresh limits straight from the app-server (account/rateLimits/read, the same request the snapshot probe already uses, with the same 3s deadline) and feeds the result through the existing merge + retro-patch path, so the failure item gainsresetAtandUsageLimitRecoveryWorkercan schedule auto-resume. The notification handler's inline patch logic is extracted into a sharedapplyRateLimitSnapshot(behavior unchanged); the read is forked into the adapter scope because its response arrives on the protocol reader fiber the terminal handler runs on. A failed or timed-out read degrades to today's behavior.Scope and approval
Fixes #27773. The issue was filed with reproduction evidence before this PR; the change is limited to the codex adapter's failure path plus exporting the existing probe timeout constant.
Verification
CodexAdapterV2.test.ts(recovers resetAt from a fresh rate-limits read when the 429 never primed the snapshot) replays the live failure shape through the existing replay harness:error(usageLimitExceeded) -> emptypremiumrateLimits notification -> failedturn/completed-> adapter issuesaccount/rateLimits/read-> response carries thecodexwindow ->turn_item.updatedwithfailure.resetAtset. It fails onmain(no read issued, no resetAt) and passes with the change.vp test run apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts: 117 passed (117).vp run check:changed: format, lint, knip, graphql, typecheck (3 packages), test (3 files) - all pass.Not verified: live behavior against a real exhausted ChatGPT-plan account (card date, "Resume at reset", an actual auto-resume) - verified at the adapter layer instead, via the same
turn_item.updatedpath the proven late-reset case uses end to end.Written with zai glm-5.3 in T3 Code.