3 AIs Hit the Limit

Codex mid-task, Claude mid-loop, Grok on every review, all inside one day. Two limits came with a percentage and a reset time, one with a status code. The silence around all three was the bug.

Two hands with pink nails on a split ergonomic keyboard, from above
Hands on the keys.
0:00
/0:36
One day, three usage screens.
πŸ“Œ
TL;DR

All three of my coding assistants ran out of allowance inside one day, at different moments, in the middle of work.

Two of the three programs already report how close they are to the wall. Nothing turned that number into a handover.

Now a small hook watches the percentage and tells the session to wrap up cleanly and leave a note for the next assistant.

The next assistant is opened with three words: β€œtakeover from claude” (or codex, or grok). It reads the note and continues, unless its own meter is full too.

When all three meters are full at once, nothing starts and the note waits. Until a meter resets, the time goes to silence or to work that needs no assistant; nothing is bought on top of the plans, and my own head stays in practice.

Grok Build exposes no percentage to hooks, the thresholds are a first guess, and I have no data yet on whether this saves time.

One day, three walls

Codex reached its weekly allowance this morning in the middle of a long document task. There was no handover, so I opened Claude Code and rebuilt the state by hand from the chat: where things stood, which files were touched, what was verified and what was not.

A few hours earlier, before dawn, a headless loop running Claude Code hit a session limit. The rejection read β€œYou’ve hit your session limit Β· resets 6:10am”. The loop’s own detector knew β€œhit your limit” and β€œhit your usage limit” and nothing else, so sixteen iterations ran to nothing before I looked at the log.

Grok Build answered every review request later the same day with HTTP 402: β€œusage balance exhausted”.

Three programs, three wordings for the same event. In two of the three programs the number was there and went nowhere; in the third there was no number to have. Claude Code on Fable 5.1 at max, Codex on GPT-6 Astra at ultra and Grok Build on Grok 4.6 at xhigh share one desk and one policy, described in I Love Trios. This post is about what happens when the meters fill up.

A limit is not an outage

An outage is unscheduled and nobody can tell you when it ends. A usage limit is the opposite. It has a reset time, the program often knows the percentage before the wall, and on this desk two other assistants may have capacity at that exact moment. All of that was true for Claude Code and Codex. Grok Build, which reports nothing before a refusal, is the exception and gets its own row below.

So the failure was never the limit. It was that nothing turned a known number, or a refusal, into a handover. The percentage sat in a statusline. It sat in a transcript field. And the work still stopped as if the power had gone out.

A limit belongs to the program and its account, not to the model. Fable 5.1 does not get worse at 95 percent; at 100 percent, Claude Code stops accepting turns until the window resets. So the fix is not more allowance and not retry-until-it-works. It is a session that sees the wall coming, finishes cleanly, writes down where it stopped, and names its successor.

What each program will tell you

ProgramWhere the signal livesWhat it carriesWhen visible
Claude CodeStatusline JSON after every response; a StopFailure hook event when a turn is refusedFive-hour and seven-day windows with used percentage and reset time; the error type of a refusal (rate limit, billing, account on hold)Before and at the limit
CodexThe session’s own transcript (JSONL); the hook receives its path and reads the tailPrimary and secondary windows with used percent, reset time, and which one was reachedBefore and at the limit
Grok BuildOnly the failure text of a refused turn: HTTP 402, β€œusage balance exhausted”A refusal only: no percentage, no reset time. Nothing about allowance reaches hooks or the statusline; the /usage screen is a view for a personAfter a refusal only

That is the whole basis of the design: read what is native, store it, act on it. No model call is involved anywhere in the signal path.

One snapshot per provider

The toolkit I already run as native hooks in all three programs is a small Python tool; it also generates each program’s global instructions and skills from one shared policy. It now keeps one private JSON file per provider: the windows it saw, when each resets, whether a refusal is still live, and a level: ok, warning, critical or rejected. Owner-only permissions. Nothing leaves the machine.

