Motivation
C4-Spend-Audit (2026-05-28, 14d, dedupliziert nach request_id): 99,7% der interaktiven Claude-Code-Kosten laufen auf claude-opus-4-7, nur 0,3% auf Sonnet — session-routing.md wird in der Praxis ignoriert. 26% der Calls (≥500k Input) = 50% der Kosten; eine einzelne Session war 2.509 Calls / $2.294.
Der bestehende Nudge im Stop-hook ist zu schwach: er feuert pro Turn, nur wenn ein einzelner Opus-Turn < $0.05 kostet, und sagt vage ⚠ tier-3 reicht für routine work. Das geht im Output unter und gibt keine konkrete Handlung.
Datei
~/.claude/hooks/log_llm_call.py (Claude-Code Stop-hook). Aktuell unversioniert — siehe optionalen Sub-Task unten.
Relevante Stellen:
- Nudge-Block:
main(), der if rows:-Block (~Z. 290–315), Bedingung if "opus" in m.lower() and 0 < turn_cost < 0.05:.
- Session-scoped DB-Query-Muster:
_query_session_total() (~Z. 320) — WHERE source='claude_code' AND task_id = cc-<session>.
- State-File:
_load_state()/_save_state() (~Z. 98–118), aktuell nur {"logged_request_ids": [...]} pro Session.
Ziel
Statt des per-Turn-Geflackers: genau einmal pro Session eine konkrete /model sonnet-Empfehlung, wenn die Session erkennbar überwiegend Tier-3-Routine auf Opus ist. Policy session-routing.md: "mention once, do not nag".
Vorgeschlagene Heuristik (Schwellen tunebar)
Nudge feuern, wenn alle zutreffen:
- Modell-Familie =
opus
- Session hat ≥ 8 geloggte Turns (genug Signal)
- ≥ 70% der bisherigen Session-Turns kosten < $0.10 (Routine-Proxy)
- Session wurde noch nicht genudged
Aktion — eine Zeile auf stderr, z.B.:
💡 Session auf Opus 4.7, aber {pct}% der {n} Turns waren Routine (Median ${med}/Turn).
Tier-3 → /model sonnet ≈ 5× günstiger (session-routing.md). [einmalige Empfehlung]
Datenquelle: ein kleiner Query über task_id = cc-<session_id> (analog _query_session_total) liefert count, Median-Turn-Cost und Cheap-Ratio in einem Rutsch (deleted_at IS NULL filtern).
Once-per-session
State-File um ein Feld tier3_nudged: true erweitern. Dafür _load_state/_save_state von "Set von request_ids" auf ein Dict {logged_request_ids: [...], tier3_nudged: bool} umstellen (Abwärtskompatibel laden: altes Format = nur Liste).
Constraints (nicht brechen)
- Hook-Kontrakt: immer exit 0, fail-silent — ein Logging/Nudge-Fehler darf Stop nie blockieren.
- Bestehender per-Turn-Burn-Nudge (
🔥 burn rate hoch bei turn_cost > 0.20) bleibt.
- Kein zusätzlicher spürbarer Latenz-Hit (eine kleine DB-Query ist ok;
_query_session_total macht ohnehin schon eine).
Acceptance Criteria
Test
tests/ für den Hook gibt es noch nicht → kleiner pytest mit gemocktem _query_session_total/State, der die Trigger-Matrix oben abdeckt (Routine→1×, Tier-4→0×, Sonnet→0×, Idempotenz→1×). Feedback-Memory: "Tests für neue Funktionen gleich mitliefern".
Optionaler Sub-Task (separat, nicht blockierend)
Hook + backfill_llm_calls.py + inject_policies.py sind unversioniert in ~/.claude/hooks/. Wie claude-policy nach platform/tools/claude-hooks/ versionieren (Symlink zurück nach ~/.claude/hooks/), damit billing-relevante Hooks reviewbar/diffbar sind.
Kontext-Referenzen
🤖 Generated with Claude Code
Motivation
C4-Spend-Audit (2026-05-28, 14d, dedupliziert nach
request_id): 99,7% der interaktiven Claude-Code-Kosten laufen aufclaude-opus-4-7, nur 0,3% auf Sonnet —session-routing.mdwird in der Praxis ignoriert. 26% der Calls (≥500k Input) = 50% der Kosten; eine einzelne Session war 2.509 Calls / $2.294.Der bestehende Nudge im Stop-hook ist zu schwach: er feuert pro Turn, nur wenn ein einzelner Opus-Turn < $0.05 kostet, und sagt vage
⚠ tier-3 reicht für routine work. Das geht im Output unter und gibt keine konkrete Handlung.Datei
~/.claude/hooks/log_llm_call.py(Claude-Code Stop-hook). Aktuell unversioniert — siehe optionalen Sub-Task unten.Relevante Stellen:
main(), derif rows:-Block (~Z. 290–315), Bedingungif "opus" in m.lower() and 0 < turn_cost < 0.05:._query_session_total()(~Z. 320) —WHERE source='claude_code' AND task_id = cc-<session>._load_state()/_save_state()(~Z. 98–118), aktuell nur{"logged_request_ids": [...]}pro Session.Ziel
Statt des per-Turn-Geflackers: genau einmal pro Session eine konkrete
/model sonnet-Empfehlung, wenn die Session erkennbar überwiegend Tier-3-Routine auf Opus ist. Policysession-routing.md: "mention once, do not nag".Vorgeschlagene Heuristik (Schwellen tunebar)
Nudge feuern, wenn alle zutreffen:
opusAktion — eine Zeile auf stderr, z.B.:
Datenquelle: ein kleiner Query über
task_id = cc-<session_id>(analog_query_session_total) liefertcount, Median-Turn-Cost und Cheap-Ratio in einem Rutsch (deleted_at IS NULLfiltern).Once-per-session
State-File um ein Feld
tier3_nudged: trueerweitern. Dafür_load_state/_save_statevon "Set von request_ids" auf ein Dict{logged_request_ids: [...], tier3_nudged: bool}umstellen (Abwärtskompatibel laden: altes Format = nur Liste).Constraints (nicht brechen)
🔥 burn rate hochbei turn_cost > 0.20) bleibt._query_session_totalmacht ohnehin schon eine).Acceptance Criteria
/model sonnet-Zeile, danach nie wieder in derselben Session/model sonnet(nicht nur "tier-3 reicht")Test
tests/für den Hook gibt es noch nicht → kleiner pytest mit gemocktem_query_session_total/State, der die Trigger-Matrix oben abdeckt (Routine→1×, Tier-4→0×, Sonnet→0×, Idempotenz→1×). Feedback-Memory: "Tests für neue Funktionen gleich mitliefern".Optionaler Sub-Task (separat, nicht blockierend)
Hook +
backfill_llm_calls.py+inject_policies.pysind unversioniert in~/.claude/hooks/. Wieclaude-policynachplatform/tools/claude-hooks/versionieren (Symlink zurück nach~/.claude/hooks/), damit billing-relevante Hooks reviewbar/diffbar sind.Kontext-Referenzen
🤖 Generated with Claude Code