Batuta Routing Table

SkillDev tools

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

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)

LaneIntentSelection rule
lowContained change, well-trodden pathsCheapest coding-capable model in the catalog
mediumNew interfaces, moderate coordinationMid-tier coding model; raise reasoning before raising cost
highNew subsystem, heavy reasoningStrong coding model, premium tier acceptable
criticalCross-cutting, high regression riskThe operator's most trusted frontier model

How batuta derives the concrete table (never copy an example)

  1. 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.
  2. Map each lane's selection rule onto the catalog using the cost fields (input_per_million / output_per_million) as evidence.
  3. 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. opencode only binds opencode/kimi-k2.5, never bare kimi-k2.5. The catalog's exact model_id is 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 id rule one lane up into the STORED override (compozy__loop_configure on implement-tasks, e.g. {"match":{"id":"task_NN"},"runtime":{...}} prepended to the rules), then re-dispatch batuta-deliver. id beats complexity; remove the rule after the task lands.
  • Operator reclassification in conversation ("use luna for this one") becomes the same stored id rule before the next dispatch.
  • The daemon persists resolved_runtime with per-field provenance on every generation — routing decisions are auditable via compozy__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