Three rules keep the snapshot honest. A window drops out when its reset time passes, so yesterday’s number cannot scare today’s session. A refusal clears when the exhausted window resets; when no reset is known, which is Grok’s case, the refusal is held for six hours and then shown as stale, not as ok. And a later, healthier-looking observation never wipes a live refusal, because a statusline at 30 percent does not mean the account stopped refusing.

Thresholds: short windows (Claude Code’s five-hour window and Codex’s primary, also five hours) warn at 85 percent and turn critical at 95. Weekly windows warn at 97 and turn critical at 99.5, because a week at 86 percent with three days left is information, not a reason to wrap up. All four are configurable, and all four are guesses until I have seen how often they fire.

LIMIT WATCH

When a level is reached, the hook injects one LIMIT WATCH block per session and level, through whichever native event each program supports: Claude Code on UserPromptSubmit, SessionStart and PostToolUse; Codex on SessionStart and PostToolUse; Grok Build on its next PreToolUse. Once per session and level means a session hears β€œwarning”, β€œcritical” and β€œrejected” once each, not on every tool call, and hears it again after a compaction or a resume, when the earlier text is gone from its context. The Claude Code statusline shows it too, as β€œ!! LIMIT WARNING 90%”.

The harness is advisory by design. It never blocks a prompt or a tool call. It never asks the model to continue after a stop. It never retries a provider. And it stays silent inside subagents, which have no business wrapping up the parent’s work. Those four rules are the part I care about most: a limit watcher that blocks calls or auto-retries is a second source of surprises, and the point of this was to remove one.

Wrap up, do not push through

A warning is useless without a fixed answer to β€œso what now”. That answer is one contract file, the same for all three programs, generated into a native β€œtakeover” skill in each. The order:

  1. Stop starting new work. Warning: reach a safe stopping point after the current step. Critical or rejected: finish only the step in flight, abort what cannot finish safely, and say so.
  2. Leave every repository consistent. Commit and push under the normal rules: no skipped hooks, no force push, no STOP marker removed to make a tree look clean.
  3. Update the ledgers the work already keeps, so the lasting state lives in the repository, not in the chat.
  4. Write the takeover record (the command below).
  5. Tell me in one line: which limit, when it resets, and β€œopen another agent and say: takeover from claude”. Then end the turn. Nobody waits for the window inside the same session.
ai-workspace takeover write --provider claude --root "$PWD" --reason "five_hour 96%" --body-file - <<'END'
## Task
## Done (verified)
## Next, in order
## Loops and automation
## Open risks
END

The toolkit records what it can check on its own: branch and head, dirty and unpushed counts, and the provider’s limit snapshot. It also records the resume pointers under the project: loop directories with their STOP markers, the saved runtime lane, and the last limit or failover lines of their logs. The session writes the body: the task, what is done with evidence, next steps in order, which loops exist and how to relaunch them, open risks.

That split is the point. The machine records the facts it can check; the model records the judgment. When I rebuilt the Codex task from the chat, both halves were missing.

Takeover from claude

The other side of the contract is just as fixed. A session told β€œtakeover from claude”, in any of the three programs, starts with two commands:

ai-workspace takeover status
ai-workspace takeover show --root "$PWD"

The first says which providers are limited as of the last observation, including the one you are running in. The second prints the latest record for the project plus a fresh scan of its loop pointers, because loop state can move after a record is written. Then the session reads the record and every ledger it names, in full; pulls; checks that head, dirty count and unpushed count match the record; and inspects any difference before touching anything.

The record is a pointer and a to-do list. It is not authority. It cannot grant a permission the previous session did not have, and β€œthe last session said so” is not a reason to do anything the repository’s rules forbid. The successor re-derives the work from those rules.

Loops get extra rules. A loop I stopped stays stopped. A loop paused by a limit is relaunched only on the successor’s own provider lane, with the loop’s documented command, and only when the loop’s policy allows it. When the work is picked up, the record is marked consumed with ai-workspace takeover done. If the successor’s own provider is also limited, it does not start. It says which providers remain usable and stops there. Three limited assistants is a real state, and the right answer to it is a sentence, not an attempt.

