ReadingStop Sending Every Agent Turn to the Frontier Model
9 min read

Stop Sending Every Agent Turn to the Frontier Model

A pull request takes an agent 30 to 60 turns and most are reading a file or running a test. I default every turn to a Workhorse tier model, escalate to the Frontier tier on four signals, and price a 40-turn trajectory at list prices.

Listen to the audio overview(3:03)Fenrir Studio Voice
0:00
3:03

An agent that lands a pull request takes somewhere between 30 and 60 turns to do it. One of those turns is a plan. A handful are edits that matter. The rest are reading a file, running a test, fixing an import, and re-running the test. When every one of those turns goes to a Frontier tier model (GPT-6 Astra, Claude Fable 5.1, Gemini 3.1 Pro), the bill charges frontier prices for grep.

This is the third part of my AI Tokenomics series. Part one covered the eleven rules and the runtime guards around them. Part two was the spend cap that does not save you from a loop. This part is about the routing decision inside the loop: which tier gets which turn, decided by a mechanical signal. Then what that does to the cost of a trajectory at published list prices.

The pattern: default every turn to a Workhorse tier model (Gemini 3.8 Flash, Claude Haiku 4.5, GPT-5.6 Luna), escalate to the Frontier tier on four signals the code can observe, cap the escalations, and log every decision with its reason. This is a routing pattern, not a vendor comparison. The arithmetic below runs the same way for every pair.
PART 01

Anatomy of a 40-turn refactor trajectory

Before I can route turns I need to know what turns look like. Here is the shape of a typical refactor run, not a measurement: an agent asked to change a loader's error handling across a small module, with a pre-commit gate that runs the tests. The shares are rough and the point is the mix, which is skewed toward cheap, repetitive work.

Turn typeWhat happensRough shareTier
PlanRead the task, pick files, decompose into steps1 turnFrontier
Read and navigateOpen a file, list a directory, follow an import30%Workhorse
EditApply a small diff to one file25%Workhorse, unless the diff touches a public export
Run gateRun the test or lint command, read the exit code20%Workhorse
RepairFix whatever the gate complained about15%Workhorse for the first two tries, then Frontier
SummarizeWrite the PR description1 turnWorkhorse

Two things stand out. The plan turn is the only one where the model has to hold the whole problem in view, and it happens once. The repair loop is where cheap models get stuck and where an expensive model earns its price, but only after the cheap one has failed in a way the gate can see. Everything else is bookkeeping, and bookkeeping does not need a frontier model.

The same 40-turn trajectory priced three ways at Gemini 3.1 Pro and Gemini 3.8 Flash list prices. The router sends 8 of 40 turns to the Frontier tier: the plan, one edit that changed a public signature, and six repairs on a file that failed the gate twice. One 40-turn refactor trajectory, priced three ways at list prices turn 1 10 20 30 40 all-Frontier $2.37 router $1.14 all-Workhorse $0.83 Frontier tier turn, $0.0592 each at Gemini 3.1 Pro list price Workhorse tier turn, $0.0207 each at Gemini 3.8 Flash list price plan public export changed file pinned after 2 gate failures 32,000 prompt tokens per turn, 2,000 output, 50% KV-cache hit rate. 8 of 40 turns escalated: 52% below all-Frontier. Shape of a typical run, not a measurement. The lab recomputes with your own numbers.
Figure 1. The same 40-turn trajectory priced three ways at Gemini 3.1 Pro and Gemini 3.8 Flash list prices. The router sends 8 of 40 turns to the Frontier tier: the plan, one edit that changed a public signature, and six repairs on a file that failed the gate twice.
02

The router

The router is one function with no dependencies beyond the standard library. It takes a turn and the trajectory state and returns a tier. The default is the Workhorse tier. It escalates on exactly four signals:

  • The turn is a plan or decompose step.
  • The pre-commit gate returned exit 1 twice for the same file.
  • The diff changes a public signature. This is an AST check on the before and after source, not a regex on the diff.
  • The Workhorse tier output failed schema validation.

Escalations are capped per trajectory. A file that has failed the gate twice is pinned to the Frontier tier for the rest of the run, so the router does not bounce it back down after one good turn. Every decision is appended to a log with its reason, because a router you cannot audit is a router you will not trust when the bill arrives.

router.py
Python
#!/usr/bin/env python3
"""route_turn: pick a tier for one agent turn from mechanical signals only.

Default is the Workhorse tier. The router escalates to the Frontier tier when
  (a) the turn is a plan or decompose step,
  (b) the pre-commit gate returned exit 1 twice for the same file,
  (c) the diff changes a public signature (AST check, not a regex),
  (d) the Workhorse output failed schema validation.
Escalations are capped per trajectory, a file is pinned to the Frontier tier
after two repairs, and every decision is logged with its reason.
Standard library only.
"""
import ast
import json
from dataclasses import dataclass, field

WORKHORSE = "workhorse"
FRONTIER = "frontier"
ESCALATION_CAP = 8        # per trajectory; 20 percent of a 40-turn run
REPAIRS_BEFORE_PIN = 2    # after this many gate failures a file stays on Frontier


