Young's Mind
03 Decisions

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."

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:

ModelokfailWhat it tells us
opencode-go/longcat-2.5-preview-free7690the free model carried the fleet
opencode-go/qwen3.8-flash500the paid model took real traffic
opencode-go/gpt-6-luna430
opencode-go/step-5-preview-free10
opencode-zen/longcat-2.5-preview-free02"provider.quota "Rate limit exceeded." 429"
opencode/mimo-v2.6-flash-free223"provider.quota: Rate limit exceeded"
opencode/big-pickle011same
lab-go/deepseek-v4-flash01"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:

FieldWhy
billing_instrumentnone · go_allowance · zen_payg — the rollover becomes visible as a change in this value
window_stateok · approaching · blocked — blocked on GO with the reason string is the exhaustion event
rollover_reasonthe verbatim provider string (provider.quota, 429) — never our paraphrase
cost_statemeasured · 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.md

This section's conclusion was wrong. It stated "No Opcode balance API was found". There is one: OpenCode's Budgets API — GET /api/v1/budgets/members returns limit_micro_cents, spent_micro_cents, exceeded and resets_at per member, with a PUT to set and DELETE to 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:

  1. GO usage within the plan does not count toward a Console budget. Only "Go extra usage paid from credits" does. So exceeded: false can read while we are inside the $10 plan — the API is authoritative about credits, not about plan usage. billing_instrument must split go_plan_spend from go_credit_spend.
  2. A budget-exceeded 429 says nothing about which budget. Account budget exceeded is a different event from provider.quota "Rate limit exceeded" — one blocks the workspace, the other blocks a model. Collapsing them makes the rollover untraceable.

What remains true here:

ApproachStatus
opencode stats --cost --modelsproven 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 stateproven — it is on disk and I read it
A programmatic GO-remaining APIexists — 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:

  1. The rollover needs a rollover_reason that is the provider's verbatim string. A paraphrase loses the distinction between 429 quota and Model access is disabled — and only the first is exhaustion.
  2. ZEN's 429 rate is measured worse than GO's. opencode-zen/longcat failed 2/2; opencode-go/longcat succeeded 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 — every terminal call 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.json is 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.
Copyright © 2026