Original: Spotify's Claude Code setup (@undefinedKi tweet) + Spotify Engineering, Sep 3 2026. Generalized below for GitHub Copilot, Kiro, and Codex (OpenAI Codex CLI).
Route grunt I/O to cheap model, keep reasoning on frontier, enforce with a hard block (>350 lines), not prompt instructions.
Spotify: Claude Opus/Sonnet + Gemini 2.5 Flash via Portal AiKA Modes β ~90% token savings on Java monorepo. Same pattern works anywhere if you add 3 layers: 1) Blocking interceptor 2) Cheap-worker scripts/MCP 3) Redirect instructions.
Use any cheap/fast model: gemini-2.5-flash / claude-haiku / gpt-4o-mini / gpt-4.1-nano / ollama/qwen2.5-coder. Temp 0.2.
1. bulk-reader β reads N files, returns bullets. Frontier never sees full files.
You are a precise code analyst. Read the provided files and answer the question concisely.
Output structured bullets only. No greetings, no prose. Lead every bullet with exact name/type/line.
Use nested bullets for details. Skip anything not asked.
2. code-writer β generates boilerplate from spec + reference, writes to disk.
You generate code files based on a spec and reference files. Match existing patterns, conventions, naming, style exactly.
Output only the code β no explanations, no markdown fences unless asked.
If ambiguous, choose what matches reference.
Spotify's Portal AiKA Modes implement these as name: bulk-reader / code-writer with model: gemini-2.5-flash. You can replace with a direct API call β see self-hosted script below.
Invocation contract (tool-agnostic):
bulk-read --question "What does this service do?" --paths src/A.java src/B.java
code-write --spec "Write tests for UserService" --reference tests/OrderTest.java --target tests/UserTest.java
| Layer | Purpose | Claude Code (Spotify) | Generalized |
|---|---|---|---|
| 1. Hook / Enforcement | Hard block, not suggestion | PreToolUse hooks:check-file-size (>SHUNT_MIN_LINES=350 ) +check-bash-read (cat/head/tail) |
See per-tool below |
| 2. Scripts / MCP | Call cheap model, handle XML/fences, report tokens | bulk-read /code-write bash wrappers overportal CLI |
Same scripts, but call curl to cheap model OR Portal CLI OR MCP server |
| 3. Skills / Instructions | Tell agent when/how to redirect | /bulk-reader skill (md) + block message |
copilot-instructions.md /.kiro/steering/ /AGENTS.md β butalways paired with Layer 1 |
Lesson from Spotify: "Written rules are a suggestion. A block is not." CLAUDE.md routing was ignored until they added blocking hooks.
- Editing β summaries lack reliable line numbers β keep targeted
read(offset,limit)on frontier. - Reasoning/debugging/architecture/thread-safety β cheap model missed subtle bug frontier caught in seconds.
- Small files β delegation overhead 10β30s + 30s cap; threshold exists for this reason.
Replace Portal with 20 lines of bash. Works for all tools:
#!/usr/bin/env bash
set -euo pipefail
QUESTION="$1"; shift
FILES=("$@")
PAYLOAD=""
for f in "${FILES[@]}"; do
PAYLOAD+="<file path=\"$f\">$(cat "$f")</file>\n"
done
curl -s https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $CHEAP_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<EOF | jq -r '.choices[0].message.content'
{
"model": "gpt-4o-mini",
"temperature": 0.2,
"messages": [
{"role":"system","content":"You are a precise code analyst. Output structured bullets only. No prose. Lead every bullet with exact name/type/line."},
{"role":"user","content":"Question: $QUESTION\n\nFiles:\n$PAYLOAD"}
]
}
EOF
Expose same as MCP tool bulk_read(question, paths) and code_write(spec, reference, target) β then every agent can call it.
Copilot has no PreToolUse hooks, so enforcement must be via MCP + custom tool descriptions.
Option A β MCP Router (recommended):
// .vscode/mcp.json
{
"servers": {
"cheap-worker": {
"command": "node",
"args": ["./mcp-cheap-worker/index.js"],
"env": { "CHEAP_MODEL": "gpt-4o-mini" }
}
}
}
- MCP exposes
bulk_read(reads filesserver-side , returns summary) andcode_write(writes file, returns path only). - In
.github/copilot-instructions.md:
## Routing (MANDATORY)
- NEVER read files >350 lines directly. Call `bulk_read` instead.
- For boilerplate/tests from a reference, call `code_write` β do not generate in context.
- Targeted reads with offset/limit are allowed for edits.
- Hard block: MCP
bulk_readis theonly tool that has file access; revoke Copilot'sreadFilefor large files via extensioncopilot-tool-filteror workspacesettings.json:
{ "github.copilot.chat.toolRestrictions": { "readFile": { "maxLines": 350 } } }
(Copilot CLI gh copilot similarly: use GH_COPILOT_TOOL_POLICY to deny large reads)
Option B β Custom Chat Mode:
Create .github/chatmodes/research.mode.md that only has bulk_read tool enabled, and .github/chatmodes/build.mode.md for code-write. Encourage /research What does X do? β auto-routes.
Kiro has hooks β closest 1:1 mapping to Claude.
// .kiro/hooks/bulk-reader.json
{
"preToolUse": [
{
"tool": "fs_read",
"check": "file.lines > 350 && !args.offset",
"block": true,
"message": "File >350 lines: use `bulk-read --question <q> --paths <files>` (cheap worker). For edits use offset/limit."
},
{
"tool": "bash",
"match": "^(cat|head|tail|less|more)\\s+",
"blockIf": "targetFile.lines > 350"
}
]
}
- Add steering:
.kiro/steering/routing.mdwith same mandatory routing text + examples. - Scripts identical: Kiro executes
bulk-readbash script which can call Portal CLIor direct API. Kiro's ephemeral runtime similar to AiKA. - Threshold configurable via
KIRO_SHUNT_MIN_LINES=500env.
Codex uses AGENTS.md + config.toml + MCP.
[mcp_servers.cheap-worker]
command = "node"
args = ["./mcp-cheap-worker/index.js"]
[tools]
deny = ["read_file({\"lines\" > 350})"]
php
<!-- AGENTS.md -->
## Model Routing (ENFORCED)
- Files >350 lines MUST use `bulk_read` MCP tool, not `read_file`.
- Boilerplate/tests MUST use `code_write` with a reference file.
- Direct generation without reference is forbidden.
- Codex respects tool
descriptionstrongly β makebulk_readdescription:MANDATORY for files >350 lines. Reads files server-side and returns concise bullets. Use instead of read_file. - CLI wrapper fallback:
codex --mcp cheap-worker+ shell aliasread = bulk-readforcat.
| Scenario | Frontier | Cheap Worker | Method |
|---|---|---|---|
| Q&A across 5 files | sees summary (200 tokens) | reads full files | bulk-read |
| Generate test copying 20 examples | never sees output | reads 1 reference + writes | code-write --target |
| Edit/fix bug | targeted read(offset,limit) |
not used | direct |
| Debug/architecture/thread-safety | full context | not used | direct |
- Start
350lines (~10k tokens). Measure: ifp50 delegation latency > time saved, raise to500. Spotify usesSHUNT_MIN_LINESenv. - Temperature
0.2for both workers;0.0for code-writer if you want deterministic scaffolding. - Always require
--referencefor code-write β without it, cheap model hallucinates patterns.
claude plugin marketplace add spotify/portal-ai-plugins
claude plugin install portal@portal && claude plugin install shunt@portal
/portal:setup
git clone https://github.com/your-org/mcp-cheap-worker
npm i && cheap-worker --setup # sets CHEAP_API_KEY, model
- Tweet: https://x.com/undefinedKi/status/2095942506433089832
- Spotify article: https://engineering.atspotify.com/2026/9/portal-by-spotify-cut-my-claude-code-token-usage-by-90
- Plugin: https://github.com/sorantis/portal-ai-plugins/tree/add-shunt-claude/plugins/shunt
- AiKA Modes: https://backstage.spotify.com/docs/portal/core-features-and-plugins/aika/modes
Generalized 2026-09-06 from Spotify's Claude-specific implementation. Principle is portable: cheap model + hard block + XML-wrapped delegation.