@dataclass
class State:
    escalations: int = 0
    gate_failures: dict = field(default_factory=dict)   # path -> consecutive exit-1 count
    pinned: set = field(default_factory=set)             # paths pinned to the Frontier tier
    log: list = field(default_factory=list)              # one dict per routed turn


def public_signatures(source: str) -> dict:
    """Name -> signature for every top-level def or class not starting with '_'."""
    out = {}
    for node in ast.parse(source).body:
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and not node.name.startswith("_"):
            out[node.name] = ast.dump(node.args)
        elif isinstance(node, ast.ClassDef) and not node.name.startswith("_"):
            out[node.name] = "class"
    return out


def public_exports_changed(before: str, after: str) -> bool:
    return public_signatures(before) != public_signatures(after)


def route_turn(turn: dict, state: State) -> str:
    """turn = {"n", "kind", "path"?, "before"?, "after"?, "schema_failed"?}."""
    kind, path = turn["kind"], turn.get("path")
    tier, reason = WORKHORSE, "default"

    if kind == "plan":
        tier, reason = FRONTIER, "plan step"
    elif path in state.pinned:
        tier, reason = FRONTIER, "file pinned after repeated repairs"
    elif kind == "repair" and state.gate_failures.get(path, 0) >= 2:
        tier, reason = FRONTIER, "gate exit 1 twice on same file"
    elif kind == "edit" and turn.get("before") is not None \
            and public_exports_changed(turn["before"], turn["after"]):
        tier, reason = FRONTIER, "diff touches public export"
    elif turn.get("schema_failed"):
        tier, reason = FRONTIER, "workhorse output failed schema"

    if tier == FRONTIER and state.escalations >= ESCALATION_CAP:
        tier, reason = WORKHORSE, "cap %d reached, stayed on workhorse" % ESCALATION_CAP
    if tier == FRONTIER:
        state.escalations += 1

    state.log.append({"turn": turn["n"], "kind": kind, "tier": tier, "reason": reason})
    return tier


def observe_gate(turn: dict, state: State, exit_code: int) -> None:
    """Feed a gate result back so the next repair on this file can escalate."""
    path = turn.get("path")
    if exit_code == 0:
        state.gate_failures.pop(path, None)
        return
    state.gate_failures[path] = state.gate_failures.get(path, 0) + 1
    if state.gate_failures[path] >= REPAIRS_BEFORE_PIN:
        state.pinned.add(path)


def dump_log(state: State) -> str:
    return "\n".join(json.dumps(entry, separators=(",", ":")) for entry in state.log)

The decision log is one JSON object per line. That format is boring on purpose: it greps, it loads into a spreadsheet, and it answers the only question that matters after the fact, which is why a given turn cost what it cost.

decisions.jsonl
JSON Lines
{"turn":1,"kind":"plan","tier":"frontier","reason":"plan step"}
{"turn":2,"kind":"read","tier":"workhorse","reason":"default"}
{"turn":18,"kind":"edit","tier":"frontier","reason":"diff touches public export"}
{"turn":32,"kind":"repair","tier":"frontier","reason":"file pinned after repeated repairs"}
{"turn":38,"kind":"edit","tier":"workhorse","reason":"cap 8 reached, stayed on workhorse"}

The video below runs the router over a scripted 40-turn trajectory and prints every decision, then prices the run three ways. The trajectory is a fixture, not a recording of a live agent. What it shows is the router doing what the code says it does.

ast-circuit-breakerScreen recording
The router over a scripted 40-turn trajectory, then the three totals at list prices
PART 02

Three numbers at list prices

I priced this at published list prices, not a report from a live run. The prices are the vendors' list prices as of the catalog's date, 14 September 2026, and they change. The lab at the end of the post recomputes everything from your own inputs, so treat the numbers here as the shape of the answer rather than the answer.

The lab's defaults: 32,000 prompt tokens per turn, 2,000 output tokens per turn, a 50% KV-cache hit rate on the prompt, 40 turns per trajectory, and a router that escalates 20% of turns. Per-turn cost is prompt tokens times the uncached input price for the half that misses, plus prompt tokens times the cached input price for the half that hits, plus output tokens times the output price.

With Gemini 3.1 Pro as the Frontier tier and Gemini 3.8 Flash as the Workhorse tier, a Frontier tier turn costs $0.0592 and a Workhorse tier turn costs $0.0207. From there:

PolicyPer turnPer 40-turn trajectoryPer 1,000 trajectories
All Frontier$0.0592$2.37$2,368
All Workhorse$0.0207$0.83$828
Router, 20% escalation$0.0284$1.14$1,136

The router lands at 52% below all-Frontier while still sending one turn in five to the expensive tier. The gap between all-Workhorse and the router, about $300 per thousand trajectories, is what the escalations buy: a frontier model on the plan, on the public-signature edit, and on the repair loop that the cheap model could not close.

The same arithmetic with the other two pairs. What changes is the ratio between the tiers. The ratio is the whole story, so I am giving the ratio and the three trajectory numbers and nothing else.

