Benchmark method

I threw my first benchmark away. This is the one that replaced it.

This page is the method I used to measure whether coldstart actually saves tokens, and the things that went wrong on the way. My first attempt ran on Kafka, Django and Mastodon, and the numbers it produced were wrong in a way I couldn't see until I went looking. The rebuild is now a separate tool you can point at your own repository, so you can check a skill, a rules file or an MCP server without taking my word for anything.

← back to the overview

What gets measured: finding the files, not finishing the job

A session has two halves. The agent works out which files matter, and then it reads, reasons and edits them. Only the first half is what a navigation tool touches. The second half is the same work whichever setup you're running, so measuring it adds the same cost to both sides and makes the difference between them look smaller than it is. So every question here asks the agent to find files, and stops there.

The task Every prompt asks where something lives, not for a fix. The answer is a list of file paths, written to a known location, because an agent handed a bug report will start editing if you let it.
Ground truth The source files the pull request actually changed, written down before either arm runs. Nothing is graded by hand afterwards, so I can't decide what counted once I know which side did better.
Reported Tokens and turns across the whole session, and how many of the right files the agent named. Both, always. A tool that reports only cost is rewarded for making the agent give up sooner.
Why the whole session. Around 90% of what a session costs is cached context replayed on every turn. Shrink what one command returns and the agent can just ask for more of it, so a tool can make its own output smaller and your bill bigger at the same time. I wrote about that at length in why most token savings tools lie.

The first attempt, and why the numbers were fiction

The first version was straightforward. I pointed an agent at a repository, let it skim the code and read commit messages, and had it write questions from what it found. Then I ran those questions on Kafka, Django and Mastodon. Large, real, well organised codebases. They are also three of the most written-about projects on the internet, and that turned out to matter more than anything about how they're built.

Claude has read all three. So the agent would take a question, already know roughly where the answer lived, and go straight to coldstart with the exact identifiers that happen to match the real filenames. It would get the right files back, read them, read the neighbours to be sure, and produce a correct answer. That looks like the tool working. It isn't. The agent was using the tool to confirm what it already remembered, and the questions themselves had been written by an agent that had read the code, so they pointed at the answer too.

You can test for this in your own results without rerunning anything. Take the arm with no tool installed, look at its very first move, before it has opened a single file, and pull out the internal class names it used. Subtract anything the prompt handed it. Whatever is left didn't come from the codebase, because the agent hadn't looked at the codebase yet.

Kafka 21 of 25 sessions (84%) named internal class names before opening a file.
Django 7 of 14 (50%).
Mastodon 6 of 18 (33%).

This isn't noise that averages out. It pushes the result one way. Knowing the codebase makes the no-tool arm cheaper, because an agent that already knows where to look skips the searching it would otherwise pay for. My savings on those three ranked Mastodon 43%, Django 28%, Kafka 15%, which is the leak rates above in reverse. I had been reading that ranking as something about languages and codebase structure. Most of it was about what the model had already read.

Renaming the codebase doesn't fix it. Stopping the model recognising a project needs consistent rewrites across declarations, imports, build config and tests, and a half-done rename leaves the no-tool arm grepping for names that no longer match, which corrupts the measurement rather than cleaning it. So I retired those three repositories. The old numbers aren't anywhere on this site, because I don't believe them.

The rebuild: questions from issues, answers from commits

Two things had to change. Smaller, less written-about repositories, so the model isn't recalling instead of searching. And questions written by someone who didn't know the answer.

Issue reports solve the second one on their own. An issue is filed before a solution exists, by someone describing a symptom, so it uses the project's own vocabulary without pointing at a file. Nobody had to be careful about it. It's just who was holding the pen and when. Commit messages and pull request descriptions are the opposite: written afterwards, by the person who did the work, naming classes and paths without thinking about it. That's what my first version was built on.

The answer comes from the same place the question does. Every issue I use is closed by a merged pull request, and the source files that pull request changed are the files a correct answer had to name. That's ground truth taken from the repository's own history rather than from my judgement, and it works on any repository with git history, including a private one.

