Repository navigation
Builtin Comparisons
These are paid runs that compare two features of claudetto with the built-in features of Claude Code.
The first is the tool advise with the built-in advisor (task 5.1).
The second is the goal engine with the built-in /goal (task 5.2).
They ran on 2026-10-21, on Claude Code 2.1.306, with Haiku 5.5 as the working model.
Each arm has 10 runs for each case, so every case meets the D25 floor.
There are two rounds.
The first round found that its cases were too easy, and the second round tried to fix that.
- On the easy tasks all arms pass, and on the hard tasks all arms fail. The advisor was called in every run of the advisor arms (see "Round 3"). So on Haiku 5.5, neither advisor changes the outcome.
- A first price for the
opuscalls was twice too high. The price table is the catalog entry of the model, $4 and $20 for one million tokens in and out, and a run of the built-in advisor confirms it (see "Cost ofadvise"). With the right price,adviseatefforthighcosts 0.63 USD against 1.19 USD for 10 runs ofadvice-audit, and 0.56 USD against 2.02 USD for 10 runs ofadvice-audit-web. - Round 6 measured the advice quality, the tokens of the built-in advisor, and the cost levers (see "Round 6").
The built-in advisor sends about 23300 tokens in and gets about 5200 tokens out for a call.
advisewitheffortlowand a short answer sends about 2000 tokens in and gets about 1200 tokens out. It names as many of the 6 faults as the built-in advisor, for about 0.37 USD for 10 runs against 2.02 USD. - Decision (the user, 2026-10-21): the default of
advisorisclaudetto, and since round 6 the default ofadvisorEffortislowand the system prompt asks for a short answer (see "Decision for the Default"). The slice turns off the built-in advisor with$.env.setatsession.start, so that Claude never sees two advisors.$.model.completereports tokens only, and the mod API has no way to add a cost to the session or a price source, so the cost of theopuscalls stays out of the session cost. - Goal engine against the built-in
/goal: parity ongoal-two-checksandgoal-first-violation(10 of 10 each, again after the fix).goal-blind-qafavours the engine by design, because the built-in/goalcannot run a check. The engine passed 10 of 10 and the built-in/goalpassed 0 of 10. - The retry case found one defect of the goal engine, a
judgecheck that cannot pass. The defect is fixed (see "Goal Engine"). - The round 3 audit runs found one limit of
advise: its context held no tool results, and this is fixed (see "Round 3"). - The missing cost of the
opuscalls ofadviseis a limit of Claude Code, not a defect ofclaudetto.
claude plugin eval could not run the arms, so a script runs them.
- The eval child takes only
EVAL_*keys inexecution.env, and it has no--advisorflag. A run withCLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL=1in the shell gave no advisor tool to the model. An option of the plugin has no way into the eval child. -
tools/compare-builtins.tsrunsclaude -pin the sandbox ofclaudetto-sandbox(Sandbox). It reads the prompt, the scaffold, and the graders of acase.yaml, so the cases keep the form of the other eval cases. - A run has a new workspace, built by the
scaffold.shof the case. The plugin loads with--plugin-dir, and the options of the arm are inpluginConfigsof the sandbox settings. The script grades the files with theregexgraders, and the tool calls with thetool_usedgraders. An optionalfinal.shof the case runs in the workspace, and it must exit 0. The cases are intests/compare/, not inevals/of a slice, so they do not ship inplugins/. Soclaude plugin eval,just eval, andjust eval-tunenever see them (tests/claudetto/evals/compare-cases.test.ts). A run passes when all graders pass. - Command:
bun tools/compare-builtins.ts <case dir> <arm> --runs 10 -j 4 --max-cost-usd <usd>. - Pilots: one run of each case on each arm ran first, to catch a broken grader.
- Model:
claude-haiku-5-5for the work. The advisor of both advisor arms isopus, so the two arms ask the same model. No arm needed a stronger working model. - The graders read files and tool calls, not the words of the reply.
| Arm | Setup |
|---|---|
advise-ours |
Option advisor set to claudetto, advisorModel set to opus, and CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1. The model calls mcp__claudetto__advise. |
advise-builtin |
Option advisor set to builtin, and the flag --advisor opus. The model calls the server tool advisor. |
advise-none |
The control: CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1 and no tool. The grader advisor-called is left out. |
goal-ours |
The plugin as it is. The prompt /goal <condition> goes to the goal engine. |
goal-builtin |
A copy of the plugin with no /goal command hook, no stop hook of finish, and no goal_status attachment, so the built-in /goal runs and the other hooks stay. |
-
advice-rounding: A cart total is one cent low, becausetoCentsfloors a float product. The prompt asks for the advisor once before any edit. Graders:ROOT.txtnamestoCents, the fix isMath.round,report.mjsandcart.mjsstay as they are, and the advisor was called. -
advice-rename-field: The fieldnamebecomesfullNamein a property, a destructure, a JSON file, and a key built from"na" + "me". Decoysusernameandfilenamemust stay. Graders: seven file graders and the advisor call. -
goal-two-checks: Three bugs, and a style script that is not a test. The prompt is/goal `node --test check` and `node check/style.mjs` both exit 0. Graders:final.shruns both commands. -
goal-first-violation: Eight modules withvarand no header, three bugs, and a style script that stops at the first fault. The prompt is the same. -
goal-blind-qa: The task is inTASK.mdand says little.CLAUDE.mdsays the files incheck/belong to QA, and that Claude must not open or run them, because the goal runs them.check/qa.mjsshows the first of six rules that fails, so the first attempt fails and the goal must start new turns. The prompt is/goal `node check/qa.mjs` exits 0. Graders:src/slug.mjsexists,qa.mjswas not read, andfinal.shrunsqa.mjs. The built-in/goalhas no engine that runs a check, so this case tests exactly the feature that differs.
The cases are in tests/compare/.
The cost is the sum of total_cost_usd of the runs.
| Case | Arm | Passed | Mean turns | Cost (USD) |
|---|---|---|---|---|
advice-rounding |
advise-ours |
10/10 | 10.7 | 0.050 |
advice-rounding |
advise-builtin |
10/10 | 8.0 | 1.193 |
advice-rounding |
advise-none |
10/10 | 5.1 | 0.083 |
advice-rename-field |
advise-ours |
10/10 | 11.6 | 0.064 |
advice-rename-field |
advise-builtin |
10/10 | 11.3 | 1.221 |
advice-rename-field |
advise-none |
10/10 | 8.0 | 0.088 |
goal-two-checks |
goal-ours |
10/10 | 11.0 | 0.056 |
goal-two-checks |
goal-builtin |
10/10 | 9.5 | 0.067 |
goal-first-violation |
goal-ours |
10/10 | 17.6 | 0.062 |
goal-first-violation |
goal-builtin |
10/10 | 16.6 | 0.079 |
goal-blind-qa |
goal-ours |
10/10 | 3.5 | 0.097 |
goal-blind-qa |
goal-builtin |
0/10 | 14.9 | 0.103 |
- For
advise, the pooled pass rate is 20/20 on each side. The 95% Newcombe CI of the difference is [-0.16, +0.16]. One case alone (10 against 10) gives [-0.28, +0.28]. - The cost of
advise-oursin this table leaves out its calls toopus. The tool calls$.model.complete, and Claude Code does not add that cost tototal_cost_usd. The sums ofadvise-oursare in "Cost ofadvise", with theopuscalls measured. - The goal arms cost the same within noise, and the working model is cheap. The turn counts differ by about 1 to 1.5 turns, and the ranges overlap.
The coordinator asked for two advise cases where the control without an advisor passes 5 of 10 runs or fewer.
Haiku 5.5 passed every candidate, in 1-run to 6-run pilots of the control arm:
| Candidate | Control passes | Pilot cost (USD) |
|---|---|---|
mergeIntervals with three faults, one visible |
4/4 | 0.03 |
A stale test, with the rule in docs/RULES.md and a hint in CLAUDE.md
|
4/4 | 0.03 |
| The same, with the hint removed | 6/6 | 0.05 |
A generated file src/routes.mjs that final.sh regenerates |
4/4 and 4/4 | 0.05 |
isoWeek near the turn of a year, hidden dates |
4/4 and 4/4 | 0.08 |
| Eight functions with one subtle fault each, hidden audit | 6/6 | 0.11 |
The same, with no Bash tool to run code |
6/6 | 0.10 |
A logic puzzle of 6 houses and 54 clues, no Bash tool |
6/6, 6/6 with --effort low, 6/6 with thinking off, then 10/10 |
0.28 |
Haiku found each hidden fault by reading the code, and solved the puzzle by hand. A case that Haiku fails alone needs a task that reasoning, reading, and running code cannot solve, and then an advisor that sees only the conversation cannot solve it either. So no honest case with a control at 5 of 10 or less was found.
The goal was a measurement with both advisors run on tasks that Haiku fails alone.
All pilots ran the control arm (no advisor) with the subagent tools off (--disallowedTools Agent,Task,ListAgents,SendMessage).
This matters: in the first pilots Haiku used a Plan subagent as its advisor, so the control was not clean.
| Candidate | Control passes | Setting |
|---|---|---|
dayStarts, date ranges across zone changes, with a time limit |
3/3, 3/3, 3/3, 3/3 | default, --effort low with thinking off, no subagent, no Bash
|
parseScsv, a precise dialect of CSV with escapes |
3/3, 3/3, 3/3, 3/3 | the same four settings |
bestJobs, jobs on k machines (min-cost flow), brute force check and time limit |
3/3, 3/3, 3/3, 3/3 | the same four settings |
erfc to a relative error of 1e-14 |
3/3, 4/4 | default, and --effort low with no Bash
|
advice-audit, a review with 8 subtle faults |
0/4, 0/4, 0/4 | no Bash, no subagent |
advice-audit-web, a review with 6 subtle faults |
3/4, 0/4, 0/4 | the same |
Haiku wrote a correct min-cost flow and a correct erfc without Bash.
Only the code reviews with subtle faults made the control fail.
The four algorithm candidates are not kept in the repository.
Two early versions of the audit cases flagged safe decoy functions for real reasons, so the final graders list the faults only and do not punish extra findings.
Both cases ran with the working model on Read, Write, and Edit, no Bash, and no subagent tools, the same in each arm.
The prompt asks for the advisor once, after the file is read.
The grader advisor-called is a tool_used grader, and the table counts it from the traces.
| Case | Arm | Passed | Runs with an advisor call | Cost (USD) |
|---|---|---|---|---|
advice-audit |
advise-none |
0/10 | 0 | 0.046 |
advice-audit |
advise-ours |
0/10 | 10 | 0.055 (session only) |
advice-audit |
advise-builtin |
0/10 | 10 | 1.185 |
advice-audit-web |
advise-none |
0/10 | 0 | 0.059 |
advice-audit-web |
advise-ours |
0/10 | 10 | 0.055 (session only) |
advice-audit-web |
advise-builtin |
0/10 | 10 | 1.481 |
- The reference list of faults, run through
final.shof each case, passes. A stub with no list fails. - In
advice-audit, every run of every arm missed the same two items:readUpload(a symlink in the folder) andfindAccount(the Kelvin sign and other case mappings oftoLowerCase). The other six items were found in 10 of 10 runs ofadvise-noneandadvise-builtin, and in 6 to 10 of 10 runs ofadvise-ours. Three runs ofadvise-oursalso missedunsign. - One direct review of
src/web.mjsbyopusin-p(the code in the prompt) named all six faults and alsowebhookOk, which is safe. The review ofsrc/app.mjsran too, but its reply was cut by the hooks of the project, so the page makes no claim on it. So the web case is solvable byopuswhen it sees the code, and the 0 of 10 there means that Haiku did not turn the advice into a complete list. - The 0 of 10 is not a broken grader: the reference list passes, and the failed runs miss named items.
The opus advice of advise began with "I haven't seen src/app.mjs, so all of this rests on your summary."
advise sends the text of the messages and the names of the tools, but no tool results, so the advisor does not see the file that Claude read.
The built-in advisor reads the whole transcript.
The fix is made after this round:
the context now holds the recent tool results, which $.session.messages() already gives in toolResults.
No run yet shows that it changes a result.
The two goal cases ran again with goal-ours on the fixed engine, with 10 runs each:
goal-two-checks 10/10 for 0.054 USD, and goal-first-violation 10/10 for 0.063 USD.
The folders are rerun-<case>-goal-ours.
With the first round, this shows parity with the built-in /goal on these two cases.
goal-blind-qa favours the engine by design, because the built-in /goal cannot run a check.
The default of advisor is now claudetto (commit b56351e0).
- On easy tasks all arms pass, and on hard tasks all arms fail, so
adviseis as good as the built-in advisor. - The first measure, at
efforthigh, was 0.63 USD against 1.19 USD for 10 runs ofadvice-audit, soadvisewas cheaper already (see "Cost ofadvise"). - Round 6 found that
effortlowand a short answer give the same recall for about 1/5 of the cost of the built-in advisor, so the defaultadvisorEffortislowand the system prompt asks for at most 10 lines. For 10 runs ofadvice-audit-web, the total is about $0.37 against $2.02. - With
claudetto, the slice setsCLAUDE_CODE_DISABLE_ADVISOR_TOOL=1atsession.startwith$.env.set, so that Claude sees one advisor.
On 2026-10-21, two claude -p runs with --advisor opus and --model haiku asked for the names of all tools that contain "advis".
The run with --plugin-dir plugins/claudetto (default options) gave mcp__claudetto__advise only.
The run with no plugin gave advisor.
So the variable that the slice sets at session.start turns off the built-in advisor.
The two runs cost $0.006.
The opus calls of advise are not in total_cost_usd.
This is a limit of Claude Code, not a defect of claudetto.
-
$.model.completeresolves the text and ausageof four token counts (ModelUsage). It gives no price, and the mod API has no call that adds a cost to the session. -
advisesends the token counts to the user with$.ui.log, in the line "advise: opus used N tokens in (R read from the cache, W written to the cache), M tokens out." The debug log holds every$.ui.logline (the typeUiLogOptionsin the mod API), soclaude --debug-file <path>captures them.tools/compare-builtins.tspasses--debug-filefor each run, andtools/advise-usage.ts(removed in 0.3.0) sums the lines and prices them. - The price is the price table that Claude Code uses for its own
total_cost_usd, read from the bundle as evidence only. The model catalog of Claude Code2.1.305(external/cc-2.1.305/strings.txt) has the entry ofclaude-opus-5-5(VERTEX_REGION_CLAUDE_5_5_OPUS) with the pricingtier_4_20_cache_read_0_20. That is $4 for one million tokens in, $20 out, $5 for a cache write, and $0.20 for a cache read. A run of the built-in advisor confirms it:modelUsagegave 22528 tokens in and 4309 tokens out for acostUSDof 0.176292, which is 4 and 20. The first version of this page took the entrykbof the same bundle ($8 and $40), which is a table for another path, and it was twice too high. -
advise-oursran with--model haikuand the advisor modelopus(claude-opus-5-5).
| Case | Runs | Passed | Session cost |
opus calls |
Tokens in | Tokens out |
opus cost |
Total | Built-in arm |
|---|---|---|---|---|---|---|---|---|---|
advice-audit, effort high
|
10 | 0/10 | 0.045 | 10 | 21390 | 24787 | 0.581 | 0.626 | 1.185 |
advice-audit-web, effort high
|
2 | 0/2 | 0.010 | 2 | 4299 | 5565 | 0.128 | 0.138 | 1.481 for 10 runs |
- At
efforthigh, one call used about 2100 tokens in and about 2500 tokens out, and no call read or wrote the cache. It cost about 0.06 USD, and the early guess was 0.035 USD. - So the first conclusion of this round, that
advisewas not cheaper, was wrong, because of the price.adviseatefforthighalready cost about half of the built-in advisor onadvice-audit.
The goal was the quality of the advice, the exact cost of the built-in advisor, and the cheapest advise at equal quality.
All arms ran advice-audit-web (6 faults), Haiku 5.5 as the working model, and opus as the advisor.
Each arm in the table ran 10 runs.
A first pass with fewer runs for each arm picked the levers, and the table holds the final measure.
- The built-in advisor gives an encrypted result (
advisor_redacted_result), so its advice text cannot be read. So the quality is measured onFINDINGS.txt, the list that Haiku writes after the advice, for all arms. A fault counts when a line of the file starts with the function and holds the words of its fault. For the arms withadvise, the advice text is graded too: the function and the words of its fault within 400 characters. - The words for each fault are in
FAULTSintools/advise-usage.ts, written before any result was graded, and a unit test covers the grader. - The tokens of the built-in advisor are the
modelUsageofclaude-opus-5-5in theresultevent of each run. Each run had one advisor call. ThethinkingTokensofopusare 0 there, andModelUsageofadvisehas no thinking count either, so thinking is inside the output tokens in both.
| Arm | Faults in FINDINGS.txt (of 6) |
Faults in the advice | Tokens in for a call | Tokens out for a call | Cost for a run (session plus opus) |
Cost for 10 runs |
|---|---|---|---|---|---|---|
| built-in | 4.8 | not readable | 23283 | 5160 | 0.202 | 2.02 |
advise, high (the first default), short answer |
4.7 | 5.0 | 2011 | 2128 | 0.056 | 0.56 |
advise, medium, short answer |
4.8 | 5.2 | 2107 | 1607 | 0.046 | 0.46 |
advise, low, short answer (the default now) |
4.9 | 5.3 | 2022 | 1196 | 0.037 | 0.37 |
- No arm passed a run, because each run missed one or two faults, so the comparison rests on the recall. The runs differ by at most 1 fault, so the noise of a mean of 10 runs is about 0.1 faults, and the four means are inside that noise.
- The built-in advisor sends the whole transcript with its own system prompt, about 23300 tokens, which is 11 times the context of
advise(about 2000 tokens). Its output is also 4 times longer. No cache was read or written in the advisor call. - The levers, in the order tried:
-
MAX_TOKENS(themaxTokensof$.model.complete): in a small trial at 1024 and at 700 the calls still gave more than the cap in output tokens (1670 on average at 700), because the cap bounds the text and not the thinking. The saving at 700 was 10% at most, and a cap can cut a piece of advice, so the bound stays 2048. -
effort: fromhightolowthe output fell from 2128 to 1196 tokens with no fall of the recall (in the first pass, with the long prompt, from 3266 to 1851).$.model.completehas no switch to turn thinking off. - A tighter
system.md("at most 10 short lines", with the function or file named for each point): atlowthe output fell from 1851 to 1196 tokens, and the recall held. - A cache mark on the stable prefix: the prefix is about 100 tokens, below the minimum that the API caches, and the logs show 0 tokens written to the cache. So it saves nothing now, and the mark stays because it costs nothing.
- A cheaper advisor model: not tried, because the cost of an
opuscall is already about 0.03 USD.
-
- The folders are in
round6/on the machine of the run.
- Both engines took
/goal <condition>in-p. In the two cases where the model may run the checks, it fixed all faults in the first stretch of work, and the arms were equal. - In
goal-blind-qathe engine of the plugin rannode check/qa.mjsat the end of each turn, and it started a retry turn with the failure. Each of 10 runs had 4 retry turns, and all 10 passed. The built-in/goalprinted "Goal set", and the model would not run the QA check. It stopped at the first turn, and 0 of 10 passed. The built-in/goalis not wrong to stop, because the model followed the rule of the project. But it shows that only the engine can run a check for Claude. - The case found a defect.
A condition that starts with a code span gave a
runcheck and ajudgecheck of the whole text. For ``node check/qa.mjsexits 0, the `run` check passed in the last turn, but the `judge` check failed, because the model could not run the command and its answer had no proof. The goal stopped by `stall` although the command exited 0. The first run of the case (before the fix) shows it: `results` held a passing `run` check and a failing `judge` check with the same time. - The fix is in
checksOf(slices/goal/domain/plan.tsatv0.2.0): the wordsboth,all,each,exits 0,passes,succeeds, andgreenno longer make a text ajudgecheck. The test was intests/claudetto/goal/plan.test.tsatv0.2.0, andGoal.mdhas the rule. After the fix, 10 of 10 runs passed. - The first-round cases
goal-two-checksandgoal-first-violationran before the fix. Their condition ("both exit 0") now gives tworunchecks and nojudgecheck, so the engine decides from the exit codes only. They were not run again.
- One working model (Haiku 5.5) and one Claude Code version.
- The
advisecases are at the ceiling, and the control passes them. -
goal-blind-qafavors the engine by design, because the project rule forbids Claude to run the check. - The grader
advisor-calledcounts calls, so it shows that the advisor ran, not that its advice was good. - The cost of
advise-oursonadvice-audit-webatefforthighin round 5 comes from 2 runs, and round 6 has 10 runs for each arm. - The arms differ in the call path of the advisor, so the turn counts can differ for that reason.
- Round 1: pilots about 0.4 USD, full runs 3.0 USD, and an unmeasured estimate for the
opuscalls ofadvise, which the measure of round 5 replaced. - Round 2: about 1.3 USD.
- Round 3: pilots about 1.7 USD, full runs 2.9 USD, and about 0.3 USD for the direct
opusreviews and an estimate for theopuscalls ofadvise. - Round 3 is about 4.9 USD, under its limit of 6 USD.
- Round 5 (the measure of
advise): about 1.5 USD, of which 1.42 USD is for theopuscalls. - Round 6 (the quality and the levers of
advise): about 5.6 USD for 80 runs, of which 2.0 USD is for the built-in advisor and 2.7 USD for theopuscalls ofadvise.
On the machine of the run, in /tmp/claudetto-evals/builtin-comparisons-0.2.0/.
The runs of round 3 are in round3/.
The folder is not in the repository.
Each case and arm has a folder full-<case>-<arm>/, and each pilot has a folder pilot*.
It holds summary.json, and for each run run-N/stream.jsonl and the workspace run-N/project/.
rest.log and advise-full.log hold the console lines.
claudetto
- Start
- Slices
- Add-Ons
- Reference
- Rationale
- Evidence
- Development
Claude Code