For your own Claude Code setup
Two models, one budget.
Stop paying for context you didn't need.
A reference for prompting Anthropic (Sonnet 5 / Opus 5) and Z.AI (GLM-5.3 / Flash) in Claude Code — built from your own measured sessions, not generic advice.
1 · Mental model
Your Claude Code setup is not "a model you talk to." It is a pipeline: a router picks which model answers, a meter watches what that costs, and an archive keeps a copy of what happened because the provider's own transcript store does not. All three are hooks — small scripts Claude Code runs at fixed points (prompt submit, session start, session stop) — and all three were built in response to one measured fact: a handful of runaway sessions consume almost all of your quota, and nothing was watching.
The two-tier design exists because model capability and token cost move independently. A work-tier model (Sonnet 5 on Anthropic, GLM-5.3-Flash on Z.AI) is cheap per token and fine for anything mechanical. A deep-tier model (Opus 5, GLM-5.3) costs roughly 3x more per input token and 3x more per output token, and is worth it exactly when the task involves a judgment call with no obvious right answer. The entire prompting discipline in this guide reduces to one question, asked honestly, per task: does this need judgment, or does it need typing?
2 · Your setup at a glance
Two config directories, each a complete Claude Code identity: ~/.claude talks to Anthropic directly; ~/.claude-zai talks to Z.AI's Anthropic-compatible endpoint via zclaude. Both are now wired identically for metering and archiving.
| File | Role | Registered in |
|---|---|---|
~/.claude/hooks/usage-guard.py | Per-session + rolling 5h/7d budget meter. Warns or blocks. | Both, UserPromptSubmit |
~/.claude/hooks/peak-hours-guard.sh | Warns/blocks the deep tier during Z.AI's peak-rate window. | Z.AI only, UserPromptSubmit |
~/.claude/hooks/log-prompt.sh | Appends every prompt to ~/.claude/logs/prompts.jsonl. | Both, UserPromptSubmit |
~/.claude/archive/archive_transcripts.py | SQLite archive of every session transcript, raw + parsed. | Both, SessionStart + Stop |
~/.claude/hooks/caveman-*.js | Terse-output mode — cuts filler tokens from every response. | Both, various |
~/.claude/hooks/active-plan-check.sh | Surfaces an in-progress plan file at session start. | Anthropic only, SessionStart |
~/scripts/zclaude | Launcher: picks the Z.AI tier pair, sets the compact window, applies the peak guard. | — |
~/.claude/tier-discipline.md | The short routing policy loaded into every session via both CLAUDE.md files. | Both |
~/.claude/model-routing.md | The long version — full measured data, not loaded into sessions. | Reference only |
Sources: ~/.claude/settings.json, ~/.claude-zai/settings.json, ~/.claude/model-routing.md.
3 · Verify it's live
Before trusting any of the guidance below, confirm the pipeline is actually running in your current setup.
python3 ~/.claude/hooks/usage-guard.py --status # Expect: policy, budgets, and rolling 5h/7d totals — not an error python3 ~/.claude/archive/archive_transcripts.py stats # Expect: session/message counts and a date range for both configs ~/.claude/hooks/peak-hours-guard.sh --status # Expect: current SGT peak window state; exit 1 only if inside peak
4 · Where the money actually goes
The credit formula looks like it rewards short output. It doesn't — it rewards small context. This is the single most important number in this guide, because every prompting rule below is downstream of it.
| Source | Tokens | Share of tokens | Share of credits |
|---|---|---|---|
| Cached input | 4,096,294,848 | 96% | 82% |
| Fresh input | 140,101,070 | 3% | 11% |
| Output | 23,295,400 | 1% | 7% |
Measured across 59,397 Z.AI turns, 2026-07-15 to 2026-09-11. Per-token, output is 3.5x fresh input and fresh input is 4.1x cached input — but volume beats ratio: there is 175x more cached-input volume than output volume, so cached input still wins on total cost even at its lower per-token rate.
What this means in practice: the size of your context window, re-read on every single turn, is the lever. Terse replies help, but only at the margin — they shave the 7% column. A bloated context that never gets compacted taxes every turn for the rest of the session.
The concentration problem
It isn't evenly distributed usage either. One session accounted for nearly half of eight weeks of spend:
| Sessions | Share of credits |
|---|---|
| Top 1 of 670 | 47.5% |
| Top 3 | 61.3% |
| Top 10 | 76.1% |
| Top 50 | 92.6% |
| Remaining 620 | 7.4% |
That top session ran 18,045 turns over 93 hours, median context 520,077 tokens, peaking at 921,806 against the 1M ceiling — an unattended loop nobody was watching, on the expensive tier, without a compact window. Replaying it against three interventions:
| Scenario | Credits | vs actual |
|---|---|---|
| As it ran (1M window, GLM-5.3) | 170,514 | 1.0x |
| 250K auto-compact only | 75,755 | 2.3x |
| Flash only | 56,265 | 3.0x |
| Both changes | 24,996 | 6.8x |
zclaude default) never fires for ordinary work — it exists purely for the tail. Don't shrink it further; a tighter window compacts normal sessions needlessly, and compaction itself costs a summarization call and loses fidelity.
Sources: ~/.claude/model-routing.md §1–3, replay against the largest recorded session's transcript (170,514 credits).
5 · Task → model routing
The wrong question is "which model is smarter?" Both are smart enough for roughly three-quarters of what actually gets typed into this setup. The right question is "does getting this wrong cost more than the token savings from getting it fast?" Below is the actual composition of your own Anthropic-side prompt corpus, split by that question.
Work tier — /model sonnet (Flash / Sonnet 5)
| Class | Share | Why it's safe |
|---|---|---|
Acks and steers (1, Yes, push, Go) | 20% | 205 prompts averaging 4 characters. No reasoning at all. |
| Short steers and re-runs | 26% | Averaging 73 characters. Mechanical. |
| Commit, push, branch, PR, worktree | 21% | Format-bound. |
| Tests, lint, formatting, chasing CI green | — | Verification is reading output, not reasoning. |
| Docs, README, changelog | 2% | Bounded scope, known format. |
| "Where is X" / "what calls Y" | — | Search, not analysis. |
| Mechanical refactors | — | Scope known before you start. |
| Log / stack-trace triage | — | Pattern matching. |
| Long unattended loops | — | The one rule with measured consequences — see §4. |
Deep tier — /model opus (GLM-5.3 / Opus 5)
| Class | Share | Why |
|---|---|---|
| Architecture, specs, phased plans | 5% | Wrong answer costs hours downstream. |
| Code review and security audit | 3% | Cheap models miss what they don't look for. |
| Root-cause debugging | 4% | After two honest attempts on the work tier. |
| Auth, money, migrations, data integrity | — | Blast radius. |
| Deciding whether, not how | — | Judgment calls with no obvious right answer. |
Roughly 75% of Anthropic-side turns fit the work tier by this breakdown. If your own ratio looks very different from that, that's a signal worth noticing — not a rule you're breaking.
/model mid-session invalidates the prompt cache — the new model re-reads the whole context as fresh input at the 4.1x-more-expensive rate instead of cached. At 250K tokens of context that's roughly 172 credits on GLM-5.3, about twenty ordinary turns. Escalate early, before context has accumulated, and never more than once per session. For work you already know is deep, start there — zclaude deep — rather than switching later.
6 · Prompt patterns per tier
Small models fail on inference, not raw capability. The gap between a work-tier model that nails a task and one that flails on it is almost always missing information the prompt should have supplied outright.
Work-tier prompts: make inference unnecessary
Every work-tier prompt should carry four things:
- Explicit paths — not "the config file," but
~/.claude/settings.json. - An explicit done condition — how the model knows it's finished, not just what to start doing.
- A scope boundary — what NOT to touch, especially in a repo with adjacent unrelated code.
- An escape hatch for hidden difficulty — when the task might be harder than it looks, add:
If you'd have to guess at anything, stop and print ESCALATE: <reason>.
Deep-tier prompts: state the judgment call, not the steps
If you're paying the deep-tier multiplier, spend it on judgment, not typing speed. Good deep-tier prompts name the trade-off explicitly ("pick between eager and lazy loading here, and say why") rather than a numbered procedure — a numbered procedure is work-tier material regardless of which model executes it.
Stop pasting command output
51% of every character typed into this setup was pasted terminal output; one lsof dump alone ran 21,864 characters. Pasted text is not compactable the way a tool result is — it sits in the conversation and is re-read on every subsequent turn.
| Instead of | Say |
|---|---|
| Pasting a stack trace or log dump | Run <cmd> and diagnose — let the tool call carry it, not the prompt |
| Re-pasting a template every time | A slash command (/postgate, /sym-phase) — same content, but now a stable cache prefix instead of fresh tokens each time |
| Describing what you just watched happen | Point at the file/output and ask for the specific judgment you need |
Sources: ~/.claude/model-routing.md §8, ~/.claude/tier-discipline.md.
7 · The budget guard
usage-guard.py exists because routing ordinary work to a cheaper tier doesn't stop a runaway session — only a budget does. On every prompt it accounts for what the session has spent since the last check, appends the delta to a durable ledger, and checks three numbers against limits: this session's total, the rolling 5-hour total across all sessions, and the rolling 7-day total.
| Z.AI | Anthropic | |
|---|---|---|
| Soft — notice in context | 1,000 credits | $2.00 |
| Hard — next prompt refused | 5,000 credits | $10.00 |
Z.AI's numbers are real credits, directly comparable to plan caps. Anthropic's are list-price dollars — a quota-pressure proxy, not a bill, since Anthropic doesn't publish the weighting behind its own 5-hour/weekly limits. At these defaults, the 402,576-credit runaway session from §4 would have been stopped at 1.2% of its eventual spend.
A blocked prompt is not a lost prompt
Hitting the hard threshold exits 2 and Claude Code surfaces the reason, but your typed text is preserved. Downshift with /model sonnet, or start a fresh session, and re-send the same prompt.
~/.claude/hooks/usage-guard.py --status # policy, budgets, rolling windows ~/.claude/hooks/usage-guard.py --windows # rolling 5h / 7d vs plan caps ~/.claude/hooks/usage-guard.py --top 10 # largest sessions in the ledger CLAUDE_BUDGET_ZAI_HARD=50000 zclaude deep # raise the ceiling for a session you know is legitimately large CLAUDE_BUDGET_POLICY=warn zclaude # never block, just tell me
Sources: ~/.claude/usage-budget.md, ~/.claude/hooks/usage-guard.py.
8 · The peak-hours guard
Z.AI's legacy prompt-count plans bill GLM-5.3 at 3x and Flash at 1.2x during Mon–Fri 14:00–18:00 Singapore time — 01:00–05:00 CDT locally. That's the middle of the night, exactly when unattended overnight loops run: 15.3% of Z.AI turns and 13.3% of credits fell inside that window, almost none of it watched work.
| Value | At launch | During a session |
|---|---|---|
warn (default) | Prints a warning, starts the deep tier anyway | Notice in context each turn |
enforce | Starts on Flash instead (--peak-ok overrides) | Blocks the prompt (exit 2) |
off | Silent | Silent |
# Interactive work — warn and let me judge (the default; nothing to set) zclaude deep # Overnight loop — never let it hold the deep tier through peak ZCLAUDE_PEAK_POLICY=enforce zclaude deep --danger -p "$(cat nightly-task.md)" zclaude --peak # window status in local time
ZCLAUDE_PEAK_POLICY=off — but --status is still a useful signal that an unattended session is sitting on the expensive tier at 3am.
Sources: ~/.claude/peak-hours.md, ~/.claude/hooks/peak-hours-guard.sh.
9 · The transcript archive
Claude Code prunes session transcripts. Between 2026-09-11 and 2026-09-13, the live Z.AI transcript count fell from 1,200 files to 585, and recorded GLM-5.2 turns fell from 10,925 to 910. Anything computed by re-reading transcripts — cost analysis, session history, this guide's own source data — silently loses history as that pruning happens. archive_transcripts.py exists so nothing has to trust the live transcript store again.
What it captures
Both storage granularities you asked for: the full raw JSONL of every transcript (zlib-compressed) for perfect-fidelity replay, and a parsed messages table (role, model, timestamp, token counts, content preview) for fast querying without decompressing anything. Scope is both configs — ~/.claude/projects and ~/.claude-zai/projects — including subagent transcripts nested under <session>/subagents/<name>.jsonl.
| Trigger | What runs | Why it's crash-safe |
|---|---|---|
SessionStart | hook-sessionstart — sweeps every live transcript file, imports anything new or more complete | Catches sessions that never fired a clean Stop — a crash, a killed terminal, a force-quit |
Stop | hook-stop — archives the just-finished session's transcript immediately | Minimizes the pruning window for the common, clean-exit case |
Import is "most-complete-version-wins": a file is only re-imported if it has more lines than what's already archived for that transcript_key. Nothing is ever deleted or overwritten with less-complete data.
python3 ~/.claude/archive/archive_transcripts.py stats # claude: sessions=5259 messages=485364 range=2025-12-24..2026-09-13 # claude-zai: sessions=13471 messages=716060 range=2025-12-24..2026-09-12 # db size: 1608.8 MiB python3 ~/.claude/archive/archive_transcripts.py sweep # manual catch-up sweep of live files python3 ~/.claude/archive/archive_transcripts.py backfill-timemachine # pull older snapshots from TM backups python3 ~/.claude/archive/archive_transcripts.py export <transcript_key> --out session.jsonl
Sources: ~/.claude/archive/archive_transcripts.py, ~/.claude/settings.json, ~/.claude-zai/settings.json.
10 · Caveman mode
The caveman-mode hooks (caveman-activate.js at SessionStart, caveman-mode-tracker.js at UserPromptSubmit) inject a standing instruction to drop articles, filler, and pleasantries from responses, keeping technical substance intact. It's a direct application of §4: output tokens are 3.5x the cost of input tokens, and every token emitted this turn is re-read as cached input on every turn after it. Terse-by-default is a small, compounding discount that costs nothing to keep on.
It explicitly steps aside for security warnings, irreversible-action confirmations, and anywhere fragment order could be misread — code, commits, and PRs are always written in full prose regardless of mode.
Labs
Lab 1 · Read your own routing signal
Run ~/.claude/hooks/usage-guard.py --top 10 and look at your own top 10 sessions by spend. For each one, ask: was that session's size a judgment call being made repeatedly (legitimate deep-tier work), or a work-tier task that ran long because the context never got trimmed?
Lab 2 · Practice the ESCALATE / DOWNSHIFT signal
Start on /model sonnet. Give it a task you already suspect is a genuine judgment call (per §5's deep-tier table). Confirm it prints ESCALATE: <reason> and stops rather than guessing. Switch with /model opus and resend once, not twice.
Lab 3 · Query the archive for a session you thought was gone
Pick a session older than a week. Run archive_transcripts.py export <transcript_key> against it (find the key by grepping sqlite3 ~/.claude/archive/transcripts.db "select transcript_key from sessions where session_id like '...'").
projects/ copy has since been pruned.Capstone
Start a real task on zclaude (work tier). Before typing the prompt, write down: the explicit path(s) involved, the done condition, the scope boundary, and whether you expect this to need ESCALATE. Work the task. If it escalates, switch once, finish, and afterward run usage-guard.py --session <id> to see what that escalation actually cost against the §5 estimate. Then check --windows to see where this session left your rolling 5h/7d totals.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Budget notice never appears even though spend feels high | Guard failed silently (its design: never break a session) or CLAUDE_BUDGET_POLICY=off | Run usage-guard.py --status directly, outside a session |
| Session totals look wrong after a transcript got pruned | Byte-offset accounting restarts from zero if a transcript shrinks or is replaced | Trust the ledger (~/.claude/usage/ledger.jsonl), not a live recompute from transcripts |
Archive stats shows fewer sessions than expected | Hooks were only just registered — historical sessions predate them | Run backfill-timemachine once to recover pre-hook history |
| Deep tier feels "stuck on" all night | An unattended loop was launched with /model opus or zclaude deep and never downshifted | Never leave an unattended loop on the deep tier — §5's one hard rule |
| Escalating felt like it cost a lot | Context had already grown large before the switch | Escalate earlier next time — cost scales with accumulated context, per §5 |
Cheat sheet
Universal starting pattern
zclaude (work tier, default)
zclaude deep (known-deep work)
/model sonnet / /model opus (switch in-session, once)
Work-tier prompt must have
Explicit path · done condition · scope boundary · "stop and print ESCALATE if you'd guess"Deep-tier triggers
Architecture/spec · security/auth/money/migration · root cause after 2 honest attempts · "whether" not "how"Never
Paste terminal output — say "run X and diagnose" · leave an unattended loop on the deep tier · ping-pong tiersBudget commands
usage-guard.py --status
usage-guard.py --windows
usage-guard.py --top 10
Archive commands
archive_transcripts.py stats
archive_transcripts.py sweep
archive_transcripts.py export <key>
Glossary
- Work tier / deep tier
- The cheap-per-token model (Sonnet 5 / Flash) vs the expensive one (Opus 5 / GLM-5.3). (§1)
- Cached input
- Context re-read from a prior turn rather than freshly processed; billed at roughly a quarter of fresh-input rate but dominates total cost by sheer volume. (§4)
- ESCALATE:
- A line the model prints on the work tier when a task needs judgment the cheap model shouldn't guess at. Does not switch models by itself. (§5)
- DOWNSHIFT:
- The reverse signal — remaining work is mechanical, offered to a deep-tier session. Does not switch models by itself. (§5)
- Ledger
- The append-only spend record at
~/.claude/usage/ledger.jsonl, kept because live transcripts get pruned and can't be trusted as a durable history. (§7) - transcript_key
- The archive's stable identity for a transcript file — its path relative to
projects/— used because subagent transcripts have no session-UUID-shaped filename of their own. (§9) - Peak window
- Z.AI's Mon–Fri 14:00–18:00 SGT surcharge window, billed only under legacy prompt-count plans. (§8)
- Compact window
- The context-size threshold (250K default) past which
zclaudeauto-summarizes to bound growth. (§4)
Index
Quiz
Flashcard style, self-scored. Aim for 12/14. Score: 0 learned · 0 review · 0/14 answered
ESCALATE: switch the model automatically?/model opus yourself and re-send. (§5)transcript_key (a relative path) instead of session ID?<session>/subagents/<name>.jsonl and have no session-UUID-shaped filename of their own; the path is a stable identity, the real session ID is still captured as a separate queryable column. (§9)SessionStart's sweep — it re-scans every live transcript, not just the one that just ended, so a crashed prior session still gets picked up on the next launch. (§9)ZCLAUDE_PEAK_POLICY=off is correct, though --status is still a useful signal. (§8)Sources
All figures traced to files on this machine, checked 2026-09-13. No external network access required to read this guide.
~/.claude/model-routing.md— full measured routing data, credit formula, escalation cost math. Best used for: re-deriving any number in §4–5.~/.claude/usage-budget.md— budget guard design, defaults, verified test cases. Best used for: tuning thresholds.~/.claude/peak-hours.md— peak-window guard design and policy semantics. Best used for: overnight/unattended launch config.~/.claude/tier-discipline.md— the short policy loaded into every session. Best used for: the exact wording the model sees.~/.claude/hooks/usage-guard.py— the budget guard implementation. Best used for: exact CLI flags and exit codes.~/.claude/hooks/peak-hours-guard.sh— the peak guard implementation.~/.claude/archive/archive_transcripts.py— the transcript archive implementation and schema. Best used for: querying historical sessions directly withsqlite3.~/scripts/zclaude— the launcher source, tier selectors, and environment overrides.~/.claude/settings.json,~/.claude-zai/settings.json— live hook registration for both configs.- Vault note:
Coding Agent usage tracking and optimization.md— the original build narrative and the transcript-pruning discovery that motivated §9.