| name | fable-cost-reduction-mdc |
| description | Use when reducing Claude Code token or quota cost, especially on a high-usage default model like Claude Fable 5. Covers the VERIFIED levers (RTK tool-output minifier, prompt-cache discipline, subagent delegation, low effort) and debunks the NON-levers (1M context has no price premium; MCP schemas are already deferred). Trigger on: save Fable cost, reduce token usage, Claude Code quota, cut usage limits, make Claude Code cheaper, why my plan burns fast. |
Fable / Claude Code Cost Reduction
Verified 2026-07-04 against Anthropic's own docs (see Sources). Scope: reduce token
and Max-plan quota burn, with a focus on Claude Fable 5 as a worked example of a
high-usage default session model (claude-fable-5[1m]). The reasoning applies to
any default model choice on a fixed weekly quota plan.
Example baseline this was written against
- Default model:
claude-fable-5[1m] (Fable 5, 1M context) on every session.
effortLevel: low already set globally. The "cap thinking to low" lever is DONE.
autoCompactThreshold: 0.85.
- Dozens of plugins enabled, several user-scope MCP servers.
NON-levers (do NOT waste time on these; they were already solved by Anthropic)
- 1M context is NOT more expensive. You are billed for tokens actually SENT, not
window size. "A 900k-token request is billed at the same per-token rate as a 9k-token
request." Defaulting to
[1m] costs nothing extra unless you fill it. Switching to 200K
saves $0 on its own.
- Plugin/MCP tool schemas no longer load every turn. Anthropic now DEFERS tool
definitions: only tool NAMES enter context until a tool is actually invoked (~85%
overhead cut). Pruning a large plugin list saves far less than it once would. Still
worth disabling truly-unused ones via
/mcp, but it is a minor lever now.
Real levers, ranked
- Subagent delegation (biggest structural lever). Subagents bill as THEIR OWN
model. Any heavy read / search / multi-step task pushed from a premium-model session
to a cheaper-tier subagent costs that cheaper tier's quota, not the premium one. If
you keep a model delegation ladder documented anywhere in your own config, this is
the lever it's protecting -- the win is in actually USING it, aggressively, during
premium-model work.
- RTK (Rust Token Killer) — MEDIUM RISK, adopt conservatively only. Real, open-source
(
github.com/rtk-ai/rtk, Apache 2.0, 5.5 months old, 68K stars, 113 contributors, no
Nick Saraev/Maker School link confirmed). Runs as a PreToolUse hook that compresses
verbose tool output (git status, cargo/npm test, grep, find) BEFORE the model sees it.
Self-reported 60-90% reduction (unaudited by any third party).
Confirmed, currently-open bugs that produce a SILENTLY WRONG result at exit code 0
(deep-dived 2026-07-04):
--ultra-compact flag (the one Claude Code integration guides recommend) has a
confirmed silent-data-loss bug on grep (issue #2620) — matches vanish, no error shown.
rtk find with flags beyond -name/-iname/-type/-maxdepth silently broadens the query
instead of erroring (#2821, filed the same day this was researched).
rtk git log silently injects --no-merges unless --merges is passed explicitly,
hiding merge commits with zero indication (#1853).
rtk diff always exits 0 even when files differ, breaking &&/if conditionals (#2446).
- Hard caps (20 errors/warnings, 20 list items, 50 inventory) mean anything past the cap
is invisible unless the agent notices a
tee hint and explicitly re-reads the full file
— nothing forces that follow-up.
- No true "lossless-only" mode exists yet (feature request #1313, open, unresolved).
If adopted: exclude
rtk find, rtk diff, rtk git log, and --ultra-compact
globally via [hooks] exclude_commands in RTK's own config; keep tee.mode = "failures"
(default) for full-output recovery; treat as safe only for routine noise (status/lint/test
summaries), never for security-critical full-repo greps or pre-merge diff review.
Claude Code natively supports the underlying mechanism: PreToolUse can rewrite a Bash
command, and PostToolUse updatedToolOutput can rewrite a completed tool's result. Both
documented. Can be gated to terminal+Fable-only via env/model-detecting hook (see below).
- Prompt-cache discipline. Cache READ = 0.1x base input price (90% off). Cache WRITE =
1.25x (5-min TTL) or 2x (1-hour TTL); breaks even after 1 read (5-min) or 2 (1-hour).
Min cacheable block 1,024 tokens. Invalidation hierarchy: Tools -> System -> Messages.
- Editing
CLAUDE.md / AGENTS.md / memory mid-session invalidates System+Messages
cache -> next turn re-pays FULL price. Batch config edits into a Sonnet session, or do
them at session end, not mid-Fable-work.
- Dynamic content in the system prompt (a timestamp, changing hook text) silently kills
the cache every call.
- Compaction resets history and forces new cache writes for the summarized prefix.
- Mind the 5-minute cache TTL. Idle gaps > 5 min expire the cache, so resuming re-pays
full input price for the whole prefix. This IS the "compact/resume is slow and expensive"
symptom. Work in bursts; compact sooner rather than sitting on a huge live context.
- Trim per-turn hook output under Fable — MINOR win, not major (corrected 2026-07-04).
UserPromptSubmit-injected text lands AFTER the cached prefix breakpoint (wrapped as a
system-reminder alongside the prompt), so it does NOT invalidate the existing cache — it
only adds a small amount of uncached tail per turn. Worth trimming, but it is not the "big
cache win" it first looked like. Cache invalidation is driven by Tools/System/Messages
changes (item 3 below), not by hook injection size.
- Prefer CLI over MCP (
gh, aws, gcloud) where possible — zero per-tool listing cost.
- Block huge reads via PreToolUse hook — LOWER RISK, evidence-backed (added 2026-07-04).
Claude Code's Read tool has a documented ~2,000-line/~25K-token cap but does NOT reliably
self-enforce it (confirmed bug, issue #6910: a 20,010-line file was attempted whole and
errored instead of auto-truncating). Worse: Anthropic's own tracker (issue #28783) confirms
a SILENT partial-read failure mode — a file can land in an "ambiguous preview" tier where
Claude reads only part of it with NO signal that content is missing, and proceeds as if it
read everything (instructions/guardrails in the unread tail silently vanish).
A PreToolUse hook that gates on file size/line count (warn ~500 lines, deny/redirect above
~2000, forcing Grep + offset/limit instead of a blind full read) is a genuine MITIGATION for
this bug, not just a cost lever — it forces an explicit decision point instead of a silent
partial read. Reference implementation exists:
smart-read in corca-ai/claude-plugins
(CWF_READ_WARN_LINES=500, CWF_READ_DENY_LINES=2000, env-configurable). Anthropic declined
to build this in natively (issue #22699, "not planned"), so it stays a community/custom hook.
Residual risk: if the follow-up targeted search misses the right term, content is still
missed — just with an explicit prompt to search rather than a silent gap.
- Logs → SQLite — BUILT 2026-07-05, gated model+terminal only.
hooks/logs_to_sqlite.py
fires on Read for files that look like logs (.log extension or common names like
app.log/error.log) once they exceed 1000 lines. Parses each line with a generic
timestamp + level + message pattern, but ALWAYS keeps the full line verbatim in a raw
column too, so an unrecognized log format still ends up fully queryable (just without
structured ts/level columns) instead of silently dropping content the way a
single-format-only parser would. Cached by file path + size + mtime (an unchanged file is
never re-parsed). Denies the raw Read and points at the SQLite db path with example
queries. Residual risk carried into the denial message itself: a bad/overly narrow SQL query
can silently return zero rows and look like a clean "no errors" answer — the message tells
the agent to SELECT COUNT(*) first. Runs ahead of block-huge-reads.py in the same Read
hook list; non-log files fall through to that hook untouched. Narrower in scope than a
general drop-in converter (only worth it if you're actually reading large logs regularly),
but that's a usage-frequency judgment call for you to make, not a technical blocker.
- Semantic compression of context files (global, NOT model-gateable since files load for
all models). Rewrite
CLAUDE.md / AGENTS.md / memory for max info density. Helps the
first-turn cache write on every model. If your own global context file is near whatever
size ceiling you've set for it, this is worth doing globally, separately from any
single-model-only work.
- Advisor-pattern approximation (added 2026-07-07). Anthropic ships a real beta API
feature ("advisor tool,"
advisor-tool-2026-03-01 header) pairing a cheap executor model
with a smarter advisor consulted mid-task; their own SWE-bench Pro numbers: ~92% of
solo-premium-model quality at ~63% of the price. It is API-only (direct Messages API
calls): Claude Code's session/subagent system has no equivalent, so it can't be wired
into the app itself. Approximate the same effect with dispatch discipline, no new
hooks/infra: instead of one large single-shot Sonnet 5 subagent, break a long/ambiguous
task into staged Agent-tool dispatches. Dispatch Sonnet 5 for one bounded chunk, review
the checkpoint yourself (the Fable session), send a short course-correction as the next
chunk's prompt, repeat. Bulk of tokens still land at Sonnet's rate; Fable's judgment only
spends tokens at each checkpoint, not on every step. Only worth the extra round-trips on
tasks long/ambiguous enough that mid-task redirection actually changes the outcome, not on
a well-scoped single-shot subagent task.
- Reverse direction: Sonnet session, Fable advisor (added 2026-07-07). Works the other
way too, and is actually the closer match to Anthropic's real advisor-tool pattern (a
cheap model drives, a smarter model is consulted rarely). Running a Sonnet 5 session as
the default driver, dispatch a single Fable 5 subagent via the Agent tool at one key
decision point (start of an ambiguous task, a fork in the road, before an architecturally
significant choice), then keep executing yourself with that guidance. Bills as one Fable
subagent call, not a whole Fable session. Not automatic: ask for it explicitly, or expect
it flagged when a task looks significant enough to warrant it.
Terminal + model gating — mechanics confirmed 2026-07-04 (harder than it first looked)
Terminal detection: CLAUDE_CODE_ENTRYPOINT is real (directly observed in a live session:
a Claude Desktop app session read claude-desktop) but is NOT in Anthropic's official
env-var docs — treat it as moderate-confidence, not a guaranteed-stable API across versions.
The documented, official signal is the OTEL attribute app.entrypoint (cli, sdk-cli, sdk-ts, sdk-py, claude-vscode), which is a better-grounded reference point if OTEL is ever wired up.
Cheap env-var check inside a hook is still the pragmatic move; just don't treat it as forever-stable.
Model detection is the hard part. NO PreToolUse or UserPromptSubmit hook payload
includes the current model — only SessionStart's stdin JSON has a model field, and that
value goes STALE the moment /model is used mid-session (confirmed from the official hooks
reference). There is no $CLAUDE_MODEL env var. The only way to stay correct after a
mid-session model switch is to re-parse the session transcript .jsonl (path given to every
hook call as transcript_path) for the most recent model entry, every time the hook fires —
not a cheap check, real parsing logic needed in each hook invocation.
Output rewriting, exact shapes (confirmed 2026-07-04):
PreToolUse rewriting the Bash COMMAND before it runs:
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","updatedInput":{"command":"..."}}}
PostToolUse rewriting the tool's OUTPUT before the model sees it (the right event for
RTK-style compression): {"hookSpecificOutput":{"hookEventName":"PostToolUse","updatedToolOutput":"..."}}
— confirmed as of Claude Code v2.1.121+, applies to all tools not just MCP.
Desktop app isn't monolithic: hooks in ~/.claude/settings.json fire in both terminal and
the Desktop app's Code tab (confirmed official), but a still-open bug (GitHub #63360) means
hooks do NOT fire in Cowork/Dispatch tabs due to a sandbox mismatch. Any terminal-gated hook
naturally skips Cowork too, which is fine for this use case but worth knowing.
File compression (item 9) and cache discipline (item 3) are largely global and cannot be
gated at all (files load identically regardless of surface or model).
What pairs well with this
- A low
effortLevel default for routine work.
- A documented model delegation ladder (premium model -> mid-tier -> cheap-tier, with
auto-escalation back up when a cheaper tier struggles) so lever #1 above actually gets used.
Sources
- Claude Code costs docs:
code.claude.com/docs/en/costs (MCP deferral, PreToolUse cost example)
- Claude Code hooks:
code.claude.com/docs/en/hooks
- Anthropic pricing, Long context section:
platform.claude.com/docs/en/about-claude/pricing
- Prompt caching:
platform.claude.com/docs/en/build-with-claude/prompt-caching
- RTK:
github.com/rtk-ai/rtk