Triage
Confirmed on t3code/codex-turn-mapping at 30f213185 (current tip of that branch). Not a duplicate, and main does not have this recovery path.
The composer banner and Usage → Limits read different snapshots, and nothing copies one into the other.
The banner in UsageLimitRecoveryBanner uses only thread.runtime.usageLimitResetAt. That value is the latest root error item's failure.resetAt (threadErrorSummary). Codex sets it from the in-session rateLimitSnapshot in CodexAdapterV2, which starts empty and is filled only by account/rateLimits/updated. codexUsageLimitResetAt returns null unless every window at usedPercent >= 100 has a resetsAt. A notification that only names rateLimitReachedType, a 100% window with no resetsAt, or a non-codex limitId (those are ignored, including model-specific buckets) leaves resetAt null. The banner then shows "Reset time unavailable; retry manually" and hides Resume at reset / Snooze until reset. A later notification can backfill the turn item, but only when that notification itself produces a non-null reset.
Usage → Limits is the status probe in CodexProvider: account/rateLimits/read, mapped by codexRateLimitsToLimits, preferring rateLimitsByLimitId.codex. That result is what draws the Session countdown. It is never written into the adapter's rateLimitSnapshot. A probe that already has Session resetsAt therefore does not repair a turn that stopped without one. Weekly quota still remaining does not change this. Only exhausted windows are considered, and the probe windows are not consulted at all.
One correction: the stored sentence "Codex usage limit reached. Send the message again once the limit resets." is codexUsageLimitMessage's no-snapshot fallback. The adapter does not call that helper. It stores Codex's turn.error.message. The missing "resets in 4h 12m" clause is because that formatter is not on the turn path. The broken part is resetAt, which is what the banner and the auto-resume worker both require.
Same usageLimitResetAt field drives the mobile recovery card. Workaround is to retry manually once the Limits countdown reaches zero.