Repository navigation
⚡ Claude Token Optimization2026-06-18 — Smoke Claude #5246
Description
Target Workflow: Smoke Claude
Source report: #5241
Estimated cost per run: ~$0.078 (runs with data)
Total tokens per run: ~60,300 (avg across 10 completed runs)
Cache read rate: 0%
Cache write rate: 99.2% of input-side tokens
LLM turns: 2 per completed run
Model: claude-haiku-4-5
Workflow source: .github/workflows/smoke-claude.md
⚠️ 95% failure rate (19/20 runs) — Root cause:max-turns: 2exhausted by retry attempts; all 16 re-attempt runs hitmax_runs_exceeded (3/2)on their first API call.
Current Configuration
| Setting | Value |
|---|---|
| Tools loaded | 34 (27 native Claude Code + 7 safeoutputs MCP) |
| Tools actually used | 2 (bash × 1 call, safeoutputs_noop × 1 call) |
| Network groups (awf-config) | 60+ domains allowed |
| Network actually used | api.anthropic.com:443 only |
| Pre-agent steps | Yes (5 steps: smoke file, PR fetch, GitHub reachability, file verify, JSON result) |
| Prompt size | 4,544 bytes (system) + 20,345 bytes AGENTS.md loaded as repo context |
max-turns |
2 |
| Run triggers | schedule (every 12h), pull_request, label_command, reaction |
| Concurrent retries observed | 6 runs in 7 min burst (Jun 17 16:18–16:25 UTC) |
Cost Breakdown Per Completed Run
| Token Category | Tokens | Rate | Cost | % of Total |
|---|---|---|---|---|
| Input (new) | 2,277 | $1.00/M | $0.0023 | 2.9% |
| Cache write | 59,746 | $1.25/M | $0.0747 | 95.6% |
| Cache read | 0 | $0.10/M | $0.0000 | 0.0% |
| Output | 237 | $5.00/M | $0.0012 | 1.5% |
| Total | 62,260 | $0.0782 |
95.6% of cost is cache writes that are never read — the ephemeral cache (5-min TTL) is written every run but expires before the next run (12h schedule). Within-run cache reuse across turns is also 0%, which is anomalous (see Cache Analysis below).
Recommendations
1. Increase max-turns from 2 to 4
Estimated token impact: None — but fixes the 95% failure rate
Root cause: max-turns: 2 translates to maxRuns: 2 in the api-proxy. Claude Code uses one extra invocation for session title generation beyond the agent's 2 task turns. When GitHub Actions auto-retries a failed run (same run_id, attempt 2+), the api-proxy counts invocations cumulatively across all attempts — so attempt 2 starts with invocation_count=3, immediately exceeding the limit. All 16 re-attempt runs show: "Maximum LLM invocations exceeded (3 / 2)" at their very first API call.
Implementation — in .github/workflows/smoke-claude.md:
# Before
max-turns: 2
# After
max-turns: 4Setting 4 provides: 1 task turn + 1 tool result turn + 1 title generation + 1 retry buffer.
CI savings: Eliminates ~200+ wasted action-minutes per analysis period and stops the 6-run burst retry pattern.
2. Inline pre-computed result to eliminate the bash tool call
Estimated savings: ~$0.002/run (~2.5%) + enables max-turns: 2 to work
Current flow:
- Turn 1 — Agent reads prompt → calls
bash: cat /tmp/gh-aw/agent/final-result.json - Turn 2 — Agent receives JSON → calls
nooporadd_comment
Optimized flow:
- Turn 1 — Agent reads prompt (which already contains the JSON inline) → directly calls
nooporadd_comment
Implementation — add a pre-agent step that injects the result inline:
steps:
# ... existing steps ...
- name: Inject result into context
env:
EXPR_GITHUB_RUN_ID: ${{ github.run_id }}
run: |
RESULT=$(cat /tmp/gh-aw/agent/final-result.json)
# Write to a file that the workflow prompt imports via {{#runtime-import}}
echo "<!-- smoke-result: ${RESULT} -->" > /tmp/gh-aw/agent/inline-result.md
echo "Pre-computed result injected: $RESULT"Then update the workflow prompt body (.github/workflows/smoke-claude.md) to replace:
Read `/tmp/gh-aw/agent/final-result.json` (one bash call: `cat /tmp/gh-aw/agent/final-result.json`).with the result passed via {{#runtime-import /tmp/gh-aw/agent/inline-result.md}} or by embedding it directly as a pre-agent step output variable.
This allows max-turns: 1 (+ 1 for title = 2 total), making the current max-turns: 2 sufficient if the result is already in the prompt.
3. Add concurrency cancellation to prevent burst retries
Estimated savings: Up to $0.078 × 5 = $0.39 per burst event
Six runs fired within 7 minutes on 2026-06-17 16:18–16:25 UTC, each consuming 60K tokens. A concurrency group per branch would cancel stale queued runs:
Implementation — in .github/workflows/smoke-claude.md frontmatter:
on:
roles: all
schedule: every 12h
workflow_dispatch:
label_command:
name: ready-for-aw
events: [pull_request]
remove_label: false
reaction: "heart"
# Add:
concurrency:
group: smoke-claude-${{ github.ref }}
cancel-in-progress: true4. Investigate 0% within-run cache reuse (largest potential savings)
Estimated savings if fixed: ~$0.069/run (~88%) — cache read costs $0.006 vs cache write at $0.075
Observation: Turn 1 writes 59,746 tokens to Anthropic ephemeral cache. Turn 2 (within the same session, seconds later) shows cache_read_input_tokens: 0. The cache should hit on Turn 2 since the 5-minute TTL hasn't expired.
Evidence from run 27730824733 (successful):
Turn 1: input=5, cache_write=59746, cache_read=0, output=~216
Turn 2: input=2272, cache_write=0, cache_read=0, output=~21
^^^^^ anomaly
Possible causes to investigate:
- Claude Code may include dynamic content (timestamps, session IDs) inside the cached prefix that invalidates the cache key between turns
- The api-proxy may be stripping
cache_controlmarkers from the API request, disabling prefix caching - Check api-proxy logs for
cache_creationvscache_readfields across both turns
How to diagnose:
# After a run, check the api-proxy CONNECT logs to see raw usage per turn:
grep -A5 '"cache_creation"' .github/aw/logs/run-<ID>/agent-stdio.log | head -40If the api-proxy is the cause, enabling pass-through of cache_control markers would reduce per-run cost from $0.078 to ~$0.009 (88% reduction).
5. Trim network allowlist (security hygiene + minor overhead)
Estimated token savings: ~0 tokens, but reduces attack surface
The awf-config allows 60+ domains including cdn.playwright.dev, registry.npmjs.org, pypi.org, packages.microsoft.com — none of which Smoke Claude uses. Firewall logs confirm 100% of traffic goes to api.anthropic.com:443.
Implementation — in .github/workflows/smoke-claude.md, add an explicit network group:
network:
allowed:
- anthropic
- githubThis replaces the inherited defaults group with only what's needed, following the principle of least privilege.
Cache Analysis (Anthropic-Specific)
| Turn | Input | Output | Cache Write | Cache Read | Net Billable |
|---|---|---|---|---|---|
| 1 | 5 | ~216 | 59,746 | 0 | 59,751 + 216 |
| 2 | 2,272 | ~21 | 0 | 0 ← anomaly | 2,272 + 21 |
| Total | 2,277 | 237 | 59,746 | 0 | 62,260 |
Cache write amortization: Turn 1 cache writes are never reused — not in Turn 2 (anomaly), not in subsequent runs (12h between runs exceeds 5-min TTL).
Cache cost vs benefit:
- Current: Pay $0.0747 to write cache that returns $0.000 in read savings
- If within-run cache reuse were fixed: Turn 2 cache read saves ~$0.059 ($0.0747 write − $0.006 read × 9 turns) — but with only 2 turns the math is: Turn 1 writes $0.0747, Turn 2 reads $0.006 = net write cost paid once, amortized. Still a net positive IF turn 2 actually reads from cache.
- Cross-run cache reuse is impossible without a persistent cache mechanism (Anthropic's ephemeral cache doesn't persist between GitHub Actions runs).
Expected Impact
| Metric | Current | After Rec #1+2+3 | Savings |
|---|---|---|---|
| Failure rate | 95% | ~5% (estimate) | −90 pp |
| Tokens/run | ~60,300 | ~58,000 | −4% |
| Cost/run (completed) | $0.078 | $0.076 | −3% |
| Wasted action-minutes | 222 min / period | ~30 min (estimate) | −86% |
| LLM turns | 2 | 1 | −1 turn |
| Burst waste (6-run event) | $0.47 | $0.08 | −83% |
If Recommendation #4 (cache fix) is also implemented: Cost/run drops to ~$0.010 (−87%).
Implementation Checklist
- Change
max-turns: 2→max-turns: 4in.github/workflows/smoke-claude.md - Add
concurrency.cancel-in-progress: trueper branch in the workflow frontmatter - Add inline result injection pre-step to eliminate the bash tool call in Turn 1
- Update workflow prompt body to read inline result instead of calling bash
- Investigate api-proxy
cache_controlpass-through for within-run cache reuse - Trim network allowlist from
defaults(60+ domains) toanthropic + github - Recompile:
gh aw compile .github/workflows/smoke-claude.md - Post-process:
npx tsx scripts/ci/postprocess-smoke-workflows.ts - Verify CI passes on a PR trigger (requires
add_commentto succeed) - Compare token usage on next run vs $0.078 baseline (target: <$0.020 if cache fixed, <$0.076 otherwise)
Generated by Daily Claude Token Optimization Advisor · ◷
Activity