Skip to content

fix(review): cache grounding's full-file-content fetch by (repo, path, head SHA)#4538

Merged
JSONbored merged 2 commits into
mainfrom
claude/fix-grounding-file-content-cache
Jul 10, 2026
Merged

fix(review): cache grounding's full-file-content fetch by (repo, path, head SHA)#4538
JSONbored merged 2 commits into
mainfrom
claude/fix-grounding-file-content-cache

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Closes #4499.

Summary

  • makeGithubFileFetcher (src/review/grounding-wire.ts) re-fetched every changed file's full post-change body from GitHub on every invocation with zero cache/memoization. Grounding is a dynamic feature (bypasses the durable ai_review result cache), so the only throttle was the 30-minute AI_REVIEW_NON_CACHEABLE_RETRY_COOLDOWN_MS cooldown — after which, or on any non-push webhook event (review comments, review submissions, thread resolutions — none of which change the head SHA), the full multi-file re-fetch repeated from scratch.
  • Adds grounding_file_content_cache (migration 0130), keyed by (repo_full_name, path, head_sha) — deliberately without a pull number, unlike linked_issue_satisfaction_cache: file content at an immutable git commit is universal, not PR-specific, so two PRs that happen to share a (repo, path, head_sha) triple (e.g. a cherry-pick) correctly share one cached row.
  • Only a genuinely successful fetch is cached — a transient network/timeout failure stays retryable rather than being treated as a confirmed-permanent (binary/oversized/inaccessible) condition. The existing oversized-file placeholder is cached too, since it's a deterministic function of the file at that commit.
  • GITTENSORY_REVIEW_GROUNDING defaults off on hosted, on in .env.selfhost.example — self-host deployments are the population most exposed to this bug.

Scope

Validation

  • npm run typecheck
  • npm run db:migrations:check / npm run db:schema-drift:check (both clean, re-verified after rebase)
  • New tests added and empirically verified to FAIL without the fix (temporarily reverted grounding-wire.ts locally): a repeat-call invariant test (2 fetches → 1) and a regression test reproducing the exact incident shape (5 cooldown-driven passes on an unchanged head → 5 fetches without the fix, 1 with it). Also added negative-path coverage: a genuinely new head SHA still fetches fresh, and a failed fetch is never cached (stays retryable).
  • npx vitest run test/unit/grounding-wiring.test.ts test/unit/review-grounding.test.ts test/unit/impact-map-grounding.test.ts test/unit/queue.test.ts (830 passed)