PairFrontier to Workhorse ratioAll FrontierAll WorkhorseRouter, 20%
Gemini 3.1 Pro / Gemini 3.8 Flash2.9x$2.37$0.83$1.14
Claude Fable 5.1 / Claude Haiku 4.59.6x$10.56$1.10$3.00
GPT-6 Astra / GPT-5.6 Luna46.6x$11.04$0.24$2.40

Read down the last column, not across the rows. The wider the price gap between a vendor's two tiers, the more a 20% escalation rate costs relative to all-Workhorse, and the more it saves relative to all-Frontier. At a 2.9x ratio the router saves 52%. At a 46.6x ratio it saves 78%, and the 20% of turns that escalate account for 92% of the routed bill. That last number is the argument for keeping the escalation rate honest: every point of escalation you cannot justify with a signal is paid at the wide end of the ratio.

Per 1,000 trajectories: $2,368 against $1,136 for the Gemini pair, $10,560 against $2,995 for the Anthropic pair, $11,040 against $2,397 for the OpenAI pair. Change the prompt size, the cache hit rate, or the escalation rate in the lab and these move together.
04

Where it breaks

Three failure modes, each with a fix already in the router or in the lab.

Cascade thrash. The Workhorse tier fails the gate, the router escalates, the Frontier tier fixes it, the router drops back to Workhorse for the next edit on the same file, the gate fails again. Without a cap this loop pays for both tiers on every cycle. The router caps escalations per trajectory and pins a file to the Frontier tier after two repairs. The third attempt on a hard file stays expensive and stays there.

KV-cache prefix loss on a tier switch. The cached input price assumes the prompt prefix is already resident on the model that is about to serve the turn. Switching tiers means the other model has never seen that prefix, so the first turn after a switch is a cold prompt billed at that tier's uncached rate. At the lab defaults a cold Frontier tier turn on Gemini 3.1 Pro is $0.0880 instead of $0.0592. The lab's cold-cache preset sets the hit rate to zero so you can see the worst case for a router that switches often. If your escalations cluster, the cost sits between the warm and cold numbers; if they alternate turn by turn, you are close to cold.

Trajectories that should never be routed. Some runs deserve the Frontier tier from the first turn: greenfield design where there is no gate to fail yet, security-sensitive changes where a wrong edit that passes tests is the failure, and multi-repo refactors where the plan has to survive across contexts the Workhorse tier will not see. Pin the whole trajectory. The router has a mode for that, and it is one line: every turn returns Frontier with the reason "pinned trajectory".

The rule under all three: the trigger is a mechanical signal the code can observe. Exit codes, AST diffs, schema validators, turn types. Never the model's own confidence. A model that is asked whether it needs a bigger model will answer in whichever direction its training rewarded, and you cannot audit that.

Latency moves the same direction as cost, and a Workhorse tier turn returns faster in wall-clock terms than a Frontier tier turn on the same prompt. How much faster depends on your region, your prompt size, and the hour. Measure it on your own traffic and enter the numbers into the lab rather than taking a figure from me.

PART 03

Lab: your prices, your trajectory

The lab below recomputes the three numbers from your own prompt size, output size, cache hit rate, turn count, and escalation rate, using the catalog list prices for whichever pair you pick. Four presets match the sections above: all-frontier is the baseline, router-20 is the run I walk through below, router-5 is what a tighter set of signals buys, and cold-cache is the tier-switch worst case with the hit rate at zero.

Interactive Lab · tokenomics-arbitrageFrontier vs Workhorse Routing Cost Engine

Compare three ways to run one agent trajectory at published per-token list prices: every turn on the Frontier tier, every turn on the Workhorse tier, or a router that keeps the Workhorse tier by default and escalates a chosen share of turns. Pick a model per tier from three vendors or type custom rates, then move prompt size, KV-cache hit rate, turn count, and escalation share.

One reading of the same idea from the research side: Pro-Router: Token-Aware Progressive Model Routing (Aug 2026) by Gui and co-authors frames routing as a progressive decision made with the token budget in view rather than a one-shot classifier. That is the same instinct as the repair-count and cap rules here. Their router learns the signal; mine hard-codes it. For a pre-commit loop I would rather have the hard-coded one, because I can read it.

06

What to change this week

  • Flip the default. Route every turn to the Workhorse tier and add the plan turn as the only escalation. Watch the gate pass rate for a day before adding the other three signals.
  • Write the decision log before you write the router. One JSON line per turn with the tier and the reason. If the reason field is ever empty or says "model asked for it", that is the bug.
  • Put the escalation cap in the same config file as your spend cap. They are the same control at two different layers, and the second one is the one that fires when the first one is misconfigured.

First published 14 Sep 2026 · last revised 16 Sep 2026 · 3 revisions

CITED BY
  1. Instrument: Frontier vs Workhorse Routing Cost Engine

    Compare three ways to run one agent trajectory at published per-token list prices: every turn on the Frontier tier, every turn on the Workhorse tier, or a router that keeps the Workhorse tier by default and escalates a chosen share of turns. Pick a model per tier from three vendors or type custom rates, then move prompt size, KV-cache hit rate, turn count, and escalation share.