When all three are full

All three meters can be full at once, usually late in the week, when the weekly allowances run out. That is expected. The successor does not start, the record waits, and the time until the first meter resets (hours for a session window, days for a weekly one) goes to one of two things: silence, or the kind of work that needs no assistant at all. Reading. Thinking a design through on paper. Writing the spec the assistants will get when the meters reset.

I accept that for two reasons. The first is money. The meters fill because every task runs the three models at their highest reasoning settings; that is the output I pay for. I could buy extra usage and keep going; I do not. A full meter means the plan was used to the end and nothing was bought on top of it; waiting for a reset costs nothing. The second is my own head. When there is always a model to lean on, I get out of practice at the parts that are mine to do: holding a whole problem at once, deciding what not to build, noticing what is missing. Days without the assistants are when those parts get done.

The loops and the console

The loop that burned sixteen iterations is not the ticket-shipping loop. Its detector learned the new phrase the same morning; it now switches lane on a classified limit, reports it into the same snapshot and writes a takeover record when every provider is exhausted. The ticket-shipping loop in my cockpit was already further along: it fails over across provider lanes after an hour blocked and fails back on recovery. What it did not cover was the interactive console session that watches it, which is an ordinary session and can hit a limit like any other. That is what the new harness covers there: the console hands over instead of dying with the batch state in its head.

The one edit I did not make is inside that loop’s template, where the lane-down path would report into the same snapshot. A batch is live, and templates are never edited under a live batch, so that edit waits.

Parity and the second machine

Each program has its own hook events and its own native skill, so drift is the default state. The toolkit’s check verifies the hook registrations in all three programs, the generated skills and the contract together, in one run. Eighteen tests cover the new module, seven of them added after an independent review of the first version found real gaps: a refusal that could age out instantly, a status line that could wipe it, a resumed transcript that could resurrect it. The suite is at 306, all passing.

A second machine gets the same files through an allowlisted packet exported from a reviewed dotfiles revision. The rule I wrote down today for that path: replicate, never reinvent. A harness rebuilt from memory on a second machine is a different harness, and a different harness has different gaps.

What is not known

Some of this is scaffolding, and I would rather list the holes myself.

  • Grok Build exposes no allowance percentage to hooks or the statusline, so a Grok session gets no warning before a refusal and will get none until the program exposes one. After a refusal, the record mostly serves the other two programs and the next takeover check.
  • The thresholds are a first guess. I have no data on how often the warning fires, how often it fires too early, or how much time a takeover saves against the manual reconstruction I did from the Codex chat. I am not claiming savings; I am claiming a procedure that exists and is tested.
  • A session that ignores the advisory simply runs into the native limit, the same as before. The harness never blocks, so it cannot force a wrap-up. In that case there is no record, and the state has to be rebuilt from the ledgers and the chat, as it was this morning.
  • Records are machine-local. Nothing is synced between machines. A limit hit on one machine is invisible on the other.
  • The Codex transcript field and Grok’s silence are dated observations from 10 September 2026. Either may change with the next release, and the parity check was not built to notice a renamed transcript field; a silent format change would most likely show up as a warning that never fires.

The three screens in the reel will happen again. The difference next time is supposed to be small and boring: the session at 95 percent says so, writes down where it stands, and the next one starts from a record instead of a chat.

πŸ’¬
Working with a team that wants to adopt AI-native workflows at scale? I help engineering teams build this capability β€” workflow design, knowledge architecture, team training, and embedded engineering. β†’ AI-Native Engineering Consulting
πŸ“–
Related Reading

I Love Trios β€” the three programs, one desk, one policy.

Grok Joins the Party β€” how Grok Build got its seat.

Graph Engineering: Evidence In, Guesswork Out β€” the same toolkit deciding what may run next.

Astra Γ— Fable: Full Power, Less Waste β€” maximum settings, audited.