If any required check was skipped, explain why:

  • Full npm run test:coverage not re-run unsharded locally; this is an additive change (new table + one function's internal caching, no shared-interface changes) with a broad targeted run already covering grounding + the full queue integration suite. Relying on CI for the full gate.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • N/A — no auth/cookie/CORS/GitHub App/Cloudflare/session changes.
  • N/A — no API/OpenAPI/MCP behavior changed (no route/schema touched).
  • N/A — no UI changes.

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.04%. Comparing base (95e9ec6) to head (f1f6d68).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4538   +/-   ##
=======================================
  Coverage   94.04%   94.04%           
=======================================
  Files         422      422           
  Lines       37600    37609    +9     
  Branches    13736    13740    +4     
=======================================
+ Hits        35362    35371    +9     
  Misses       1583     1583           
  Partials      655      655           
Files with missing lines Coverage Δ
src/db/repositories.ts 96.73% <100.00%> (+<0.01%) ⬆️
src/db/schema.ts 72.43% <100.00%> (+0.30%) ⬆️
src/review/grounding-wire.ts 96.47% <100.00%> (+0.08%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 9, 2026
@loopover-orb

loopover-orb Bot commented Jul 9, 2026

Copy link
Copy Markdown

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-10 00:11:21 UTC

5 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This adds a durable (repo, path, head_sha) cache for grounding's full-file-content GitHub fetch, correctly keyed without a pull number since blob content at an immutable SHA is universal. The cache-read happens before the network call and only a genuinely successful fetch (or the deterministic oversized-placeholder) is written back via ON CONFLICT upsert; non-OK responses and thrown exceptions return early and bypass the write, keeping transient failures retryable. Schema, migration (D1-safe, no temp/PRAGMA/ATTACH), and repository bind-order all line up correctly, and the new tests exercise the hit/miss/new-SHA/failed-fetch paths against the real code path.

Nits — 6 non-blocking
  • migrations/0130_grounding_file_content_cache.sql:14 uses a static `DEFAULT CURRENT_TIMESTAMP`, but repositories.ts always supplies `fetched_at` explicitly via `nowIso()` on insert, so the SQL default is dead code — harmless here since every write is explicit, but worth aligning with the `$defaultFn(() => nowIso())`-only convention if a future writer ever omits the column.
  • codecov/patch is failing at 71.42% vs a 99% target — the untested branches look like the nullish/no-op arms in src/db/repositories.ts (`if (!headSha) return null;` / `if (!headSha) return;`) and the `.catch(() => null)` / `.catch(() => undefined)` fallback paths in grounding-wire.ts when the cache read/write itself throws.
  • src/review/grounding-wire.ts: `let content: string | null` is only ever assigned a non-null string via the ternary before the `content !== null` check, so that guard is either dead or silently relying on an unseen null return from `readTextWithLimit` — worth a comment or test making clear which case it actually guards against.
  • Add a direct unit test for `getCachedGroundingFileContent`/`putCachedGroundingFileContent` in src/db/repositories.ts covering a null/undefined `headSha` no-op, to close the coverage gap flagged by codecov/patch.
  • Add a test where the cache read itself throws (DB error) to confirm the `.catch(() => null)` fallback still falls through to a fresh GitHub fetch rather than silently failing.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #4499
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 48 registered-repo PR(s), 40 merged, 363 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 48 PR(s), 363 issue(s).
Gate result ✅ Passing No configured blocker found.
Linked issue satisfaction

Addressed
The PR adds a durable grounding_file_content_cache keyed exactly by (repo_full_name, path, head_sha), wires getFileContent to check it before any GitHub call and only writes back genuinely successful fetches, and includes the required invariant, regression (repeated non-push webhook calls on an unchanged head), and negative-path (new head SHA re-fetches) tests plus extra edge-case coverage for cac

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 48 PR(s), 363 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 9, 2026
JSONbored added 2 commits July 9, 2026 16:59
…, head SHA)

makeGithubFileFetcher re-fetched every changed file's full post-change body
from GitHub on every invocation with zero caching. Grounding bypasses the
durable ai_review result cache (a dynamic feature), so the only throttle was
the 30-minute non-cacheable cooldown -- after which, or on any non-push
webhook event (review comments, review submissions, thread resolutions --
none of which change the head SHA), the full multi-file re-fetch repeated
from scratch.

Adds grounding_file_content_cache, keyed by (repo, path, head_sha) without a
pull number (file content at an immutable commit is universal, not
PR-specific). Only a genuinely successful fetch is cached -- a transient
network/timeout failure stays retryable rather than being treated as a
confirmed-permanent condition.

Closes #4499.
…t cache

Adds direct tests for getCachedGroundingFileContent/putCachedGroundingFileContent's
nullish-headSha no-op paths, and for the cache read/write fail-safe .catch()
handlers (a throwing DB read/write must never block the underlying file
fetch). Documents two genuinely-unreachable-via-the-real-code-path branches
(a Drizzle-query-builder-only schema default never exercised by the raw-SQL
write path, and readTextWithLimit's defensive-only null return type) with
v8 ignore comments explaining why.
@JSONbored
JSONbored force-pushed the claude/fix-grounding-file-content-cache branch from 0bd0523 to f1f6d68 Compare July 9, 2026 23:59
@JSONbored
JSONbored merged commit 9479b2b into main Jul 10, 2026
12 checks passed
@JSONbored
JSONbored deleted the claude/fix-grounding-file-content-cache branch July 10, 2026 00:37
JSONbored added a commit that referenced this pull request Jul 10, 2026
JSONbored added a commit that referenced this pull request Jul 10, 2026
JSONbored added a commit that referenced this pull request Jul 10, 2026
JSONbored added a commit that referenced this pull request Jul 10, 2026
JSONbored added a commit that referenced this pull request Jul 10, 2026
JSONbored added a commit that referenced this pull request Jul 10, 2026
JSONbored added a commit that referenced this pull request Jul 10, 2026
…-wide for confirmed miners (#4546)

* fix(review): make submitter-reputation burst/AI-spend defense install-wide for confirmed miners

getSubmitterReputation's burst/low-sample thresholds are scoped
WHERE project = ? AND submitter = ? -- a single repo. A fleet
identity spreading a handful of gate-passing-but-low-value PRs
across dozens of repos in one self-hosted install never accumulates
enough same-repo sample density to read as "burst" or "low"
anywhere, so the reputation defense never fires for it and every one
of its submissions burns full paid AI-review spend indefinitely.

- New getSubmitterReputationAcrossInstall in submitter-reputation.ts:
  the identical quality-weighted signal derivation, scoped by
  review_targets.installation_id instead of project (that column
  already existed, migrations/0050; this adds the supporting index).
- New getEffectiveSubmitterReputation in reputation-wire.ts: the
  per-repo signal, additionally widened to the install-wide view for
  a CONFIRMED official Gittensor miner -- but only when the per-repo
  signal alone doesn't already justify caution, so an ordinary
  contributor or an already-flagged submitter pays no extra lookup.
- Extracted the miner-identity check (shared with #4512) into
  src/gittensor/miner-detection-cache.ts so both this module and
  unlinked-issue-guardrail.ts can use it without a circular import
  through processors.ts.
- Wired into both call sites: shouldSkipAiForReputation (the main
  AI-spend gate) and the vision-review reputation check.

Fixes #4513

* fix: renumber migration 0130 -> 0131 (0130 was claimed by #4538, already merged to main)

* fix(test): account for getEffectiveSubmitterReputation's miner-identity check in visual-vision tests

runVisualVisionForAdvisory now resolves reputation via getEffectiveSubmitterReputation
(#4513), which checks confirmed-official-miner identity (a fetch to
api.gittensor.io/miners) whenever the submitter's per-repo signal is
neutral -- a real, intentional behavior change this test file's existing
"no network calls at all" assertions didn't account for. Stub that one
identity check to resolve cleanly and assert precisely that no OTHER
(BYOK/vision-spend) network call happens, rather than asserting zero fetch
calls outright.

* fix(db): renumber migration 0131 -> 0133 (0131/0132 claimed by merged PRs)

origin/main has since merged #4545 (0131_screenshot_table_gate_matrix.sql)
and #4554 (0132_impact_map_query_cache.sql, itself a collision fix); rebase
onto current main and take the next free number.

* fix(db): renumber migration 0133 -> 0134 (0133 claimed by merged PR #4556)

Rebase onto current main and take the next free number now that
migrations/0133_screenshot_table_gate_skill_link.sql (from #4556) occupies
the number this branch previously took.

* fix(db): renumber migration 0134 -> 0139 (0134 has a pre-existing 3-way collision on main)

main currently has three DIFFERENT already-merged files at 0134
(#4549/#4558/#4563) -- tracked separately as its own fix (PR #4577, not yet
merged). Since that fix already claims through 0138, take 0139 here to
avoid colliding with it once it lands; this branch's own
db:migrations:check will stay red until #4577 merges (unrelated to this
branch's own changes), and will need one more rebase afterward.

* test(review): close 2 coverage gaps surfaced by the #4514 rebase-merge

getEffectiveSubmitterReputation's getRepository().catch() needed a real
read-failure test (added); its isConfirmedOfficialMiner().catch() is
unreachable (that function already catches every internal failure point
itself) and getSubmitterReputationAcrossInstall's results ?? [] fallback is
the same "D1 always populates results" case already v8-ignored elsewhere in
this codebase -- both marked accordingly.

* fix(review): isolate #4507's reputation-single-read invariant from cross-test miner-cache pollution

Several earlier tests in this file cache "contributor" as a confirmed official
Gittensor miner (5-min TTL) in the shared per-file D1 instance. That collided
with #4513's new install-wide reputation widening, which correctly detects the
stale cache and adds a 4th reputation-scan D1 read for a submitter this test
never intended to be a miner. Use a submitter login unique to this test instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Development

Successfully merging this pull request may close these issues.

fix(review): grounding re-fetches every changed file's full body every 30 min with zero caching

1 participant