1 · select Closed issues whose pull request was merged, touching between two and eight source files. One-file fixes are just a grep. Fifty-file refactors make the overlap score meaningless.
2 · scrub Take out file paths, stack frames, code blocks, screenshots and issue numbers. What's left should still say what's broken, and not where it lives.
3 · reshape Turn the report into a question about where something lives, keeping the author's words and adding none of my own. The original and the rewrite are both saved, so anyone can check what changed.
4 · drop Throw away anything too vague to navigate from, and anything that lost most of itself in the scrub, which is what happens when an issue was really just a pointer to a file.

A lot of candidates don't survive that, and on one repository more were thrown out than kept. Each one I drop is recorded with the reason, because a list of what was removed is the only way you can tell that what's left wasn't picked to make the result look good. The whole procedure is now its own repository, coldbench, since it has nothing to do with coldstart and shouldn't live inside it.

The result

Two repositories, run more than once each. Arches is a Python and Django application. JMRI is Java, considerably more verbose per file and with a heavier build surface. Two arms in each: plain Claude Code with the search tools it ships with, and the same thing with coldstart installed and nothing else changed. Same questions both sides, Sonnet 5 throughout, one fresh session per question.

Arches Python / Django
−64%
tokens against the no-tool baseline, over 27 questions. Recall came out two points better, because the baseline sometimes ran out of turns before it found the file at all.
JMRI Java
−31%
tokens over 25 questions, recall level with the baseline. The first run said 39%, on a machine that was busy with other work. Rerun in isolation it came out eight points lower, so 31% is the number.

Both figures are the CLI, which is how coldstart is meant to be run. Those two also disagree, and I'm not going to average them. One tidier number would hide the only interesting thing here, which is that Java's verbosity and build tooling change what a single turn costs, and that changes how much a tool which cuts turns can save you.

Both question sets, and the files a correct answer had to name
  • arches · 32 questions, 123 gold files. The figure above is q01 to q27; the last five were generated by the same pass and never run.
  • jmri · 25 questions, 93 gold files.

Every question is one file of plain text with the issue number recorded beside its gold set, so you can open the original report and check the rewrite against it. Each is tagged by how far the fix spread, from one area to across subsystems, because a tool that helps on the narrow ones and not the wide ones is making a different claim than its average suggests.

This is a claim about turns. Cost compounds across turns, because every turn replays everything before it. So the saving lives in questions that would otherwise have taken a long search. On something an agent can settle in two or three turns there is very little to take away, and coldstart won't show you much. That mechanism is the subject of where the tokens actually go.

Run it on something that isn't mine

None of this is specific to coldstart. The same setup measures a skill, a rules file, an MCP server, or a change you made to your own prompt. Take two copies of one repository, install the thing you're testing in exactly one of them, ask the same questions in both, and score what comes back against the commits.

The harness coldbench holds the method and the scoring. It takes the answers from both arms plus your session transcripts and reports recall and tokens together, along with flags for the three things that quietly ruin a comparison. Generating questions from your own repository is written up as a procedure and not yet automated, so the quickest start is to borrow one of the two sets above.
The token count convotokens reads your own transcripts off your own disk and reports tokens and turns for the whole session, subagents included. It makes no network calls and won't invent a dollar figure for you.
One prompt, one session Otherwise your third question is cheap because the first two already loaded the context, and what you've measured is the order you asked things in.
Repeat before believing Agent runs vary on their own. Run one side several times without changing anything and look at how much it moves, before you believe a difference between the two sides.
Your own private codebase is the best thing to run this on. The problem in the first section can't happen in a repository the model has never seen, and the ground truth is sitting in your git history already. If you're deciding whether a tool is worth it for your team, your own closed code gives you a cleaner answer than any public benchmark will.

What this does not tell you

Not whether the fix was goodThis measures finding the right files. Whether the patch that followed was any good is a different experiment, and I haven't run it.
Not every file in a commitA pull request can touch files that weren't really part of the answer. That costs both arms the same, so it moves the scores without moving the gap between them.
Not your codebaseTwo repositories, two languages, and they already disagree by 33 points. Your layout and conventions will move the number again, in either direction.
Not a billThese are token counts, not dollars. On a subscription any conversion to money is something I'd be making up.

Two longer pieces sit behind this method, both about the ways a benchmark misleads the person running it: why most token savings tools lie and how your own benchmark lies to you first.