← Back to all posts
Tools

Spotify Stopped Letting Claude Read Files. Tokens Fell 90%.

September 5, 2026 · 04:08 UTC · Tools
Spotify Stopped Letting Claude Read Files. Tokens Fell 90%.

TL;DR

On September 3, Spotify's engineering blog published shunt, an Apache-2.0 Claude Code plugin built on a rude premise: your coding agent should not be allowed to read large files. A PreToolUse hook blocks any Read over 350 lines and redirects it to a script, which ships the file to a cheap worker model and returns bullets. Measured on a 162,000-line Java monorepo, a source-and-test pair that would have dumped 75,990 tokens into context came back at 4,148. Mean saving across the bulk-read scenarios: about 90%.


The premise: most of what an agent does is not thinking

Author Dimitri Mazmanov, a principal product manager at Spotify, states the thesis in one line: "Most of what an AI coding agent does for me isn't thinking. It's I/O."

That is the whole argument, and it holds up if you have ever watched a session burn context. The agent reads a 4,000-line service class to answer one question about which methods touch the database. It reads a test file to copy its shape. It reads three files across two services to work out a call path. None of that needs frontier reasoning. All of it gets billed at frontier rates, and all of it crowds out the context you actually wanted the model to hold.

The economics behind the post are not hypothetical. Mazmanov writes that a quarter of engineering leaders already burn $200 to $500 per developer per month on tokens, with some well past $2,000, and cites Gartner's June 24 prediction that AI coding costs will pass the average developer's salary by 2028. Gartner's Nitish Tyagi framed the reason bluntly in that release: token discipline will not arrive through developer choice, because developers optimize for speed and convenience over cost.

So Spotify took the choice away.

How the interception works

shunt is three layers, ordered from hard gate to soft suggestion. Hooks block. Scripts execute. Skills explain.

one large file read, intercepted Read Service.java4,014 lines PreToolUse hookover 350 lines: block worker model reads5,737 tokens back Claude never sees the file, only the answer to the question it asked.
The hook is a hard gate, not a suggestion the model can talk itself out of.

Two hooks do the gating. check-file-size fires on every Read and blocks anything over SHUNT_MIN_LINES, which defaults to 350. check-bash-read catches the obvious escape route, cat, head, tail, less and more pointed at a big file. Both are deliberately narrow. Targeted reads with an offset or limit go through. Piped commands go through, because cat file | grep is already a targeted read. Redirections go through, because writing to a file does not consume context.

When a read is blocked, Claude calls a script with named arguments rather than assembling a bash pipeline from prose. bulk-read takes a question and a list of paths, wraps each file in XML tags for clean boundaries, and sends one invocation to a worker mode. code-write takes a spec, a required reference file and an optional target path, then strips the markdown fences off whatever comes back. The reference is mandatory by design: without a file to pattern-match against, the worker would produce context-free code that fits nothing in the repo.

The mental model is an intern sent to the archive. They come back with a page of notes, not the filing cabinet.

The numbers, and where they came from

The repo ships its benchmarks rather than describing them, which is more than most plugin READMEs manage. Four scenarios, run against a 162,000-line Java monorepo, with Gemini 2.5 Flash as the worker model in the published examples.

tokens entering Claude's context, before and after one 4,014-line source file 33,684 direct 5,737 shunted, 82% less source and test pair, 7,408 lines 75,990 direct 4,148 shunted, 94% less three files across services, 1,281 lines 16,221 direct 821 shunted, 94% less Measured on a 162,000-line Java monorepo. Mean bulk-read saving: 90%.
The bigger the read, the harder the saving lands. The 7,408-line pair is a 94% cut.

Note the shape of that chart. The single-file case saves 82%, the multi-file cases save 94%. Delegation has a fixed overhead, so the savings curve bends the right way: the worse the read, the better the trade. That is also why the plugin refuses to delegate anything under 350 lines, where the overhead eats the win.

The fourth scenario is code-write, and it is measured differently because it is a different job: 3,667 lines of reference material and 40,614 tokens of would-be generation turned into 833 lines written straight to disk, never passing through Claude's context at all.

