Claude PR reviewer reloads the full coding-standards ruleset every run instead of caching it #93665
Description
Problem
The Claude PR reviewer dispatches a code-inline-reviewer subagent whose definition tells it to Glob and Read every coding-standards rule file (~25K tokens across 28 files) on every run. Those reads land in the conversation (message) layer, beneath a system prompt that embeds per-checkout sections (cwd, git state, OS).
Prompt-cache hits require a byte-identical prefix, and the message layer sits below that ever-changing prefix, so the ruleset is never a stable cacheable prefix. As a result it is re-paid at the cache-write rate (1.25x) on every PR run rather than read from cache (0.1x), and ~29 extra tool round-trips are spent each run just loading the rules.
Solution
Move the ruleset into the reviewer's system prompt as a deterministic, byte-stable prefix:
- A small deterministic script concatenates the reviewer instructions and every rule file into one system-prompt file (stable order, fixed separators - so identical bytes across runs, which is what prompt-cache prefix hits require), supplied to the reviewer via
--append-system-prompt-file. - Add
--exclude-dynamic-system-prompt-sectionsso the per-checkout system-prompt sections (cwd, git, OS) stop invalidating the prefix. - Run the reviewer as a single agent so the rules sit in its own system prompt.
Net effect: the rules load once per run instead of via ~29 tool calls, and the now byte-stable prefix becomes eligible for cross-run cache reads (0.1x) instead of writes (1.25x) when runs fall within the cache TTL. Reviewer behavior is unchanged - the same rules are enforced.
Implemented in #93661.
Activity
kacper-mikolajczak commentedon Jun 18, 2026
Claude PR Reviewer — Prompt-Cache Prefix
Status: validated on a test fork; change open as draft PR #93661.
1. Problem
The Claude reviewer loads the full coding-standards ruleset (28 files, ~26K tokens) into context on every PR review. The rules are pulled in at runtime by the
code-inline-reviewersub-agent and land in the conversation's message layer, below a per-checkout system prompt — so they are not a cacheable prefix and are reprocessed on every run.2. Idea
Put the ruleset in a byte-stable, cacheable prompt prefix so prompt caching serves it at the read rate (0.1×) instead of reprocessing — when the prefix is byte-identical and a prior run is within the cache TTL.
3. Solution — two changes, both required
--append-system-prompt-file--exclude-dynamic-system-prompt-sectionsflowchart TB subgraph OLD["OLD — current reviewer"] direction TB O1["tools"]:::stable --> O2["system: preset<br/>+ dynamic sections cwd / git / OS<br/>(change every run)"]:::volatile O2 --> O3["messages:<br/>Glob + Read 28 rule files (~29 tool calls)<br/>then PR diff"]:::volatile end subgraph NEWREV["NEW — cache-prefix reviewer"] direction TB N1["tools"]:::stable --> N2["system: preset (dynamic EXCLUDED)<br/>+ 28 rule files inlined<br/>(byte-stable → CACHED)"]:::cached N2 --> N3["messages:<br/>PR diff only"]:::volatile end classDef cached fill:#d4f8d4,stroke:#2e7d32,color:#1b5e20; classDef volatile fill:#ffe0e0,stroke:#c62828,color:#b71c1c; classDef stable fill:#e8eaf6,stroke:#3949ab,color:#1a237e;4. Implementation — draft PR #93661
.github/scripts/pack-reviewer-prompt.sh.github/reviewer/ci-instructions.md.github/workflows/claude-review.ymlTasksub-agent).claude/agents/code-inline-reviewer.md3 files, +97 / −2. Nothing generated is committed. Needs bundled CLI ≥ 2.1.98 — App is on
claude-code-action@v1.0.149(✓). 1h cache TTL recommended.5. Cache behavior
TTL 5 min default, 1 h opt-in (
ENABLE_PROMPT_CACHING_1H=1) — required since a run takes ~5-6 min. Cache key = prefix bytes + model + org, independent of the PR diff.6. Test framework
{OLD, NEW} × {cold, warm}total_cost_usd+ directcache_read/cache_creationtokens (show_full_output)claude-opus-4-8, actionv1.0.149,ubuntu-latest, 1h TTL, single session; cold/warm verified per run from tokens7. Results (code-step cost, USD)
cache_creationcache_read8. Economics
OLD(h)=0.28h+0.53(1−h)·NEW(h)=0.19h+0.81(1−h)xychart-beta title "Cost per review vs warm-hit-rate (break-even at h ~ 0.75)" x-axis "warm-hit-rate h" [0, 0.25, 0.5, 0.75, 1.0] y-axis "USD per review" 0 --> 0.9 line "OLD" [0.53, 0.468, 0.405, 0.343, 0.28] line "NEW" [0.81, 0.655, 0.50, 0.345, 0.19]hBreak-even ≈ 75% warm.
Updated
SUMMARY.md. The two changed sections now read:9. Verdict for App repo
Adopt — with the 1h cache TTL enabled. The reviewer's measured run cadence puts it deep in the warm regime, well past the break-even.
Run history (
claude-review.yml, 28,489 runs over 268 days, ~106/day):h@ 1h TTLh@ 5-min TTLAt
h ≈ 0.96: OLD ≈ $0.29/review, NEW ≈ $0.22/review → ~25% saving (~$0.07/run; ~$8/day ≈ ~$2.8K/yr on the code reviewer alone), plus lower latency (skips ~29 rule-loading tool round-trips/run).Monitor via the action's
execution_fileoutput.kacper-mikolajczak commentedon Jun 18, 2026
Hi @Julesssss @rlinoz! 👋 One of the possible enhancements to the reviewer setup - this time coming with possible non-functional improvement on time and savings. Let me know if you have any thoughts 🙏
Julesssss commentedon Jun 18, 2026
Oh I'd like to look at this more closely, very interesting to see how we could make our prompting more efficient. To be honest the savings aren't that exciting here though, and it comes with some overhead of needing to maintain and be detect regressions to the caching. The idea is interesting though.
kacper-mikolajczak commentedon Jun 19, 2026
Thanks. I agree this is more of an end-level optimisation that comes with a cost.
However, it could be beneficial to monitor this approach as it will become more and more valid as the rule-set grows - both from duration (currently rules are loaded explicitly by prompting, not programmatically) and savings perspective.
Prompting efficiency is topic that I'd definitely want to look into more deeply in spare time. Let's get ourselves posted if we come up with anything interesting. :)
kacper-mikolajczak commentedon Jun 22, 2026
With that said, here's a possible alternative - a lightweight monitoring proposal we could use to gauge over time how much caching actually helps vs adds cost, and catch it if the savings quietly erode (a rules edit, action/CLI bump, model change etc.).
CACHE_USAGE creation=… read=… cost=…). No message content; safe in public logs.Draft PR: callstack-internal#57
Julesssss commentedon Jun 23, 2026
@kacper-mikolajczak to be honest I think we can just get you to re-investigate later and focus on
device-agent. If you are interested in prompting efficiency we could instead look into Melvin costs. We have a cost analysis script/summary here that you may have some thoughts on?kacper-mikolajczak commentedon Jun 24, 2026
@Julesssss Makes total sense - parking this for now and focusing on agent-device first 👍
Bakomebandias commentedon Jun 26, 2026
Picking this up! 👋
Fix plan: Move the coding-standards ruleset from the message layer into the system prompt as a static, byte-stable prefix so Anthropic's prompt cache can hit on every PR run.
This will eliminate the ~29 extra tool round-trips and reduce cost from 1.25x (cache-write) to 0.1x (cache-read) per run.
Will submit PR shortly. @Bakomebandias
melvin-bot commentedon Jun 26, 2026
📣 @Bakomebandias! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
Please follow these steps:
Format:
Bakomebandias commentedon Jun 26, 2026
Withdrawing my attempt — I see PR #93661 already addresses this. Good work @kacper-mikolajczak! 🙏
melvin-bot commentedon Sep 4, 2026
@kacper-mikolajczak, this Monthly task hasn't been acted upon in 6 weeks; closing.
If you disagree, feel encouraged to reopen it -- but pick your least important issue to close instead.