Routing
How spawn picks the right model and provider for each task using a tiny local classifier.
spawn decides which model answers. When a task reaches the gateway, the router reads its description, classifies it once, and maps that classification to a concrete provider and model, all locally, before the request is forwarded. Routing happens once per task, not per turn, so there's no thrashing between models mid-conversation.
The classifier
The router is spawn-router (the tiny coding router): a text-only DeBERTa multi-head classifier. From a single task description it predicts several dimensions at once:
- task_type: bugfix, feature, refactor, test, design, docs, migration, exploration.
- complexity: easy, medium, or hard, plus 0–1 sub-dimensions (reasoning depth, scope breadth, domain knowledge, spec completeness).
- risk: low, medium, or high, plus 0–1 sub-dimensions (security surface, data sensitivity, production exposure, reversal cost).
It runs locally and fast. Production inference uses a quantized int8 ONNX model at roughly ~10–50ms on CPU, with a PyTorch subprocess as fallback. No provider keys are needed to classify. Because it classifies once per task rather than on every turn, the chosen model stays stable for the life of the task.
From prediction to decision
The raw prediction is mapped to a decision through a tier ladder plus ordered rules, which is config you own. Each provider exposes three tiers (small, mid, large), and every tier resolves to a target atom: a model id, optionally with a pinned reasoning effort ("gpt-5.5, low").
Default ladder:
| Tier | Anthropic | OpenAI |
|---|---|---|
| small | claude-haiku-4-5 | gpt-5.4-mini |
| mid | claude-sonnet-4-6 | gpt-5.5, low |
| large | claude-opus-4-8 | gpt-5.5, high |
On the OpenAI ladder, mid and large are the same base model at two different pinned efforts: the effort dial is the real lever there. These tier maps live in your config, so you decide exactly which target each tier points at.
Rules layer on top: ordered { when, use } overrides on the classifier's dimensions (task_type, complexity, risk). The first matching rule wins; use is either a tier name (safety bumps still apply) or an explicit target atom (final). With no rules configured, the ladder alone decides. See Configuration for the rule shape and examples like preferring a strong reasoning model for design tasks, or routing easy + low risk work to small.
Effort and confidence
Two dials refine the raw choice, independent of which model is picked:
- Effort routing. For targets that don't pin their own effort, reasoning effort tracks complexity by default: easy tasks run at
loweffort (the cheap win), hard athigh. Effort levels go up toxhighandmaxfor rules or tiers that want to pin one explicitly. Risk can bump effort up a safe step on top of this. - Confidence floor. When the classifier's overall confidence falls below a configured floor (
0.55by default), spawn fails safe and routes up a tier rather than risk under-powering a task. High risk similarly bumps both tier and effort by default (bumpOnHighRisk), so risky work is never silently nerfed.
How it runs
The node engine (the default) runs inference in-process via onnxruntime-node, downloading pinned ONNX weights from Hugging Face on first use. Classification is cached per task so repeated turns of the same task don't re-run inference. The router is consulted once at task kickoff; the decision is then locked for that task. A "python" engine is also available, shelling out to a spawn-router checkout for the PyTorch model; see Configuration.
For wire formats, credential forwarding, and auth modes, see the gateway.