The routing logic itself is tested. evals/run.sh runs 51 cases with no Portal access required at all: 17 for the read hook, 17 for the bash hook, 17 for the transport layer against a stubbed CLI, plus three end-to-end skill cases and the four benchmark scenarios behind a --benchmark flag. You can verify the gate works before you verify the savings exist.

What it refuses to delegate

The interesting engineering is not the delegation. It is the restraint.

the plugin's own line between cheap work and real work SHUNTED OUT bulk reads over 350 lines cat / head / tail on big files boilerplate from a reference test scaffolds written to disk cross-service file surveys STAYS WITH CLAUDE debugging any edit needing exact text files under 350 lines architectural judgment safety-critical code
Everything on the left is I/O wearing a reasoning task's clothes.

Editing is the sharpest exclusion. An edit needs the exact bytes in context, so a summary is worse than useless. The documented answer is to use targeted reads with an offset and limit, which the hook lets through untouched. Debugging is excluded for the same reason in reverse: a summary throws away the one weird line you were hunting for.

Put another way, shunt delegates precisely the work nobody wanted to pay frontier rates for in the first place.

The catch, and it is a real one

shunt does not call a model API. It calls the Portal CLI actions registry, one aika:invoke-chat per delegation, against a Spotify Portal instance with AiKA enabled. Portal is Spotify's agent platform, the commercial descendant of Backstage, and AiKA Modes are declarative agents on an ephemeral runtime that Mazmanov describes as Lambda for agents. No Portal instance, no shunt.

So this is not a plugin a solo developer installs on a Tuesday. If you already run Portal, the two modes it needs, bulk-reader and code-writer, may already exist as public modes on your instance, and the README gives you the exact aika:create-mode payloads if they do not. If you do not run Portal, what you get is the design, not the tool.

The published limitations are worth reading before you assume this is free money:

  • Only half of it is enforced. The read hook is a hard gate. code-writer has no hook at all and relies on Claude noticing the skill description, which is a very different reliability class.
  • Everything travels through argv. Request payloads go on the command line, so a call has to fit inside ARG_MAX, 1 MB on macOS and a 128 KiB per-argument cap on Linux. shunt refuses anything over SHUNT_MAX_PAYLOAD_BYTES with a clear error instead of failing with E2BIG. Your 2026 token-optimization pipeline is bounded by a Unix process limit older than most of the people using it.
  • Latency is not free. Round trips run 10 to 30 seconds, and Portal caps a single invocation at 30 seconds, so large generations have to be split.
  • Every call is one shot. aika:invoke-chat stores nothing server side, and shunt deliberately does not replay prior turns, because replaying a file corpus is the exact cost it exists to avoid.

What to take from it if you cannot run it

The transferable part is the gate, not the vendor. A PreToolUse hook that inspects a Read call, counts lines, and returns a block with a redirect message is a shell script. The published eval suite is 51 cases against stubbed input, which is a template you can copy outright. The worker model is a config field, not an architectural commitment.

The line that survives all of it: your context window is a budget, and the largest single line item is usually text your model did not need to see in full. Whether you route that to Flash through Portal, a local model, or a subagent with a tighter system prompt, the 350-line threshold and the refusal list are the two decisions that matter. Spotify published both, with numbers.

Key Takeaways

  • shunt is an Apache-2.0 Claude Code plugin, published September 3 in spotify/portal-ai-plugins, that blocks file reads over 350 lines and delegates them to a cheap worker model.
  • On a 162,000-line Java monorepo it cut a 7,408-line source-and-test read from 75,990 tokens to 4,148, a 94% saving, with a mean bulk-read saving around 90%.
  • Savings scale with read size, which is why anything under 350 lines is deliberately left alone: delegation overhead would eat the win.
  • Debugging, edits, architectural judgment and safety-critical code are excluded on purpose, because a summary destroys exactly what those tasks need.
  • It requires a Spotify Portal instance with AiKA enabled, so most readers get the design pattern rather than a drop-in install.
  • The context for all of it is Gartner's June 24 prediction that AI coding costs pass the average developer salary by 2028, and Spotify's own figure of $200 to $500 per developer per month today.

Sources: Spotify Engineering, shunt README on GitHub, spotify/portal-ai-plugins, Gartner press release, June 24, 2026, Claude Code hooks documentation

AICoding AgentsClaude CodeSpotifyOpen SourceTokensCostDeveloper Tools
CONSOLE
$