Retitled and rewritten. The first draft led with an implementation and a
cute codename. Both were wrong. What matters is the four capabilities below —
and the one that makes the rest age well is that nothing about models can be
hardcoded. Models ship faster than any table in any repo survives.
Summary
Hive's governor decides when to kick an agent. Nothing decides which
backend and model it should be pointed at, or whether that backend can
serve right now. So a hive with a dry DeepSeek balance, a capped Codex
account, or a lapsed Claude session keeps kicking agents into backends that
cannot answer. They fail quietly and look healthy.
We have run an external component covering this for ~6 weeks. This RFC is not a
request to merge it — it is 1400 lines of bash against hive's HTTP API. It is a
report on the four capabilities the problem actually decomposes into, and
evidence for each.
The four capabilities
1. Usage probe — normalized headroom per backend
"How much is left" has a different answer shape per provider, and none is a
token count:
| Backend |
Where headroom lives |
Shape |
| claude |
GET /api/oauth/usage with the OAuth token |
percent + resets_at, three limits |
| codex |
/status typed into a live TUI |
"NN% left, resets HH:MM on DD Mon" |
| agy |
agy --print /usage |
two pools × weekly/5-hour, percent remaining |
| deepseek |
GET /user/balance |
dollars of prepaid credit |
A model-scoped cap is not provider exhaustion. Anthropic returns session,
weekly_all, and weekly_scoped — the last carries scope.model and caps one
model class. Taking the max across all three parked our entire fleet while
claude-sonnet-5 and claude-opus-5 both answered normally. Headroom must
come from unscoped limits, with scoped caps reported separately so
placement can avoid that model rather than the whole provider.
Probes that read a provider through a consumer of it will deadlock. This
bit us four separate times: google required an unpaused agent though its probe
is headless; openai skipped paused agents though a paused agent's CLI is still
running; contributors parked every worker and then could not measure the
provider needed to restore them; an emptied credential file reported "unknown"
instead of "exhausted". If hive builds this, that is the invariant to design
against.
2. Model inventory — what a backend can run right now
This is the capability we have least of, and the one that decides whether the
rest ages well. Today we hardcode model IDs in a table. That table is wrong the
week a model ships.
The backends are wildly inconsistent, which is exactly why it belongs behind
one interface:
| Backend |
Enumerable? |
How |
| claude |
yes, structured |
GET /v1/models with the OAuth token — returns id + display_name |
| deepseek |
yes, structured |
OpenAI-compatible GET /models |
| agy |
prose only |
agy --print /models renders a human table, not a list |
| codex |
no |
--model takes a string; nothing enumerates |
Verified live on our fleet — Anthropic returns claude-fable-5-1,
claude-opus-5, claude-sonnet-5, claude-opus-4-8, … and DeepSeek returns
deepseek-v4-flash, deepseek-v4-pro, deepseek-v4-flash-vision-exp.
Two of four can answer precisely. Hive is better placed than any operator to
normalize that and to push the other two upstream.
A related, cheap win: validate a model id against the inventory before
setting it. A model that a backend cannot run is a hard startup failure that
currently surfaces as a dead pane.
3. Tiering — ranked by benchmark, refreshed, never hardcoded
Placement needs to know which models are interchangeable in competence, so an
agent can move sideways rather than being silently downgraded. That ranking
cannot live in a repo:
- Our table has 14 hardcoded rungs. Every one is a guess with a shelf life.
- Our refresher (pull rankings from a benchmark API, rebuild the table) has
never successfully run — it needs an API key nobody set, and it fails
quietly, so the fleet has always used the stale built-in table.
Two things we got wrong that are worth encoding as warnings:
- Bigger is not better for agent work. DeepSeek's own release notes say not
to move to V4-Pro for agent tasks on price grounds — Flash leads the agent
suites. A naive "rank by size/price" tiering inverts this.
- Benchmarks measure agent+model pairs, not models. The same model swings
~8 points between harnesses. A ranking that ignores the harness does not
predict the fleet running these CLIs.
So: auto-tier from an external agentic benchmark, refresh on a schedule, and
make the source pluggable — but treat the result as a proposal, not an
auto-adopted change. Which leads to:
4. Operator-editable policy
Ours is a bash heredoc. That is the wrong shape for something an operator must
tune. The decisions a human needs to make:
- which tier each agent requires (a competence floor, not a cost knob)
- provider preference order — ours puts free before metered, which is not
universal
- per-provider exhaustion thresholds, since a shared subscription and a private
one deserve different caution
- whether a newly-benchmarked model is adopted automatically or offered
And one behaviour worth stating explicitly, because it is counter-intuitive:
rotate on exhaustion, never on preference. If a provider is usable and the
current rung is legitimate for the agent's tier, leave it alone even when
something better-ranked exists — otherwise the fleet churns every tick chasing
whichever pool is momentarily cheapest.
What we would upstream first
- Document the atomic
PUT /api/config/agent/{name}/models as the
preferred path over switch then model. Two calls with no transaction
leave codex --model claude-opus-5 — unlaunchable — when the second times
out. It is also ~2.4s versus ~45s. Pure docs; removes a live footgun.
- A model-inventory interface a backend can implement, plus validation of
a model id before it is set. Two of four backends can already answer.
- A headroom interface, unscoped-vs-scoped separated, with the
consumer-deadlock invariant written down.
- Exhaustion as an agent condition, alongside
Authenticated /
Producing. Hive detects and reports agent state well; it has nowhere to
say "the backend is out of quota".
- Optional placement engine, off by default, consuming 2–4.
1–4 are useful even if nobody builds 5. We are not asking hive to adopt our
tier rankings — those are ours, and they change.
Environment
Two spokes + self-hosted hub on Talos, ACMM L5/L6, backends claude / agy /
codex / pi across Claude Max, Google AI Pro, ChatGPT Plus and DeepSeek prepaid.
Related: #5632, #5633, #5661, #5690, #5691.
Summary
Hive's governor decides when to kick an agent. Nothing decides which
backend and model it should be pointed at, or whether that backend can
serve right now. So a hive with a dry DeepSeek balance, a capped Codex
account, or a lapsed Claude session keeps kicking agents into backends that
cannot answer. They fail quietly and look healthy.
We have run an external component covering this for ~6 weeks. This RFC is not a
request to merge it — it is 1400 lines of bash against hive's HTTP API. It is a
report on the four capabilities the problem actually decomposes into, and
evidence for each.
The four capabilities
1. Usage probe — normalized headroom per backend
"How much is left" has a different answer shape per provider, and none is a
token count:
GET /api/oauth/usagewith the OAuth tokenresets_at, three limits/statustyped into a live TUIagy --print /usageGET /user/balanceA model-scoped cap is not provider exhaustion. Anthropic returns
session,weekly_all, andweekly_scoped— the last carriesscope.modeland caps onemodel class. Taking the max across all three parked our entire fleet while
claude-sonnet-5andclaude-opus-5both answered normally. Headroom mustcome from unscoped limits, with scoped caps reported separately so
placement can avoid that model rather than the whole provider.
Probes that read a provider through a consumer of it will deadlock. This
bit us four separate times: google required an unpaused agent though its probe
is headless; openai skipped paused agents though a paused agent's CLI is still
running; contributors parked every worker and then could not measure the
provider needed to restore them; an emptied credential file reported "unknown"
instead of "exhausted". If hive builds this, that is the invariant to design
against.
2. Model inventory — what a backend can run right now
This is the capability we have least of, and the one that decides whether the
rest ages well. Today we hardcode model IDs in a table. That table is wrong the
week a model ships.
The backends are wildly inconsistent, which is exactly why it belongs behind
one interface:
GET /v1/modelswith the OAuth token — returnsid+display_nameGET /modelsagy --print /modelsrenders a human table, not a list--modeltakes a string; nothing enumeratesVerified live on our fleet — Anthropic returns
claude-fable-5-1,claude-opus-5,claude-sonnet-5,claude-opus-4-8, … and DeepSeek returnsdeepseek-v4-flash,deepseek-v4-pro,deepseek-v4-flash-vision-exp.Two of four can answer precisely. Hive is better placed than any operator to
normalize that and to push the other two upstream.
A related, cheap win: validate a model id against the inventory before
setting it. A model that a backend cannot run is a hard startup failure that
currently surfaces as a dead pane.
3. Tiering — ranked by benchmark, refreshed, never hardcoded
Placement needs to know which models are interchangeable in competence, so an
agent can move sideways rather than being silently downgraded. That ranking
cannot live in a repo:
never successfully run — it needs an API key nobody set, and it fails
quietly, so the fleet has always used the stale built-in table.
Two things we got wrong that are worth encoding as warnings:
to move to V4-Pro for agent tasks on price grounds — Flash leads the agent
suites. A naive "rank by size/price" tiering inverts this.
~8 points between harnesses. A ranking that ignores the harness does not
predict the fleet running these CLIs.
So: auto-tier from an external agentic benchmark, refresh on a schedule, and
make the source pluggable — but treat the result as a proposal, not an
auto-adopted change. Which leads to:
4. Operator-editable policy
Ours is a bash heredoc. That is the wrong shape for something an operator must
tune. The decisions a human needs to make:
universal
one deserve different caution
And one behaviour worth stating explicitly, because it is counter-intuitive:
rotate on exhaustion, never on preference. If a provider is usable and the
current rung is legitimate for the agent's tier, leave it alone even when
something better-ranked exists — otherwise the fleet churns every tick chasing
whichever pool is momentarily cheapest.
What we would upstream first
PUT /api/config/agent/{name}/modelsas thepreferred path over
switchthenmodel. Two calls with no transactionleave
codex --model claude-opus-5— unlaunchable — when the second timesout. It is also ~2.4s versus ~45s. Pure docs; removes a live footgun.
a model id before it is set. Two of four backends can already answer.
consumer-deadlock invariant written down.
Authenticated/Producing. Hive detects and reports agent state well; it has nowhere tosay "the backend is out of quota".
1–4 are useful even if nobody builds 5. We are not asking hive to adopt our
tier rankings — those are ours, and they change.
Environment
Two spokes + self-hosted hub on Talos, ACMM L5/L6, backends claude / agy /
codex / pi across Claude Max, Google AI Pro, ChatGPT Plus and DeepSeek prepaid.
Related: #5632, #5633, #5661, #5690, #5691.