Token test: with a recipe vs without
We gave a model 20 coding tasks, once with just the task and once with the matching ProvenPin recipe. Here is what happened, including the parts that don't look good for us.
The short version
Recipes did not cut total tokens. With a recipe, the model used 6.9% more tokens in total, because the recipe itself adds input. But it wrote far less: output tokens dropped 63%. Output costs more than input, so the runs with a recipe cost about 45% less, ran about 2.4x faster, and needed fewer fixes (2 vs 8).
Side by side
| Task only | Task + recipe | |
|---|---|---|
| Passed in the end | 60/60 | 60/60 |
| Passed on the first try | 52/60 | 58/60 |
| Fix attempts | 8 | 2 |
| Total tokens | 314,215 | 335,994 |
| Input tokens | 89,765 | 252,953 |
| Output tokens | 224,450 | 83,041 |
| Cost (all 60 runs) | $0.121 | $0.067 |
| Median model time per run | 14.2 s | 5.8 s |
60 runs per side. Cost uses the model's list price: $0.10 per million input tokens, $0.50 per million output tokens.
How we ran it
- Tasks: 20 (10 Python, 10 TypeScript). They were picked by a fixed rule before any model call: for each of the most common library stacks in our recipes, the first recipe for that stack in alphabetical order. One task was swapped before any run, because its reference recipe would not install on our test machine.
- Model:
claude-haiku-5-5, the same on both sides. Its temperature can't be set, so every task ran 3 times per side. That's 120 runs, and all of them are reported. - Task only: the task text, the pinned versions, the files to write, and the recipe's test files.
- Task + recipe: the same, plus the top result from our MCP server's
search_recipesfor that task, fetched withget_recipe. The recipe counts as input tokens. - Grading: the model's files were run against the recipe's own tests, installed on its pinned versions. If tests failed, the model got up to 2 fix attempts with the failing test output. Same rule on both sides. Tokens from every call are counted.
- When: October 17, 2026, between about 6:24 and 6:55 PM Alaska time.
Where it didn't help
- With a recipe, total tokens were lower on only 5 of 20 tasks. On the other 15, the recipe added about 2,100 to 4,200 input tokens per run, and the output it saved didn't cover that.
- On
fastify-json-schema, search returned the wrong recipe all 3 times. Those runs still passed, but used more tokens than the task alone, and it was the one task where output didn't go down. - On easy tasks, the model was already cheap without help, so the recipe mostly added input.
Limits
- Small sample: 20 tasks, 3 runs each, one model, one day.
- The tasks come from our own recipes and are graded by our own tests. With a recipe, the model gets a solution that already passes, so its pass rate is close to the ceiling by design. This measures what it costs to reach passing code, not general coding skill.
- The task text is the recipe's own title and summary, so search had it easy. Real users will word things differently.
- Both sides saw the test files. That makes things easier for the task-only side.
- Without a recipe, billed output tokens were much higher than the visible reply text. That's likely the model's internal reasoning being billed as output. We haven't verified it.
- Model time leaves out the 0.5 to 1 second it took to fetch the recipe.
Every task
Median tokens over 3 runs per side. Green means the recipe side used fewer total tokens.
| Task | Lang | Total tokens | Output tokens | Fixes | Right recipe | ||
|---|---|---|---|---|---|---|---|
| Task only | + Recipe | Task only | + Recipe | only vs + | found | ||
| ajv-formats-json-schema | TypeScript | 3,381 | 4,017 | 2,587 | 1,058 | 1 vs 0 | 3/3 |
| better-sqlite3-zod-transactions | TypeScript | 6,561 | 7,392 | 5,093 | 2,093 | 1 vs 0 | 3/3 |
| celery-beat-schedule-crontab | Python | 5,262 | 4,213 | 4,466 | 1,026 | 1 vs 0 | 3/3 |
| django-celery-eager-signals | Python | 13,807 | 6,632 | 9,840 | 1,432 | 3 vs 0 | 3/3 |
| drizzle-pglite-migrations | TypeScript | 5,436 | 6,490 | 4,338 | 1,867 | 0 vs 0 | 3/3 |
| express-helmet-rate-limit | TypeScript | 2,370 | 3,770 | 1,474 | 768 | 0 vs 0 | 3/3 |
| fastapi-argon2-login | Python | 2,726 | 5,457 | 1,423 | 1,120 | 0 vs 0 | 3/3 |
| fastify-json-schema | TypeScript | 2,017 | 4,772 | 1,362 | 1,490 | 1 vs 1 | 0/3 |
| flask-jwt-extended-auth | Python | 2,975 | 4,234 | 2,178 | 930 | 0 vs 0 | 3/3 |
| graphql-js-schema-execute | TypeScript | 4,024 | 5,896 | 2,876 | 1,571 | 0 vs 0 | 3/3 |
| hono-jwt-auth | TypeScript | 2,520 | 4,085 | 1,652 | 982 | 0 vs 0 | 3/3 |
| httpx-pydantic-typed-client | Python | 3,849 | 4,954 | 2,694 | 1,093 | 0 vs 0 | 3/3 |
| i18next-react-ssr | TypeScript | 4,363 | 5,274 | 3,229 | 1,347 | 0 vs 0 | 3/3 |
| jsonwebtoken-hs256-rs256 | TypeScript | 7,322 | 8,101 | 4,975 | 1,522 | 0 vs 1 | 3/3 |
| pandas-openpyxl-excel-report | Python | 5,305 | 5,756 | 4,242 | 1,862 | 0 vs 0 | 3/3 |
| pydantic-annotated-after-validator | Python | 5,273 | 5,021 | 4,344 | 1,316 | 0 vs 0 | 3/3 |
| pydantic-settings-dotenv-secrets | Python | 4,153 | 6,024 | 2,841 | 1,577 | 0 vs 0 | 3/3 |
| rich-typer-progress-cli | Python | 7,332 | 5,399 | 6,370 | 1,560 | 0 vs 0 | 3/3 |
| sqlalchemy-aiosqlite-session-factory | Python | 2,580 | 4,797 | 1,396 | 674 | 0 vs 0 | 3/3 |
| zod-env-dotenv-config | TypeScript | 5,113 | 4,730 | 4,050 | 1,284 | 1 vs 0 | 3/3 |
Raw data
One row per run: task, side, pass or fail, fix attempts, tokens, cost, time, and which recipe search returned. No prompts or code.