Teardownspublic

Headroom

It started as an adoption review. Measuring my own environment, the thing this tool would shrink didn't exist.

No tool came in, no technique came in, and what stayed is one convention. The headline “60–95% compression” is measured on JSON arrays; coding work sits at 15–20%, and source code isn’t a compression target at all — it’s passed through by design. Then, measuring my own environment, cache reuse was already at 92.5–96.3%, leaving no room for this tool’s main benefit, and there was no large payload anywhere on the LLM path to compress. This investigation only ended after the verdict reversed twice.

agent — Claude Code · Cursor · Codex · your own code prompts · tool output logs · retrieval results · files Headroom — runs locally data never leaves the machine LLM provider compressed prompt + a retrieval tool it classifies the content and routes it to a different compressor structured data statistical summary + outliers prose condensed by a separate model source code passed through — untouched retrieved documents passed through — untouched The clever part is that it's reversible — the original stays local and the model can pull it back when it needs to
Four ways to attach it: library, proxy, agent wrapper, MCP server. Python and Rust, Apache-2.0, local-first.

What the tool does

A layer that sits between the agent and the LLM and routes outgoing context to different compressors by content type. Tool output, logs, retrieval results, files.

The smartest thing in the design is that it’s reversible. It compresses but keeps the original cached locally, and if the model decides it needs the original it pulls it back through a retrieval tool. That’s what makes lossiness survivable.

There are four ways to attach it, and they differ sharply.

Mode Character
Library Inserted directly into your code. Maximum control
Proxy Zero code changes. But all traffic passes through it → a single point of failure
Agent wrapper Starts the proxy and injects the config in one command
MCP server Called only when needed. The safest place to start

63,331 stars against 4,807 forks, six months old. A 13-to-1 fork ratio means people are actually cloning and using it, not just watching. That said, 592 open issues with only 115 labelled means triage isn’t keeping up with intake.

The gap between the headline and my actual work

The official limitations document is unexpectedly honest — a far more reliable source than the README.

compression by content type — from the official limitations doc arrays of JSON objects 86–100% structured logs 82–95% long agent conversations 56–81% prose · documents 43–46% short conversations median 4.8% — effectively nothing source code passed through — not compressed, by design The headline “60–95%” is the top two rows. Sessions that read and edit code live in the bottom two
The same tool ranges from 100% to 0% depending on content type. Which row you're on is the entire adoption decision.

Passing code through isn’t a bug, it’s the intent. There is a code compressor, but three safety layers keep it from firing in practice — short spans are skipped silently, the code in recent messages is never touched, and if the last user message contains words like “analyse,” “review,” “fix” or “debug” it protects the code in the entire conversation.

Which means a “read this code and fix it” session compresses almost nothing. The docs say so themselves. It’s the right default — and it’s exactly where the gap between the headline number and real coding work opens up.

What broke — compression was 0% and the bill went up 7×

A third party ran a controlled A/B on real traffic and attached the billing receipt. The only variable was whether the proxy was in the path.

uncached input per call total cost
Direct ~2 $0.46
Through the proxy ~3,000 $3.19 (6.9×)

And in the same report, the savings rate was 0.00% on all seven runs. Compression never fired at all, and the bill was still 7×. So the cause isn’t over-compression, it’s plumbing.

The mechanism: a prompt cache is only reused when the leading bytes are byte-identical. Change that prefix every turn and instead of reading the cache you write it again each time — and writing costs more than reading, so the “saving” becomes a surcharge.

There’s a worse detail. The proxy’s own logs were reporting cache hits as true. The bill and the dashboard were pointing in opposite directions.

Even so, “confirmed loss” is wrong. That measurement came from one version, a cache defect was closed afterwards, and nobody has re-measured the current one. The accurate state is “neither benefit nor loss has been measured” — and blurring that distinction isn’t allowed.

There’s a finding in the opposite direction too. Reading the compression engine’s source, there is no randomness in it at all — same input, same output. The unseeded randomness that caused trouble is in the benchmark harness, not the engine. Fail to separate those two and it reads as “the engine can’t be trusted either.”

And then my own first pass broke

This is the most valuable part of the investigation. The first pass measured nothing. It read the repository’s own documents and a third-party issue report and relayed them. I was criticising claims with no reproduction while standing on claims with no reproduction.

  • I named the wrong mechanism. “Re-serialisation breaks the cache” was my inference — and that defect had been closed 18 days before the reporter’s version. I attached a plausible mechanism without verifying it, which is the same class of mistake I was accusing the repository of
  • I presented “2–7×” as the current state. One version, one report, and I put the magnitude in the conclusion
  • “Two people confirmed it” was a confirmation of different symptoms. I made the evidence look thicker than it was
  • I used issue counts as a scare. Strip duplicates, hearsay and test reports and two families actually applied
  • I applied scepticism to one side only. I discounted the vendor’s flattering table for having no reproduction, then quoted the same vendor’s unflattering figure at face value. Same source; both deserve the same doubt

The last one stung most. The moment you pick which side to doubt, you’ve already picked the conclusion.

Measurement ended the investigation

Passes one and two were entirely other people’s numbers. On the third I measured my own environment, and the reason to evaluate this tool disappeared.

The cache was already at ceiling. Session logs record cache reads, writes and uncached input per call — the receipts from past runs were already on disk; nothing had to be re-run.

Session Calls uncached in / call Reuse
Main 116 1.8 92.5%
Large (119 MB) 5,388 300.7 95.9%
Three subagents 98–121 2.0 96.0–96.3%

The controlled experiment above cited ~2 tokens per call as its “normal” baseline. Mine is also ~2. There is nothing for cache alignment to gain, and the item I’d written down as “first priority, biggest win” was already satisfied.

And there was no target to compress. Large logs and data files exist — but code condenses all of them before they go up. There is no point on the LLM path where a large payload enters.

Verdict — 0 tools, 0 techniques, 1 convention

Neither adoption nor technique harvest. One convention was adopted.

Don’t invent values you can’t measure. Attach a confidence interval and an [estimated] label to estimates, and to actually measure, hold back 10% as an untouched control.

That this convention came from this repository is the irony. It ships a design for reporting counterfactual savings honestly, confidence intervals and all — while its own headline table has no reproduction and its dashboard pointed the opposite way from the bill. Having a measurement design and applying it to your own banner are two different things — and the first draft of this analysis fell into exactly the same trap.

The resume condition is on the record. When a point appears where a large payload goes into an LLM whole, or when someone re-measures the current version. A rejection with no condition makes you repeat the same investigation six months later.