Overtime: When the Plan Runs Out, Credits Take Over
My harness stopped Claude Code and Codex at 95% of their plan windows, even when usage credits could have paid for more. Now it checks for usable credits first, keeps working until they run low, and says so in the status bar.
| โก | TL;DR One extra question before any hold: can this provider's own usage credits pay for the next request? If not, the old 95%/98% rule stands. If they can, the agent keeps working, the status bar says so, and the hold returns when the credits run low, run out or a request is refused. The signals are already on disk, in Codex's session transcript and in Claude Code's own state file. Claude Code keeps no balance. Proven: tests for every branch, a live session that crossed 95% and kept working, a replay at 100%. Not yet observed: a request actually billed past 100%. |
| ๐งญ | Start here: make your own limit watch credit-aware 1. Read your Codex credit state. Run the first command below. It shows the credit state reported in the newest record of your most recently written transcript, which can be old if no session ran lately: 2. Read your Claude Code credit state. Run the second command; it follows 3. Change one condition. "Window at 95%, hand over" becomes "window at 95% and no usable credits, hand over". A refused request still wins over everything. 4. Keep a small reserve where you can see a balance. Codex reports one, so hold again a little before zero; the agent gets to checkpoint instead of dying mid-step. 5. Show it. Put the credit state in your status line, and send one visible hook message when it changes. In Codex, that hook message is the only surface you control. |
# 1. Codex: the newest rate-limit record of the most recently written transcript (it can be stale)
f=$(ls -t ~/.codex/sessions/*/*/*/rollout-*.jsonl | head -1)
grep '"token_count"' "$f" | tail -1 |
jq '.payload.rate_limits | {primary, credits, reached: .rate_limit_reached_type}'
# 2. Claude Code: the account's cached usage-credit state
jq '{enabled: .oauthAccount.hasExtraUsageEnabled,
reason_present: has("cachedExtraUsageDisabledReason"),
reason: .cachedExtraUsageDisabledReason}' "${CLAUDE_CONFIG_DIR:-$HOME}/.claude.json"The rule that stopped too early
My harness has watched usage limits since September. It reads Claude Code's five-hour and seven-day windows from the statusline payload and Codex's windows from its session transcript. At 95% it tells the agent to stop starting new work, checkpoint, commit, write a takeover record and hand over to the other agent. At 98% it allows only the step in flight. Unattended loops pause the lane. I wrote up the original design in Handling AI Usage Limits and Clean Handovers.
That rule assumed the window was the end of the road. With usage credits, it is not. Credits exist precisely so the work continues past the window, and a harness that wraps up at 95% throws that away. It also costs something real: a handover drops the session's working context, and the next agent has to rebuild it from the record.
So I added one question before any hold: can this provider's own credits pay for the next request?
The change proved itself before it was finished. While I was building it, the weekly window of the account running the session crossed 95%. Under the old rule, the session would have received "Stop starting new work or delegates; reach a safe stopping point after the current step." What it received instead was a one-time note: "This is information, not a request to stop: keep working normally and do not hand over because of the plan window." The session kept working.
What the vendors promise
Anthropic's help article Manage usage credits for paid Claude plans is explicit. Once an included limit is used up, "Your requests switch to pay-as-you-go pricing on usage credits and keep working." That lasts "until you reach your monthly spend limit, or your balance runs out with auto-reload off", and "Usage credits apply to both Claude conversations and Claude Code terminal usage." The Claude Code plan article puts it for Max users: "Enable usage credits to keep using Claude with your Max plan after you reach your five-hour session limit, your general weekly limit, or your separate weekly limit for Fable."
OpenAI's Codex pricing page says the same in one line: "After you reach your included limits, available credits let you continue working."
Both vendors also describe a small grace for work already in flight: Claude Code's capped "Usage limit reached ยท wrapping up", and Codex's note that an active turn can continue "subject to fair use limits". That grace finishes a step; it is not a reason to launch the next iteration of a loop. Credits are.
Credits are not unlimited either. Balances end, spend caps are reached, and a request can still be refused. The harness has to keep every one of those stops.
Where the credit signal lives
This is the part that took the research. The two CLIs expose very different things.
On the Codex side, the open-source code makes the semantics clear. The response headers x-codex-credits-has-credits, x-codex-credits-unlimited and x-codex-credits-balance feed the credit snapshot. Codex's own usage-limit notice in the TUI returns nothing when credits.unlimited || credits.has_credits (tui/src/chatwidget/usage_notice.rs): Codex itself does not warn about a window that credits cover. And rate_limit_reached_type is attached only to a refused request, a 429 whose error type is usage_limit_reached (codex-rs/codex-api/src/api_bridge.rs). That distinction carries the whole design: a high window percentage describes consumption; a recorded refusal says a request was actually rejected.
On the Claude side, I read the installed binary. After each response, Claude Code stores the anthropic-ratelimit-unified-overage-disabled-reason header in cachedExtraUsageDisabledReason in its state file (~/.claude.json, or .claude.json in CLAUDE_CONFIG_DIR), or null when the header is absent, and its own checks treat null as usable credits. The values that mean they cannot include out_of_credits and org_level_disabled from Claude Code's own list, and org_spend_cap_reached, which I have seen in the field. I count null as usable only together with hasExtraUsageEnabled: true. That is cached eligibility, not a balance, and I label it that way everywhere it shows.
None of this adds a network request, a credential read or a model call. The harness reads files the two CLIs already write. Anything it cannot read is unknown, and unknown keeps the old behavior.
The decision
The plan level is computed exactly as before: warning at 95%, critical at 98%. Then the credit state decides what that level means.
The reserve is my policy, not a vendor rule, and it is the credit counterpart of stopping a plan window at 95%. Running to the last credit risks a refusal before the checkpoint finishes, which is the exact failure the limit watch exists to prevent. By my arithmetic from OpenAI's published rates (GPT-6 Astra: 250 credits per million input tokens, 25 per million cached, 1,250 per million output), a turn with 200,000 cached input tokens, 20,000 fresh ones and 5,000 output tokens costs about 16 credits. That estimate informed my 1,000-credit reserve: room for several sessions to checkpoint, not a guarantee. Zero and zero is one setting away for anyone who wants to run dry.
Claude cannot have a reserve, because there is no local balance to compare. Its path ends at out_of_credits, a reached cap or a refused turn. I would rather show that gap than estimate a number.
One more rule came out of review: in Codex, the harness clears a refusal only when it sees a newer rate-limit record without the refusal marker. A healthy-looking percentage alone never clears it.
Whose credits, and who may spend them
Availability and permission are different questions, and automation needs both answered before it spends money.
My personal Claude account and my Codex account spend their own credits, including from unattended loops; Codex stops at its reserve. A work account on a Team seat stays plan-only: its credits belong to the organization, and a positive signal on that seat does not change the setting. One setting per provider and one per Claude account decide this, once, so a loop never asks on every iteration and never infers permission from a balance.
The consequence is worth stating plainly. An unattended loop on Codex will now keep launching workers past the weekly window and spend credits until the reserve. That is what I asked for. It is also why the reserve and the status bar exist.
What the status bar says
The Claude Code statusline is a command I own, so it carries the state for both agents. The balances here are example values:
credits ready the plan has room; credits wait in reserve
CREDITS READY 7d 96% the plan is nearly used; credits take over at 100%
ON CREDITS 7d 100% ยท 12.5k left plan window used up; credits reported usable
!! CREDITS LOW 900 left a hold again: wrap up and hand over
credits out spent or capped: the old 95/98% rules apply
codex on credits 12.5k the other agent, shown only when it matters
The yellow labels take the place of the red !! LIMIT marker that a hold shows. ON CREDITS reflects the local window and credit signals; it is not a billing receipt.
Codex's own status line lists five-hour and weekly limits, and its only credit item is described as Enterprise-only. So in Codex the visible surface is a hook systemMessage, sent once per state change: "Usage credits ยท codex: 7d 100% of the plan used, continuing on credits (12,500 credits left)." The agent gets a matching context note, once per state, that says to keep working.
The review that made it safer
I asked Codex for an independent review of the diff before landing it. It came back with eight findings, four marked high, and they were real:
- Order of observations. A resumed old Codex session could overwrite a newer, lower balance with its stale tail and lift a hold. Observations are now ordered by the record's own timestamp, to the sub-second.
- Returning holds. My delivery rule sent each level once per window. Critical, then credits, then critical again would have left the agent holding the last instruction: keep working. Every change of state is delivered now.
- Spend caps. A gateway spend limit sat in the same list as the plan windows, so credits could have waived it. It keeps its own level now.
- Honest wording. The status table said "paying" at 99%, while the plan was still paying. It says "credits ready" until the window is used up.
It also challenged the reserve. I kept one, lowered it, and wrote down why.
The fixes went back to the same reviewer, which found six problems in them, two marked high: concurrent hooks could still interleave an older record after a newer one, and the rule that carried credits over a record without them never actually expired. A third round found one more race: two hooks of the same session could deliver a stale "keep working" note after a newer hold. Snapshot writes and advisory deliveries now each happen under a lock, and the credits note itself says that a LIMIT WATCH overrides it whenever it arrives.
A fourth round flagged a test that never reached the branch it claimed to cover, and following it up exposed an older bug: the check that recognizes a stale refusal in a resumed session, in the harness since September, had never matched Codex's real refusal names. Every finding has a regression test, and the race test fails with the old ordering.
What I verified, and what I did not
- 764 automated tests pass, including one for every branch above and one for each review finding.
- Live: the account running the session crossed 95% of its weekly window, the hook delivered the credits note instead of a wrap-up, and the session kept working.
- Live: a real Codex request reported
has_credits: trueand a balance equal to the account's usage page. A work seat without usable credits kept its 95% warning exactly as before. - Replay in a sandbox: real records with the usage set to 100% and test balances. Codex on credits; at a test balance of 900 credits, warning; at 400, critical; the personal Claude account on credits; the work seat critical.
- Not verified: a request actually billed to credits past 100%. Neither window was at 100% that day, and burning a week's allowance just to watch it happen is the wrong kind of test. The vendors document the behavior; my evidence shows the harness reads the signals and makes the intended decision.
The weak spot is the source of the Claude signal. Those two fields are undocumented internals of one Claude Code version, read from its binary. If those fields go missing or unreadable, the state turns unknown and the harness falls back to the old rule. If a new version simply stopped updating them, the last value could linger, so I re-check them after every Claude Code upgrade.
What I would ask the vendors for
A plan percentage is an early warning. Whether credits can pay decides what the warning means, and today that answer is buried in a transcript on one side and in a cache file on the other. Put the credit state, and the balance where there is one, into the documented hook and statusline payloads. Harnesses could then read it without relying on undocumented fields.