Published Aug 18, 2026

A Roster Is Not a Router: Building the Brain That Picks Its Own Tools
Last week I wrote about NeMoCo, a self-supervised system that learns to categorize neural structures the way brains categorize the world — from raw, unlabeled experience, without being told what the categories are. I kept coming back to the same line: labels are a shortcut, structure is what matters.
This week I built the other half of that idea, in the domain I actually work in. I run a personal AI assistant — Aura — that manages memory, routes tasks, and tries to be genuinely useful across sessions. It runs on a fleet of models: Claude for production coding, a handful of cloud models for research and design, and a local model that burns nothing. The problem is the same one NeMoCo solves, just inverted. NeMoCo learns structure from raw data. I had the structure — a roster of agents, each with a job — and I needed the system to decide which model should do which task, live, under a budget that runs out.
That decision engine is what I built this week. It has three parts: a classifier that reads a task and decides what kind of work it is, a router that maps that to the best model chain given live quota, and a dashboard that renders the whole thing as a living, pulsing brain. This is the technical writeup — what each piece does, why it's shaped the way it is, and the failures that forced the design.
The Problem: A Roster Is Not a Router
The naive setup is a static table: this agent runs this model, that agent runs that model. It's simple, and it's wrong, for three reasons.
First, the budget runs out. Claude runs on a weekly quota and a rolling session quota, and they move independently. On one day the weekly sat at 15% while the session sat at 71% — and because the old router only watched the weekly number, it reported "Claude available, mode NORMAL" and routed coding work straight into a wall. The CLI refused every call for three hours with "You've hit your session limit" while the dashboard kept saying everything was fine. A router that doesn't read the limit that's actually binding isn't a router, it's a guess.
Second, quality is not one number. A model that writes good code is not therefore good at long-context retrieval or UI generation. Scoring every model with a single "quality" score is useless — it can't tell you which model should lead the coder versus the researcher. Each agent needs to be scored on its own axis.
Third, the reviewer must not inherit the writer's blind spots. I deliberately pair a writer and a reviewer — coder writes, diagnostician reviews — and they have to run different model families, or the reviewer just re-makes the same mistakes. The first version of the router kept landing both heads on the same model because it broke ties by latency. That defeated the entire design.
So the static table had to go. What replaced it is a decision engine that reads live state and computes, per task, the best model chain.
The Classifier: Reading the Task
The first piece is a classifier that reads a task and decides what kind of work it is. The input is a sentence — "the payment endpoint is returning 500 in production since the deploy" — and the output is a verdict: kind, tier, and priority.
The first version was pure regex. It worked, and it was brittle in exactly the ways regex always is. "Log parser" misrouted to ops (it's a code component, not an ops task). "Checkout is charging customers twice" carried no error vocabulary at all, so it classified as unknown and fell through to the main agent — on a production incident. "Throwing 500s" didn't match the status-code pattern because the trailing 's' broke the word boundary. Each of these was a real misroute that I fixed one at a time, and each fix was a new regex rule. That's the tell that you're fighting the wrong tool.
So the classifier now tries a cheap model first — `deepseek-v4-flash:cloud` reads the task and returns a JSON verdict. If the model is unavailable, it falls back to the regex classifier. The model is the primary path; the regex is the safety net. This is the same lesson as NeMoCo: don't hand-label every case, learn the structure and let it transfer.
The classifier also receives the live quota from its caller, so it can never drift from the router. The caller reads the quota state and passes it in; the classifier doesn't read the quota file directly. The key rule: production work is never downgraded onto a weak model, however tight the budget. A production incident classifies as `prod` even when Claude is exhausted — it doesn't get quietly rerouted to a light agent and reported as done. It blocks, and the handoff protocol fires. Quality risk is worse than quota risk, and the design never forgets which is which.
The Router: The Decision Engine
The router is where the real thinking happens. It takes the classifier's verdict and the live provider state, and computes a model chain per task tier.
The core is axis-based scoring. Each agent is scored on its own skill axis — coder on code generation, diagnostician on debug reasoning, researcher on fact retrieval, creative on UI generation, ops on tool calling, main on orchestration. A model's effective quality is its synthetic benchmark score times its observed reliability in real sessions. A model that benchmarks well but errors in production gets demoted. This is the "learn from real performance" layer: the benchmark is synthetic capability, the reliability data is what actually held up.
The scoring formula is Bander's weight matrix: `score = quality·Wq − cost·Wc − latency·Wl + claudeBonus`. Cost is a relative 0..1 tier, not real dollars — because ollama cloud has no per-call price and Claude is a subscription, you can't min-max scale them against each other honestly. And there's a hard quality floor: a candidate scoring below it on its own axis is dropped before ranking, so a cheap-but-dumb model can never win on price alone.
The router also handles the decorrelation problem — forcing the coder and diagnostician heads onto different model families so the reviewer doesn't inherit the writer's blind spots. And it handles subagent economics: a subagent runs a slice of its parent's job, and one delegation can fan out up to 20×, so subagent chains weight cost far above any standing tier. That's what keeps Claude out of subagent chains without a hard exclusion — at a 0.45 cost weight it simply can't win.
The Dashboard: The Robotic Brain
The dashboard is the part that's hardest to capture in text. It renders the whole system as a living brain.
There's a 3D brain view — a WebGL-rendered neural structure where each agent is a region, and you can orbit, zoom, and click a region to drill into its model chain, stats, and override selector. And there's an "electronic brain centerpiece" in the grid view: an SVG circuit board where the agents are workflow nodes connected by animated signal traces, with the whole thing pulsing faster or slower based on live activity.
The brain isn't just decoration. It's telemetry made visible. The dashboard derives per-agent activity from session-file growth between polls — the strongest signal that an agent is actually working — and renders it as signal pulses traveling along the circuit traces. When the system is idle, the brain slows to a crawl. When it's busy, the traces race. You can see the brain thinking.
There's also a tier selector — six buttons (Tier 1–4, a "Local" shield, and a "Baseline" reset) that let you force the whole fleet onto a given quality tier. And per-agent model overrides, persisted to a file, so you can pin a specific model to a specific agent when the live router's pick isn't what you want. The dashboard is the control surface for the brain.
The Failures That Shaped It
Every design decision here is a scar from a real failure, and they're worth listing because they're the actual content of the week:
- The session-limit blind spot. The router gated only on the weekly quota and ignored the session quota that was actually binding. The fix: read both, and let the tighter one win.
- The benchmark envelope bug. The rev1 benchmark wrote its file in a `{value:[...]}` envelope instead of a plain array, so every row lacked a `.model` field and the router silently got an empty map. Benchmark data never reached the router, and nothing complained. The fix: unwrap the legacy shape so old files still load.
- The axis-rubric inversion. The axis rubric scored opus 0.50 on debug reasoning — below a model that should never review production code. Letting it reorder the prod tiers would have made the reviewer the writer. The fix: prod tiers are not re-ranked by the rubric. A keyword rubric is not evidence strong enough to overturn a stated design decision.
- The context-window death spiral. Putting a 131k-context model at the head of the orchestration tier overflowed the window and triggered the compaction death spiral. The fix: every orchestration candidate must have a window comfortably above the context budget plus reserve.
- The reliability no-op. Session logs store the bare model name while candidates carry a provider prefix, so the reliability layer silently matched nothing. The fix: strip the prefix before matching.
What I'm Taking Away
The NeMoCo post ended with: labels are a shortcut, structure is what matters, and you can learn structure from raw experience. This week was the engineering version of that. I didn't hand-write a rule for every task — I built a classifier that reads the task and a router that reads the live state, and let the structure of the work decide the tool. The regex classifier was the "labels" approach, and it broke exactly where labels always break: at the edges, on the cases you didn't anticipate. The model-assisted classifier is the "structure" approach: learn the pattern, transfer it, fall back gracefully.
And the brain dashboard is the part that makes it feel real. When you can watch the signal pulses race along the circuit traces as the agents actually work, and see the whole thing slow to a crawl when it's idle, you're not looking at a config file anymore. You're looking at a system that has a pulse.
The router is open-source in spirit — it's a set of scripts in my workspace, `classify-job.mjs`, `model-router-core.ps1`, `quota-dashboard.mjs` — and it runs on a mix of Claude, cloud ollama models, and a local model that burns nothing. It's not a locked-down corporate system. It's a decision engine you could build yourself, and the failures that shaped it are the useful part.
That's the lesson. A roster is a list. A router is a decision. And a brain is what you get when the decision is made live, under a budget that runs out, by a system that learns from its own mistakes.