Part of #4496. P3 — medium severity, high confidence.
Context
buildReviewGroundingText's file-content half (src/review/grounding-wire.ts:224-225) re-fetches the ENTIRE post-change content of every changed file via a live GitHub Contents API call on every invocation:
const fetcher = await makeGithubFileFetcher(env, args.repoFullName, args.installationId);
const fileContents = await fetchFullFileContents(flags, args.headSha ?? undefined, toGroundingFiles(args.files), fetcher);
makeGithubFileFetcher (grounding-wire.ts:126-170) issues a live GET /repos/{owner}/{repo}/contents/{path}?ref={ref} GET per file with NO cache/memoization keyed by (repo, path, ref) anywhere in the file — contrast the CI half of the same feature, which explicitly reuses cached data ("reuses the already-cached CI check summaries", file header lines 10-11). The shared GitHub response cache (src/github/client.ts:107-132, githubCacheClassForUrl) doesn't help either — it only classifies branch-protection, bare commit, and bare user/repo lookups as cacheable; the contents/{path} endpoint matches none of those.
Grounding is listed in dynamicReviewFeatures (processors.ts:9724-9725), which unconditionally bypasses the durable ai_review cache (processors.ts:9919, !dynamicReviewContextActive) — a known, deliberate tradeoff for features whose context can go stale for an unchanged head SHA. That leaves only the 30-minute AI_REVIEW_NON_CACHEABLE_RETRY_COOLDOWN_MS (processors.ts:4089) as protection. Once that cooldown lapses, the full multi-file GitHub content re-fetch repeats from scratch for the identical head SHA — and this isn't limited to sweep ticks: maybePublishPrPublicSurface is also invoked from real webhook triggers unrelated to code changes (pull_request_review_comment, pull_request_review, pull_request_review_thread, per PR_PUBLIC_SURFACE_ACTIONS/PR_GATE_CLOSED_ACTIONS, processors.ts:6597-6614), none of which change the head SHA — so any actively-discussed PR with grounding enabled re-triggers the full re-fetch every time a comment/review event lands past the cooldown.
Note: GITTENSORY_REVIEW_GROUNDING defaults false on hosted (wrangler.jsonc) but true in .env.selfhost.example — so the affected population is real specifically for self-host deployments, where grounding is the suggested default.
Requirements
- Cache fetched file content keyed by
(repoFullName, path, headSha) — content for an unchanged head SHA never needs re-fetching. A short-TTL or durable table (mirroring the signal_snapshots-style store repo-culture-profile.ts already uses) both work; durable is preferable since the key includes an immutable head SHA.
- Ensure a NEW head SHA (a genuine push) still gets a fresh fetch — this must not accidentally suppress re-grounding on real code changes.
- Ensure the fix doesn't interfere with the deliberate "dynamic feature bypasses the ai_review cache" design — this is a narrower, file-content-level cache underneath that decision, not a replacement for it.
- Invariant + regression tests (non-negotiable): an invariant test asserting a second
buildReviewGroundingText call for the SAME (repo, path, headSha) triple makes zero additional GitHub fetch calls; a regression test reproducing the exact bug shape (repeated non-push webhook events — e.g. two pull_request_review submissions 31+ minutes apart — on an unchanged head SHA, asserting the file-content fetch only happens once); a test confirming a genuinely new head SHA DOES trigger a fresh fetch (the negative case, so the cache can't accidentally go stale).
Deliverables
Expected outcome
Grounding's file-content fetch reuses prior file bodies for an unchanged head SHA instead of re-fetching every changed file from GitHub on every cooldown-expiry cycle or non-push webhook event, cutting real GitHub API load on any actively-discussed PR with grounding enabled — the exact population self-host deployments are pointed at by default.
References
src/review/grounding-wire.ts:126-170, 224-225 (the fetcher + call site)
src/review/review-grounding.ts:195-234 (fetchFullFileContents)
src/github/client.ts:107-132 (the shared cache class that doesn't cover this endpoint)
src/queue/processors.ts:9724-9725, 9811-9824, 9919 (dynamic-feature cache-bypass rationale)
src/review/rag.ts:439 (chunkCountCache — a precedent short-TTL cache pattern in a sibling feature)
wrangler.jsonc vs .env.selfhost.example (hosted-off / self-host-on default)
Effort
M
Part of #4496. P3 — medium severity, high confidence.
Context
buildReviewGroundingText's file-content half (src/review/grounding-wire.ts:224-225) re-fetches the ENTIRE post-change content of every changed file via a live GitHub Contents API call on every invocation:makeGithubFileFetcher(grounding-wire.ts:126-170) issues a liveGET /repos/{owner}/{repo}/contents/{path}?ref={ref}GET per file with NO cache/memoization keyed by(repo, path, ref)anywhere in the file — contrast the CI half of the same feature, which explicitly reuses cached data ("reuses the already-cached CI check summaries", file header lines 10-11). The shared GitHub response cache (src/github/client.ts:107-132,githubCacheClassForUrl) doesn't help either — it only classifies branch-protection, bare commit, and bare user/repo lookups as cacheable; thecontents/{path}endpoint matches none of those.Grounding is listed in
dynamicReviewFeatures(processors.ts:9724-9725), which unconditionally bypasses the durableai_reviewcache (processors.ts:9919,!dynamicReviewContextActive) — a known, deliberate tradeoff for features whose context can go stale for an unchanged head SHA. That leaves only the 30-minuteAI_REVIEW_NON_CACHEABLE_RETRY_COOLDOWN_MS(processors.ts:4089) as protection. Once that cooldown lapses, the full multi-file GitHub content re-fetch repeats from scratch for the identical head SHA — and this isn't limited to sweep ticks:maybePublishPrPublicSurfaceis also invoked from real webhook triggers unrelated to code changes (pull_request_review_comment,pull_request_review,pull_request_review_thread, perPR_PUBLIC_SURFACE_ACTIONS/PR_GATE_CLOSED_ACTIONS,processors.ts:6597-6614), none of which change the head SHA — so any actively-discussed PR with grounding enabled re-triggers the full re-fetch every time a comment/review event lands past the cooldown.Note:
GITTENSORY_REVIEW_GROUNDINGdefaultsfalseon hosted (wrangler.jsonc) buttruein.env.selfhost.example— so the affected population is real specifically for self-host deployments, where grounding is the suggested default.Requirements
(repoFullName, path, headSha)— content for an unchanged head SHA never needs re-fetching. A short-TTL or durable table (mirroring thesignal_snapshots-style storerepo-culture-profile.tsalready uses) both work; durable is preferable since the key includes an immutable head SHA.buildReviewGroundingTextcall for the SAME(repo, path, headSha)triple makes zero additional GitHub fetch calls; a regression test reproducing the exact bug shape (repeated non-push webhook events — e.g. twopull_request_reviewsubmissions 31+ minutes apart — on an unchanged head SHA, asserting the file-content fetch only happens once); a test confirming a genuinely new head SHA DOES trigger a fresh fetch (the negative case, so the cache can't accidentally go stale).Deliverables
(repoFullName, path, headSha), reused across calls for an unchanged headExpected outcome
Grounding's file-content fetch reuses prior file bodies for an unchanged head SHA instead of re-fetching every changed file from GitHub on every cooldown-expiry cycle or non-push webhook event, cutting real GitHub API load on any actively-discussed PR with grounding enabled — the exact population self-host deployments are pointed at by default.
References
src/review/grounding-wire.ts:126-170, 224-225(the fetcher + call site)src/review/review-grounding.ts:195-234(fetchFullFileContents)src/github/client.ts:107-132(the shared cache class that doesn't cover this endpoint)src/queue/processors.ts:9724-9725, 9811-9824, 9919(dynamic-feature cache-bypass rationale)src/review/rag.ts:439(chunkCountCache— a precedent short-TTL cache pattern in a sibling feature)wrangler.jsoncvs.env.selfhost.example(hosted-off / self-host-on default)Effort
M