Batuta Routing Table
SkillDev toolsLets your agent pick the cheapest capable AI model for each task based on its complexity.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Batuta Routing Table skill
About this capability
Default cost/complexity routing table for the batuta conductor. Read at bootstrap as a starting point, validated against the live provider catalog, then stored as the per-workspace loop configuration; the stored workspace override is authoritative afterwards.
What this skill tells your AI
The instructions your AI receives, as published by compozy/compozy in catalog/packages/batuta/resources/skills/batuta-routing/SKILL.md and read by ahel’s review.
Batuta's core opinion: route every task to the cheapest executor that can
handle it. Lanes use the complexity vocabulary that cy-create-tasks
writes into task frontmatter (low, medium, high, critical) — the
same vocabulary runtime_rules[].match.complexity matches on.
Lane semantics (the durable opinion)
| Lane | Intent | Selection rule |
|---|---|---|
low | Contained change, well-trodden paths | Cheapest coding-capable model in the catalog |
medium | New interfaces, moderate coordination | Mid-tier coding model; raise reasoning before raising cost |
high | New subsystem, heavy reasoning | Strong coding model, premium tier acceptable |
critical | Cross-cutting, high regression risk | The operator's most trusted frontier model |
How batuta derives the concrete table (never copy an example)
compozy__provider_models_list(with costs) is the ONLY source of concrete provider/model IDs — it reflects the CLIs actually installed and the models actually discovered on this machine. A provider absent from the catalog is not installed; never route to it.- Map each lane's selection rule onto the catalog using the cost fields
(
input_per_million/output_per_million) as evidence. - Model enablement is account-side and invisible to the daemon — present the derived table (with costs) to the operator for confirmation before storing; ask what their accounts enable when in doubt.
Example only — derived on one machine on 2026-08-11, DO NOT reuse
On that machine the derivation produced: low → codex/gpt-5.6-luna,
medium → codex/gpt-5.6-terra@high, high → codex/gpt-5.6-sol,
critical → claude/claude-opus-4-8. Your catalog will differ; derive, do
not copy.
Canonical rule shape
This is the exact JSON SHAPE batuta writes with compozy__loop_configure
(stored per-workspace override for implement-tasks) after deriving the
values from the catalog — the model/provider strings below are the same
dated example as above and MUST be replaced by the derived ones. The stored
override is what run-loop children resolve at execution — batuta never
sends per-run rules on dispatch, because per-run rules freeze into the run
and are not inherited by run-loop children anyway. Rule matching
precedence inside the stored layer: id > type > complexity.
[
{ "match": { "complexity": "low" }, "runtime": { "provider": "codex", "model": "gpt-5.6-luna" } },
{
"match": { "complexity": "medium" },
"runtime": { "provider": "codex", "model": "gpt-5.6-terra", "reasoning": "high" }
},
{ "match": { "complexity": "high" }, "runtime": { "provider": "codex", "model": "gpt-5.6-sol" } },
{
"match": { "complexity": "critical" },
"runtime": { "provider": "claude", "model": "claude-opus-4-8" }
}
]
Provider quirks
- Some providers multiplex upstreams and require the model field to carry a
prefix — e.g.
opencodeonly bindsopencode/kimi-k2.5, never barekimi-k2.5. The catalog's exactmodel_idis authoritative; copy it verbatim into the rule. - A model can exist in the catalog and still be disabled for the operator's account at the provider (invisible to the daemon). When a lane fails its bind with zero tokens, ask the operator what their account enables.
Escalation and reclassification
- Repeated failure in a lane: write a surgical
idrule one lane up into the STORED override (compozy__loop_configureonimplement-tasks, e.g.{"match":{"id":"task_NN"},"runtime":{...}}prepended to the rules), then re-dispatchbatuta-deliver.idbeatscomplexity; remove the rule after the task lands. - Operator reclassification in conversation ("use luna for this one")
becomes the same stored
idrule before the next dispatch. - The daemon persists
resolved_runtimewith per-field provenance on every generation — routing decisions are auditable viacompozy__loop_status, never narrated.
Signals
- GitHub stars
- 3k
- Forks
- 177
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
batuta-routing- Source
- github.com/compozy/compozy