Opus 4.8 runs the session. Opus 5 builds. Who’s good at what.
Opus 4.8 orchestrates, Opus 5 builds. The chart sets Opus 4.8 at zero. One catch: Opus 5’s numbers come from when it drove sessions itself, before it stopped too often and deleted too much of its own work. See the two measures that matter.
Of the sixteen underlying measures, Fable 5 leads eight, Opus 5 leads seven, the two tie on one, and Opus 4.8 leads none.
Fable 5 ran 2–20 July and was dropped when a 2× usage promotion ended. Opus 5 became available on 25 July. The two never ran concurrently, and Opus 4.8 was in use throughout. Claude Code offers no configuration in which Opus 4.8 orchestrates Opus 5: across the 3,925 subagents recorded under those two models, none pairs them in either direction.
| Orchestration: dispatched work keeps moving | Fable 5 | Opus 4.8 | Opus 5 |
|---|---|---|---|
| Dispatching turns that end with workers still running ↓ | 29.3% | 44.6% | 68.8% |
| Tool calls after the last dispatch ↑ | 2.83 | 1.94 | 1.87 |
| Background wakes absorbed without a person, per span ↑ | 1.80 | 0.79 | 0.70 |
| Running workers re-tasked, per 100 turns ↑ | 9.2 | 2.6 | 4.6 |
| Subagents it went back to ↑ | 9.6% | 2.5% | 5.9% |
| Median unattended run ↑ | 4.8 min | 3.2 min | 2.6 min |
| Escalation: the model asks a person | Fable 5 | Opus 4.8 | Opus 5 |
|---|---|---|---|
| Turns ending in a question ↓ | 3.8% | 18.3% | 6.4% |
| AskUserQuestion calls, per 100 turns ↓ | 2.7 | 7.5 | 3.4 |
| Permission phrasing, per 1k chars ↓ | 0.13 | 0.27 | 0.13 |
| Turns interrupted by the person ↓ | 3.8% | 5.0% | 3.1% |
| Coding: what the session produced | Fable 5 | Opus 4.8 | Opus 5 |
|---|---|---|---|
| Edits per session ↑ | 80 | 94 | 192 |
| Code written per session ↑ | 181k | 192k | 439k |
| Edit failure rate ↓ | 5.10% | 3.35% | 2.57% |
| Tests per 100 edits ↑ | 59 | 55 | 71 |
| Commits per session ↑ | 9.4 | 10.4 | 18.3 |
| Cost per edit ↓ | $2.28 | $2.03 | $1.61 |
Edits per session against handbacks per hour, each model across its full corpus.
Per handback: 18.1 edits for Fable, 8.7 for Opus 4.8, 12.0 for Opus 5. Per session: 4.4, 10.7 and 16.0 handbacks.
Bars scale within each row against the largest value in that row.
Sessions of six or more spans, split into thirds, showing the share of dispatching turns that ended while subagents were still running: Opus 4.8 goes 23.5%, 20.7%, 12.0%; Opus 5 goes 34.0%, 35.9%, 34.4%. Fable's median session is 4 spans, so its row rests on 16, 5 and 8 turns and is not usable.
Background wakes absorbed per span across the same thirds fall from 0.79 to 0.44 for Opus 5 and from 0.83 to 0.60 for Opus 4.8.
Per-hour rates in this chart are workload-sensitive: Opus 4.8 records 1.08 handbacks per hour across its full corpus and 2.06 within the 2–20 July window alone. Per-turn ratios are the comparable figures across models.
Per-turn and per-session rates across each model's full corpus.
Turns containing a dispatch: 23.7%, 22.7% and 21.3%. Workers that ran more than one round, meaning the orchestrator went back to them: 9.6% of 477 for Fable, 2.5% of 3,112 for Opus 4.8, 5.9% of 813 for Opus 5.
Per session, main loop and all subagents combined, attributed to the driving model.
All four billable token counters at API list rates, against the model that produced each message.
These sessions ran on a subscription, so cost figures are equivalent API spend rather than amounts billed. Cache reads are the majority of tokens in every case, at 176M, 234M and 491M per session. Subagents did 79%, 76% and 87% of all edits.
Edits, characters and commits count volume. Edit-failure rate, rework ratio and reverts are the available proxies for care. No measure here establishes whether the code was correct.
Four extractors walk raw session transcripts, which are JSONL with one file per session and subagent transcripts nested beneath. Each metric is attributed to the model named in the assistant record, so a session that changed models is split rather than assigned to one.
A span runs from one human message to the next. Three kinds of user record are treated differently: a typed message is a handback; a <task-notification> is a background task waking the session and does not close a span; tool results, hook injections and system reminders are not user turns. Across the corpus, 45% of the wakes that close an assistant turn are task notifications rather than typed messages. Time is charged as work while the model is producing and as wait between its last message and the next human one, with waits over four hours discarded.
| Corpus | Fable 5 | Opus 4.8 | Opus 5 |
|---|
Opus 4.8, Opus 5 and Sonnet 5 ran the same weeks, so it’s mostly like-for-like. Fable 5 is sparse; Opus 5 stops after early August. Project mix tracks the model. Turns: 2,709 / 1,816 / 526 / 440.
Every recorded turn ran at high reasoning effort, so that stays uncontrolled. Haiku 4.5 is too rare to include.
The figures above come from four extractors run over raw session transcripts. Rather than distributing them as scripts to execute against a full session history, the specification is given below. Handed to Claude, it produces the extractor and runs it locally.
It fixes the parts that determine comparability: where the transcripts live, what counts as a turn, which regexes are applied, how subagent work is attributed, and the exact JSON shape the charts read.
Measure how different models actually behave in my own Claude Code logs, then build
a metrics.json I can drop into https://3dl.dev/coding-vs-orchestration.html to
redraw its charts with my numbers.
Write the extraction script yourself. Python 3, standard library only, no installs.
Run it, sanity-check the output, and tell me what it says.
── WHERE THE DATA IS ──────────────────────────────────────────────────────────
~/.claude/projects/<slug>/<session-id>.jsonl one JSON object per line
~/.claude/projects/<slug>/<session-id>/subagents/**/*.jsonl subagent transcripts
Each record has: type ("user" | "assistant" | ...), timestamp, sessionId,
isSidechain, message.model, message.usage.output_tokens, and message.content as a
list of blocks typed "text" | "thinking" | "tool_use" (with .name, .input) |
"tool_result" (with .tool_use_id, .is_error, .content).
Transcripts rotate after ~30 days. If I use more than one machine, run this on each
and merge — my laptop held a quarter of what my build box did, and the small slice
gave the opposite answer on two metrics.
── WHAT COUNTS AS A TURN ──────────────────────────────────────────────────────
One human message plus every assistant message before the next human message.
A "user" record is NOT human if isSidechain or isMeta is set, if its content list
contains a tool_result, or if its text begins with "<system-reminder>",
"<local-command" or "Caveat:". Text beginning "[Request interrupted" ends the turn
and marks it interrupted. Attribute a turn to the first assistant model in it.
Skip the model "<synthetic>". Record the "effort" field if present — it is a
confound and I want to be able to filter on it.
── WHAT TO MEASURE ────────────────────────────────────────────────────────────
Per turn: total tool calls and per-tool counts; whether any dispatch tool ran
(Agent, Workflow, Task); and how many tool calls came AFTER the last dispatch.
That last one is the follow-through signal — zero means the turn ended the instant
the workers reported back. Also output tokens, and whether my next message was a
bare nudge ("continue", "keep going", "why did you stop").
On the final assistant text block of each turn — the part a human actually reads —
measure length, words per sentence, unique-word ratio, and per-1000-character
counts of bullets (^\s*[-*] ), headers (^#+ ), bold pairs, table rows (^\|),
backticks and digits, plus regex hits for:
permission want me to|should i|shall i|let me know|do you want|would you like|your call
limitation i (did not|didn't|have not|can't|cannot)|not verified|out of scope|deferred|blocked|untested
caveat caveat|worth flagging|to be clear|note that|heads.up|strictly speaking
hedge likely|probably|appears to|seems to|may be|might be|unclear|ambiguous
Per session: how many subagent transcripts were spawned, split into Workflow runs
(subagents/workflows/<run>/agent-*.jsonl — count the runs too, so you can get
agents per wave) versus plain Agent calls, and their total output tokens.
Coding, counted across the main loop AND every subagent beneath it, attributed to
the session's dominant model: Edit/Write calls, characters written, failed tool
results (is_error, or text starting "Error:", or containing "String to replace not
found"), Bash commands matching a test runner, a git commit, or a git revert, and
repeat edits to the same file path (rework). Counting only the orchestrator's own
edits will badly understate a model that delegates its typing — that mistake
reversed one of my conclusions.
── PICK THE MODELS ────────────────────────────────────────────────────────────
Find what is actually in my logs and how much of each:
grep -ho '"model":"[^"]*"' ~/.claude/projects/*/*.jsonl | sort | uniq -c | sort -rn
Compare the two to four with the most turns. Treat anything under ~300 turns as too
small to draw a line from, and say so rather than quietly including it.
── OUTPUT SHAPE ───────────────────────────────────────────────────────────────
Write metrics.json exactly like this, or the page will not read it:
{
"corpus": [ {"key":"turns","label":"Turns analysed","unit":"turns",
"better":"high","note":"","v":{"model-a":3027,"model-b":1370}} ],
"orch": [ ... ], "esc": [ ... ], "prose": [ ... ], "code": [ ... ],
"index": { "code": {"model-a":67.4,"model-b":91.2},
"persist": {"model-a":83.6,"model-b":60.0} }
}
Five arrays — corpus, orch, esc, prose, code — each a list of metric rows with
key, label, unit, better ("high" or "low", meaning which direction is good), note,
and v mapping each model id to its value. Model keys must be identical everywhere.
Put cold-stop rate, work after last dispatch, agents per wave, subagents per
session, dispatch rate, tool calls per turn, dead turns and worker tokens in
"orch"; the question and phrase-density metrics in "esc"; the writing metrics in
"prose"; the edit and test metrics in "code".
For the two indices: score every model on each metric against the best performer —
100 x value / best where higher is better, 100 x best / value where lower is better
— then take the unweighted mean. "persist" is built from cold-stop rate, work after
last dispatch, agents per wave and subagents per session. "code" from edits,
characters, edit-failure rate, tool-error rate, rework, tests per edit, commits and
reverts.
── THEN TELL ME ───────────────────────────────────────────────────────────────
Which model ends the most delegated turns cold, which writes the most code per
session, and whether they are the same model. Flag every confound you can see:
models used in different months, on different projects, or at different reasoning
effort. Say which comparisons are too thin to trust. If a result contradicts what I
expected, say so plainly — do not smooth it over. Volume is not quality: edits and
commits measure throughput, and nothing here knows whether the code was correct.
About 20 seconds over a couple of gigabytes. The records hold counts, not transcript text, so they are safe to copy off a remote box.
~/.claude/skills/model-behavior/SKILL.md and ask for it by name.
metrics.json
The file behind this page. A worked example of the output shape. It drops straight back in below.
Drop the metrics.json the prompt produces and every chart on this page re-renders from your file, baseline chart included. The file is read in the browser; nothing is uploaded and no account is involved. The page is complete without sharing anything.
Model IDs, legend and colours follow the loaded file. The baseline chart needs fable-5, opus-4-8 and opus-5 in the data; every other chart follows whatever models the file contains.
One operator's logs answer some questions; a distribution across operators answers more. If you are willing to share yours, a community dataset at github.com/3dl-dev/model-telemetry accepts the same file as a pull request, wrapped with a short meta block its skill prepares for you. Submissions are counts only, carry no transcript text or project names, and are licensed CC0. Inclusion in the published aggregate is a separate, allowlist-based curation step recorded in the repository, so what is in and what is out stays auditable. Sharing is optional; the charts above work either way.
The scripts used to produce the figures on this page. The prompt above generates equivalent output.
metrics.jsonTwo measures need a second look. Both are about work that doesn’t stick.
Most edits, most characters per session. Also the most stops and the most reverts. That’s why it builds as a worker now, not the driver.
A turn ending right after dispatch might be a clean hand-off or an early quit. The tell: did you have to type “continue”? Per 100 turns:
| Times you had to push it | Fable 5 | Opus 4.8 | Opus 5 | Sonnet 5 |
|---|---|---|---|---|
| nudges per 100 turns, lower is better | 0.57 | 0.81 | 1.82 | 0.23 |
Opus 5, two to eight times as often as the rest. Sonnet 5 almost never, but it does the least.
The charts count what a model types, not what it deletes. Characters written then removed the same session (rm, git restore, git revert):
| Code killed same-session | Fable 5 | Opus 4.8 | Opus 5 | Sonnet 5 |
|---|---|---|---|---|
| % of written chars killed, lower is better | 43% | 27% | 61% | 27% |
| net surviving code, MB per model | 5.2 | 16.3 | 10.9 | 1.8 |
Opus 5 writes the most and deletes the most. Net of waste, Opus 4.8 leaves the most standing. Real source, not scratch. Read it as a waste ratio, not line counts.
Fold survival in and Opus 5 still tops the code index. Eight of its nine parts are just volume. The rows tell the truth; the score doesn’t.
Shortest replies, fewest edits, most do-nothing turns. Too few sessions to rank.
The numbers track the workflow. Opus 5 driving meant more code and less of it kept. It builds better as a worker.