Prompt & Done · HEADROOM test kit

94% fewer tokens: where Headroom wins (and where it doesn't)

Headroom (free, Apache-2.0) compresses what your AI agent reads before the model sees it. When your own code sends a big log to Claude, it's dramatic: 94% fewer tokens, same answer. Inside Claude Code I measured about 2%. Run it yourself.

1 What I found (20 Oct 2026)

The win: a big log sent straight to Claude. I asked Claude (Sonnet 5.5) the same question about a 2,502-line server log, three times with the full log and three times with the log squeezed by headroom.compress() (94 lines kept). Full log: 149,481 input tokens and about $0.60 per question (API-equivalent, list price). Squeezed: 8,754 tokens and about $0.04. That's 94% fewer tokens. All six answers named the same first error and the same cause. One thing you lose: from the squeezed log Claude couldn't give the exact error count (it said so and pointed to the retrieve hash).

Use it in your own agent or script (Python, pip install "headroom-ai[all]"):

from headroom import compress

# messages: your Anthropic-style messages list, with the big log as a tool_result
result = compress(messages, model="claude-sonnet-5-5")
print(result.tokens_before, "->", result.tokens_before - result.tokens_saved)
messages = result.messages  # send these to Claude instead

Why Claude Code barely benefits: when a command prints too much, Claude Code saves the output to a file and shows the model a 2KB preview ("Output too large (252.9KB) … Preview (first 2KB)"), then greps it. Headroom also skips Read, Grep and WebFetch results by design. A cat of the same log inside Claude Code: 88,454 tokens without Headroom, 86,512 with it.

Everyday Claude Code (bug fix): same bug-fix prompt, 3 runs with headroom wrap claude and 3 without, interleaved. All six fixed the bug (641/641 tests). Input tokens per run (input + cache write + cache read) and Claude Code's API-equivalent cost:

direct 1191,027$0.1152
headroom 1179,977$0.1121
direct 2185,540$0.1129
headroom 2147,303$0.1019
direct 3187,020$0.1145
headroom 3222,823$0.1289
mean direct187,862$0.1142
mean headroom183,368$0.1143

That's 2.4% fewer input tokens and the same bill. The spread between runs is bigger than the difference, so treat it as "no meaningful saving on this task". Why: Claude Code already runs tail and grep instead of reading whole logs, so there was little left to squeeze.

Setup: Headroom 0.41.0, Claude Code 2.1.303, Sonnet 5.5, Max plan (headless claude -p). Three runs per arm is a small sample. Your workload may differ: big MCP/JSON outputs and agents you build on the API are where it's designed to help.

2 Install, run, undo

uv tool install --python 3.13 "headroom-ai[all]"
HEADROOM_BEACON=off headroom wrap claude
# undo:
headroom unwrap claude

Heads-up: an anonymous telemetry beacon is on by default (HEADROOM_BEACON=off turns it off). wrap registers an MCP server for Claude Code; unwrap removes it.

3 The bench (a shop API with one planted bug)

Make an empty folder, save these files, run python3 make_bench.py, then git init && git add -A && git commit -m bench. You get 641 tests (one fails), a 14,000-line config/discounts.json and a 20,000-line logs/app.log. The bug: the FALL25 code has "percent": "25%", so checkout totals become NaN.

make_bench.py
# Generates the scratch shop-api bench (run inside the repo dir). Seeded, deterministic.
import json, random, datetime, math, os
for d in ('config', 'test', 'logs', 'src'): os.makedirs(d, exist_ok=True)
random.seed(7)
words=["SPRING","SUMMER","FALL","WINTER","VIP","WELCOME","FLASH","CYBER","BLACK","LOYAL","STUDENT","BDAY","FRIEND","APP","EMAIL","HOLIDAY","NEW","BACK","TEAM","GIFT"]
pcts=[5,10,15,20,25,30,40,50]; sufx=["","A","B","X","26","NY","LA"]
allc=[w+str(p)+s for w in words for p in pcts for s in sufx]
random.shuffle(allc)
codes=[]
for c in allc[:900]:
    if c=="FALL25": continue
    codes.append({"code":c,"percent":random.choice(pcts),"active":random.random()>0.25,
      "created":f"2026-{random.randint(1,9):02d}-{random.randint(1,28):02d}","owner":random.choice(["growth","crm","retail","partnerships","support"]),
      "min_subtotal":random.choice([0,0,25,50,100]),"max_uses":random.choice([100,500,1000,5000,None]),"uses":random.randint(0,4000),
      "channels":random.sample(["web","ios","android","pos","email"],k=random.randint(1,4)),"notes":random.choice(["","seasonal","partner campaign","do not extend","migrated from v1"])})
codes.insert(617,{"code":"FALL25","percent":"25%","active":True,"created":"2026-10-14","owner":"growth","min_subtotal":0,"max_uses":5000,"uses":1843,"channels":["web","ios","android"],"notes":"fall campaign, imported from sheet"})
json.dump({"version":42,"updated":"2026-10-14T09:12:44Z","codes":codes},open("config/discounts.json","w"),indent=2)
act=[c for c in codes if c["active"] and c["code"]!="FALL25"]
L=["import { test } from 'node:test';","import assert from 'node:assert/strict';","import { checkoutTotal, applyDiscount } from '../src/pricing.js';",""]
for i in range(600):
    c=random.choice(act); sub=round(random.uniform(5,400),2)
    exp=math.floor(sub*(1-c["percent"]/100)*100+0.5)/100
    L.append(f"test('discount {c['code']} on {sub}', () => {{ assert.equal(applyDiscount({sub}, '{c['code']}'), {exp}); }});")
L+=["test('checkout with FALL25 (fall campaign)', () => {","  const total = checkoutTotal([{ price: 40, qty: 2 }, { price: 19.99, qty: 1 }], 'FALL25');","  assert.equal(total, 80.99);","});"]
for i in range(40):
    c=random.choice(act)
    L.append(f"test('checkout {i} with {c['code']}', () => {{ assert.ok(checkoutTotal([{{ price: 10, qty: {i+1} }}], '{c['code']}') > 0); }});")
open("test/pricing.test.js","w").write("\n".join(L)+"\n")
t=datetime.datetime(2026,10,14,6,0,0); out=[]
paths=["/api/cart","/api/products","/api/checkout","/api/user","/api/search","/healthz"]
for i in range(20000):
    t+=datetime.timedelta(milliseconds=random.randint(200,1500))
    rid="%08x"%random.getrandbits(32); p=random.choice(paths)
    if p=="/api/checkout" and t>datetime.datetime(2026,10,14,9,13) and random.random()<0.35:
        out.append(f"{t.isoformat()}Z ERROR req={rid} POST /api/checkout 500 code=FALL25 err=\"checkout total is NaN\" ms={random.randint(3,30)}")
    else:
        lvl=random.choices(["INFO","DEBUG","WARN"],[70,25,5])[0]
        st=200 if lvl!="WARN" else random.choice([404,429])
        out.append(f"{t.isoformat()}Z {lvl} req={rid} {random.choice(['GET','POST'])} {p} {st} ms={random.randint(2,400)} ua=\"{random.choice(['ios/5.2','android/5.1','web/chrome','web/safari'])}\" region={random.choice(['us-east','us-west','eu-west'])}")
open("logs/app.log","w").write("\n".join(out)+"\n")
src/pricing.js
import { readFileSync } from 'node:fs';

const discounts = JSON.parse(readFileSync(new URL('../config/discounts.json', import.meta.url)));

export function findDiscount(code) {
  if (!code) return null;
  return discounts.codes.find((d) => d.code === code.toUpperCase() && d.active) || null;
}

export function applyDiscount(subtotal, code) {
  const d = findDiscount(code);
  if (!d) return subtotal;
  const pct = Number(d.percent);
  return Math.round(subtotal * (1 - pct / 100) * 100) / 100;
}

export function checkoutTotal(items, code, taxRate = 0.08) {
  const subtotal = items.reduce((s, it) => s + it.price * it.qty, 0);
  const discounted = applyDiscount(subtotal, code);
  const total = Math.round(discounted * (1 + taxRate) * 100) / 100;
  if (Number.isNaN(total)) throw new Error('checkout total is NaN');
  return total;
}
package.json
{
  "name": "shop-api",
  "version": "1.4.2",
  "private": true,
  "type": "module",
  "scripts": { "test": "node --test --test-reporter=spec" }
}

4 The A/B script

ab.sh
#!/bin/bash
# A/B: plain Claude Code vs `headroom wrap claude`, 3 runs each, interleaved, same prompt.
# Needs: claude (Claude Code), headroom (uv tool install --python 3.13 "headroom-ai[all]"), node, a pristine copy of the bench in ./pristine
Q='Checkout has been failing for some customers since Oct 14 and npm test is red. Use the test output and logs/app.log to find the root cause, fix it properly, and make npm test pass. Finish with a two-line summary of the cause and the fix.'
export HEADROOM_BEACON=off DO_NOT_TRACK=1
for n in 1 2 3; do
  rm -rf runs/direct-$n runs/wrap-$n; mkdir -p runs
  cp -R pristine runs/direct-$n; (cd runs/direct-$n && claude -p --model claude-sonnet-5-5 --allowedTools "Bash Read Edit Write Grep Glob" --output-format json "$Q" < /dev/null > ../direct-$n.json)
  cp -R pristine runs/wrap-$n;   (cd runs/wrap-$n && headroom wrap claude --code-memory none -- -p --model claude-sonnet-5-5 --allowedTools "Bash Read Edit Write Grep Glob mcp__headroom__headroom_retrieve" --output-format json "$Q" < /dev/null > ../wrap-$n.json)
done
# input tokens = usage.input_tokens + cache_creation_input_tokens + cache_read_input_tokens; cost = total_cost_usd
headroom unwrap claude
squeeze.py (the giant-log test)
# pip install "headroom-ai[all]"   (run with HEADROOM_BEACON=off)
from headroom import compress
log = open('slice.log').read()          # 2,500 lines around the first error in logs/app.log
msgs = [{"role": "user", "content": "why did checkout fail?"},
        {"role": "assistant", "content": [{"type": "tool_use", "id": "t1", "name": "Bash", "input": {"command": "cat slice.log"}}]},
        {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t1", "content": log}]}]
r = compress(msgs, model="claude-sonnet-5-5")
print(r.tokens_before, '->', r.tokens_before - r.tokens_saved)   # we got 112,029 -> 4,310