Measure the agent loop's invisible taxes — from transcripts you already have.
Coding agents re-send their entire conversation on every model call. Prompt caching makes that affordable — until it silently doesn't: caches expire while you grab lunch, compacted context gets re-read back in, the same file is re-fetched twenty times, and your usage limits drain in ways no dashboard explains (anthropics/claude-code#16157, #38335 — thousands of users report drain they cannot attribute).
looptax is an offline analyzer that reads agent transcripts (Claude Code
session JSONL) and quantifies four cost mechanisms that no accounting tool
reports:
| Tax | Question it answers |
|---|---|
| Replay | What share of everything the model processed was re-sent history? How many times was the average token (re)processed? |
| Idle | How much did resuming sessions after cache TTL expiry cost, versus the counterfactual where the cache survived? |
| Amnesia | How many tokens were spent re-reading files the context already had — including content thrown away by compaction? |
| Retry | How often did mid-session API errors force re-processing? |
Zero API calls, zero cost, local-only: it parses files on your disk and never uploads anything.
Snapshot from 2026-07-27: 95 real Claude Code transcript files (87 conversation streams, 2,444 API requests, ~308M processed input tokens). Reproduce the method with one command against your own transcripts — see Usage.
- 96.2% of all input tokens the model processed were re-sent cached content. Only ~0.01% of processed input was genuinely new (uncached) text. The agent loop processed ~34x more input than the peak context size — i.e. the average context token was paid for (mostly at the cheap cache-read rate) roughly thirty-four times.
- The loop reads ~250 input tokens for every token it writes (input:output = 252:1). Agent cost intuition built on chat usage ("output is what's expensive") inverts: at this ratio, input-side handling — cache hygiene, context size — dominates spend.
- 1.3% of requests caused 22% of all cache writes. Requests that resumed a session after a 5-minute-to-1-hour idle gap (31 of 2,444) accounted for over a fifth of total cache-write volume — the single most concentrated waste mechanism found, and one no per-request dashboard surfaces.
- 9.5% of file Reads were redundant re-reads of a file already read in the same conversation.
Numbers above are from looptax report --format md on the author's machine
(single-user corpus; see Limitations).
pip install "git+https://github.com/ahian-lee/looptax.git"Or from source:
git clone https://github.com/ahian-lee/looptax
cd looptax
pip install .# Analyze your entire Claude Code history (~/.claude/projects)
looptax report
# Analyze specific files or directories
looptax report path/to/session.jsonl path/to/project-dir
# Machine-readable / markdown output
looptax report --format json
looptax report --format mdSample output (truncated):
looptax report
============================================================
corpus: 95 files, 87 streams, 2444 requests (3039 duplicate records ignored)
[1] replay tax - history re-transmission
input processed 307,772,990 tokens
cache reads 295,925,133 (96.2%)
...
- Usage comes from provider-reported fields, not tokenizer guesses: every
request's
input_tokens,cache_read_input_tokens, andcache_creation_input_tokens(with the 5m/1h TTL split) are read from the transcript. Requests split across multiple JSONL records are deduplicated by request id — usage counted once, tool calls collected from all records. - Idle tax classifies each request by the gap since the previous request in the same conversation stream (<5m / 5m–1h / ≥1h). Cache writes after a ≥5m gap are candidate TTL-expiry rewrites; the primary estimate is capped at the previous request's cached footprint (only content demonstrably in cache can have expired), with the uncapped number reported as an upper bound. The counterfactual prices those tokens at the 0.1x cache-read rate instead of the 1.25x/2x write premium.
- Amnesia tax detects repeat
Readcalls per file per stream and sizes the waste from the repeat reads' actual tool_result payloads (chars/4, labeled an estimate). Compaction ROI is only reported when the corpus contains compaction events — zero events is reported as no data, never as "no waste". - Costs use a dated Anthropic price snapshot with documented cache multipliers (read 0.1x, 5m write 1.25x, 1h write 2.0x). Tokens from models the snapshot cannot price are surfaced as a warning — never silently costed at $0.
The tracking layer is well served; use these tools for it, not looptax:
- ccusage and CodeBurn — daily/session cost accounting across agent CLIs (how much you spent).
- llmprof and contextspy — live proxies that decompose the context window of traffic you route through them.
- tokenscope — per-session context treemaps for Claude Code.
looptax is the measurement layer: it answers why the spend happened —
mechanisms (TTL expiry, re-transmission, re-reads) that only appear when you
analyze whole conversations offline, across their real timestamps — and ships
with a reproducible method and corpus-level findings rather than only code.
- Findings above are a single-user corpus (n=1, 95 files). They are the first published data point for these mechanisms, not a population estimate. Run it on your own corpus; if you'd like to contribute anonymized aggregate numbers (the JSON report contains no message content), open an issue.
- Dollar totals are API-equivalent estimates at dated Anthropic list prices, not the amount a Claude Pro/Max subscriber was billed. Provider, geography, negotiated discounts, and temporary promotions can change actual spend.
- The idle-tax estimate cannot distinguish TTL expiry from harness-side prefix invalidation; the cap against the prior cached footprint bounds, but does not eliminate, attribution error. Both bounds are always shown.
- Re-read waste uses a chars/4 token approximation (provider tokenizers are not public); usage-field metrics (replay, idle) do not use approximations.
- Compaction ROI requires a corpus containing compaction events; current
Claude Code versions summarize without emitting
compact_boundarymarkers, so this chapter reports honestly when it has no data. - One ingest format today (Claude Code JSONL). The
ingest/package is adapter-structured; SWE-agent/OpenHands trajectory support is the next milestone.
pip install -e ".[dev]"
pytestTest names state the failure they prevent (e.g.
test_idle_estimate_capped_by_previously_cached_footprint,
test_unknown_model_not_silently_priced_at_zero) — the test suite is
specifically hostile to bugs that would make results look better than
reality.
MIT