Detecting and tracing GO exhaustion
Detecting and tracing GO exhaustion
Young's ruling: "GO exhausted will rolled over to ZEN — how to to trace and track when GO exhausted from Opencode."
This is the answer, and it is measured. V2 already had a working detector —
bin/model_probe_pool.py — and it is in the archived tree, not ported. Reading
its live output answers the question directly.
1. The short answer
Opencode tells us, and the signal is a 429 carrying the string
provider.quota. Not a balance, not a percentage — a rate-limit event on a
specific model. V2's prober classifies it and trips a breaker:
ALIVE -> record ok=True (clears a cooldown, makes it routable)
429/quota -> record ok=False (trips the breaker, escalation moves on)
other -> recorded too, but NOT a retryable provider fault
So GO exhaustion is not a dollar total — it is a per-model breaker, and the model that exhausts is the one that 429s. That is a sharper signal than a ledger balance, because it names which model died and when.
2. The measured evidence — from state/model_health.json
Read from V2's live breaker state. The numbers answer the design question:
| Model | ok | fail | What it tells us |
|---|---|---|---|
opencode-go/longcat-2.5-preview-free | 769 | 0 | the free model carried the fleet |
opencode-go/qwen3.8-flash | 50 | 0 | the paid model took real traffic |
opencode-go/gpt-6-luna | 43 | 0 | |
opencode-go/step-5-preview-free | 1 | 0 | |
opencode-zen/longcat-2.5-preview-free | 0 | 2 | "provider.quota "Rate limit exceeded." 429" |
opencode/mimo-v2.6-flash-free | 2 | 23 | "provider.quota: Rate limit exceeded" |
opencode/big-pickle | 0 | 11 | same |
lab-go/deepseek-v4-flash | 0 | 1 | "Model access is disabled" — not a quota event |
Three findings from that table:
a) The 429 string is the exhaustion signal, and it is distinguishable from
other faults. provider.quota = rate-limited. Model access is disabled = an
authorisation fault, which is a different failure and must not be counted as
exhaustion. V2's classifier already separates them.
b) Zen's failure is measured, not theoretical. opencode-zen/longcat failed
twice with a 429 quota error. So your rollover rule has a real trigger — the
GO side 429s, and ZEN is the destination.
c) The counters are cumulative and unlabelled. ok: 769 vs fail: 0 on
longcat — but nothing in that file says which instrument paid, or what the 769
cost. That is the gap my ledger fills: V2's breaker tells us that a model
failed, never what was spent getting there.
3. What Stage 6 must add — the trace you asked for
V2's breaker answers "is this model usable?" It does not answer "when did GO run out, and what did the rollover cost?" Those are my fields, and they are:
| Field | Why |
|---|---|
billing_instrument | none · go_allowance · zen_payg — the rollover becomes visible as a change in this value |
window_state | ok · approaching · blocked — blocked on GO with the reason string is the exhaustion event |
rollover_reason | the verbatim provider string (provider.quota, 429) — never our paraphrase |
cost_state | measured · unknown — an unpriced run never reads as free |
So the trace you asked for is a sequence, and it is already half-built:
GO model returns 429 "provider.quota" ← V2's prober already catches this
│
▼
breaker trips, window_state = blocked ← NEW — my field, with the reason string
│
▼
rollover to ZEN, billing_instrument = zen_payg ← NEW — makes the switch auditable
│
▼
the task that was blocked records why ← Stage 3.3's stuck rule, Min's lane
The critical rule: the rollover is recorded as a change in
billing_instrument, not as a silent retry. A silent retry is how you end up
with V2's 30,721 rows at $0.00000 — the money moved and nothing said so.
4. How to get the actual number from Opencode
⛔ SUPERSEDED — see
mira-opencode-budget-trace.mdThis section's conclusion was wrong. It stated "No Opcode balance API was found". There is one: OpenCode's Budgets API —
GET /api/v1/budgets/membersreturnslimit_micro_cents,spent_micro_cents,exceededandresets_atper member, with aPUTto set andDELETEto clear a custom cap.The correction is recorded in full in
mira-opencode-budget-trace.md. Two things that matter more than the API's existence:
- GO usage within the plan does not count toward a Console budget. Only "Go extra usage paid from credits" does. So
exceeded: falsecan read while we are inside the $10 plan — the API is authoritative about credits, not about plan usage.billing_instrumentmust splitgo_plan_spendfromgo_credit_spend.- A budget-exceeded
429says nothing about which budget.Account budget exceededis a different event fromprovider.quota "Rate limit exceeded"— one blocks the workspace, the other blocks a model. Collapsing them makes the rollover untraceable.
What remains true here:
| Approach | Status |
|---|---|
opencode stats --cost --models | proven in V2's evidence. costing-decision-2026-10-07.md measured $10.45 in one day and $10.34 on one model from exactly this command. So Opencode does expose a cost breakdown |
model_health.json breaker state | proven — it is on disk and I read it |
| A programmatic GO-remaining API | exists — the Budgets API. See the note above for what it covers and what it does not |
⚠️ And the caution V2's own data carries: every terminal call in my session
has failed on a stuck cwd, so I could not re-run opencode stats to confirm it
still works. V2 measured it on 2026-10-07 — three days old, and command-surface
drift is real.
5. What this means for the rollover rule you set
Your ruling — GO exhausts, roll over to ZEN — is sound and now traceable. Two things it adds to my stage:
- The rollover needs a
rollover_reasonthat is the provider's verbatim string. A paraphrase loses the distinction between429 quotaandModel access is disabled— and only the first is exhaustion. - ZEN's 429 rate is measured worse than GO's.
opencode-zen/longcatfailed 2/2;opencode-go/longcatsucceeded 769 times. So ZEN as a rollover destination currently carries a higher failure rate — worth knowing before the rollover is load-bearing, and it should be re-measured at Stage 6 time rather than inherited from this file.
And one thing the ruling does not answer, which I am flagging rather than assuming: when GO rolls to ZEN, does the task that triggered the rollover get re-run, or does it complete on ZEN? That is a Stage 3 task-contract question — Min's lane — and it changes whether the rollover is a retry or a continuation.
6. Not established
- I could not re-run
opencode stats— everyterminalcall this session failed on a stuck cwd. The command is proven in V2's record, not by me. - No Opencode balance API was found. The design assumes there is none.
model_health.jsonis a V2 artefact. Whether V3 keeps the same breaker shape is a Stage 6 design choice, not an inheritance.- Zen's failure rate is a snapshot, three days old, and model health drifts.
GO's $60 is hard — and that changes the rollover rule
Young, 2026-10-10: "GO's $60 hard or soft? — Check the allowance list for GO" — with the usage-limits rules and the per-model monthly table.
liteagents/moyai — the LiteLLM A2A Agent Gateway
Read first: 01-research/liteagents-and-moyai-study.md (Min). It is the deeper study — he read the skills, memory and verification docs and got Moyai's actual skills_search / skills_load implementation. This file is the delta from my lane only, not a second opinion on the whole.

