Skip to content

⚡ Claude Token Optimization2026-06-18 — Smoke Claude #5246

Closed

Description

@github-actions

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: 2 exhausted by retry attempts; all 16 re-attempt runs hit max_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: 4

Setting 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:

  1. Turn 1 — Agent reads prompt → calls bash: cat /tmp/gh-aw/agent/final-result.json
  2. Turn 2 — Agent receives JSON → calls noop or add_comment

Optimized flow:

  1. Turn 1 — Agent reads prompt (which already contains the JSON inline) → directly calls noop or add_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: true

4. 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_control markers from the API request, disabling prefix caching
  • Check api-proxy logs for cache_creation vs cache_read fields 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 -40

If 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
    - github

This 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: 4 in .github/workflows/smoke-claude.md
  • Add concurrency.cancel-in-progress: true per 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_control pass-through for within-run cache reuse
  • Trim network allowlist from defaults (60+ domains) to anthropic + 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_comment to 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 · ◷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions