feat(review): vision-verify screenshot-table PRs with the local VLM - #4691
Conversation
…4366) screenshot-table-gate.ts only checks markdown STRUCTURE (a table with image markup exists) -- a contributor can satisfy it with two identical images or a screenshot unrelated to the stated change. Adds an advisory-only check on top, split into two stages: 1. A free, deterministic byte-identical comparison (before/after fetched as base64 -- identical bytes need no AI call at all). 2. A bounded AI-vision call (BYOK or self-host env.AI_VISION, reusing #4335's binding) for genuinely different pairs, judging whether they still look near-identical or plausibly unrelated to the PR's title. Extracts image URLs from table cells (screenshot-table-gate.ts's extractTableRowImageUrls, handling bare ![]() cells, <img src>, and the PR template's [](link-url) clickable-thumbnail convention) and routes every URL through isSafeHttpUrl before fetching -- unlike every other consumer of fetchShotContentBlock, these are contributor-supplied, not server-constructed. No new config field: gated on the existing screenshotTableGate.enabled plus the same reputation/BYOK/aiReviewAllAuthors settings #4111's sibling visual-vision check already uses -- mirrors that precedent instead of inventing a second toggle. Mirrors visual-findings.ts's pure-decision-logic/live-caller split exactly (screenshot-table-vision.ts + runScreenshotTableVisionForAdvisory in processors.ts). Strictly advisory: the new finding code is absent from isConfiguredGateBlocker's allowlist, so it can never become a gate blocker. Also updates the hand-duplicated packages/gittensory-engine copy of screenshot-table-gate.ts (engine-parity:drift-check) with the same pure extraction functions.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | 021e64e | Commit Preview URL Branch Preview URL |
Jul 10 2026, 08:39 PM |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4691 +/- ##
==========================================
+ Coverage 94.14% 94.15% +0.01%
==========================================
Files 437 438 +1
Lines 38531 38614 +83
Branches 14049 14080 +31
==========================================
+ Hits 36274 36357 +83
Misses 1599 1599
Partials 658 658
🚀 New features to boost your workflow:
|
|
Caution 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🛑 Gittensory review result - fixes requiredReview updated: 2026-07-10 19:35:24 UTC
🛑 Suggested Action - Manual Review
Review summary Nits — 6 non-blocking
Why this is blocked
CI checks failing
Review context
Contributor next steps
Signal definitions
🟩 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.
|
…ion PR - Replace a fake API-key-shaped test fixture that tripped the secret-leak hard blocker (generic_secret_assignment) with an explicit fake-labeled value; scanned the full diff against the real detector to confirm clean. - Add missing coverage the gate flagged: the engine-package twin of extractTableRowImageUrls had zero test coverage (a different file from the host copy despite mirrored content), a fetch-partial-failure path, a null-author path, an explicit aiReviewProvider match/mismatch path, the BYOK "200 with no usable text" ternary arm, and the catch-block error path were all untested. - Mark one truly unreachable branch (extractCellImageUrl's fallback null) with a v8-ignore, matching this file's existing defensive-code convention. - Fix a real nit: two identical-image rows in the same PR produced two indistinguishable findings; each now names its row number.
Summary
screenshot-table-gate.ts's existing check is deterministic (no AI) and only verifies markdown STRUCTURE — a table with image-bearing cells exists. It has no way to catch a contributor pasting two identical images, an unrelated screenshot, or otherwise gaming the gate. This adds an advisory-only vision check on top, never a gate blocker.env.AI_VISION, reusing Stretch: evaluate spare GPU headroom for a local vision-language model in visual-review #4335's existing binding) for pairs that survive stage 1 — asks whether the two images still look near-identical (a re-encoded duplicate a byte comparison would miss) or plausibly unrelated to the PR's stated title.extractTableRowImageUrlsinscreenshot-table-gate.ts) handles bare![]()cells,<img src>tags, and the PR template's own clickable-thumbnail convention ([](link-url)— correctly targets the inner image URL, not the outer link).isSafeHttpUrlbefore fetching — unlike every existingfetchShotContentBlockcaller (which only ever fetches the bot's own server-constructed shot URLs), these come from contributor-supplied PR body text.screenshotTableGate.enabled(the deterministic gate must already be opted into) plus the same reputation/BYOK/aiReviewAllAuthorssettings feat(review): advisory-only AI-vision analysis of before/after visual captures #4111's sibling visual-vision check already uses — mirrors that precedent instead of inventing a second per-repo toggle, which keeps this a much smaller, better-precedented change than a brand-new.gittensory.ymlfield would require (DB migration + Drizzle schema + settings resolver + OpenAPI + yml schema, none of which feat(review): advisory-only AI-vision analysis of before/after visual captures #4111 needed either).Closes #4366
Design notes
visual-findings.ts's pure-decision-logic / live-caller split exactly:src/review/visual/screenshot-table-vision.ts(gate, prompt, response parsing, finding construction — no I/O) +runScreenshotTableVisionForAdvisoryinprocessors.ts(fetch, byte-compare, BYOK/self-host resolution, the actual AI call).SCREENSHOT_TABLE_VISION_FINDING_CODEis absent fromisConfiguredGateBlocker's allowlist, so this can never become a gate blocker — same pattern asvisual_regression_finding, verified by a regression test assertinggate.conclusionstays"success"even under a maximally strict policy.packages/gittensory-engine/src/review/screenshot-table-gate.tsis a hand-duplicated twin (engine-parity:drift-check,scripts/check-engine-parity.ts) — updated with the identical pure extraction functions (import-path-normalized per that script's own convention). The AI-calling code stays out of that package entirely, matching its "pure, deterministic only" scope.MAX_VISION_ROUTESin the sibling feat(review): advisory-only AI-vision analysis of before/after visual captures #4111 check) so a long table can't translate into unbounded fetch/vision spend.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally;codecov/patchrequires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderatenpm run engine-parity:drift-check— the hand-duplicated engine-package twin was updated in the same commit and verified in sync.If any required check was skipped, explain why:
actionlint/test:workers/build:mcp/test:mcp-pack/ui:*not applicable — nopackage.json,Dockerfile, orwrangler.jsoncchanges this time).test:coveragewas run targeted on the exact touched/related test files (297 tests across 8 files, all green — screenshot-table-vision pure module 100% stmt/branch/func, screenshot-table-gate.ts 100% stmt, 99.2% branch (162/162, 124/125), the newprocessors.tswiring exercised by 14 dedicated tests) rather than the full unsharded suite. Also rannpm --workspace @jsonbored/gittensory-engine run buildand its own test suite (349 tests) directly since this PR touches that workspace package.Safety
VISUAL_VISION_SYSTEM_PROMPT's own convention.)UI Evidencesection below. (N/A — no visible UI change; this only ever adds an advisory finding to the existing "Visual findings" collapsible)UI Evidence
N/A — no UI/frontend/docs-visible change.
Notes
screenshotTableGate.enabled: truefor a repo AND a configured vision provider (BYOK or self-hostAI_VISION), neither of which is currently set for any of the 3 gate repos, so there's no live PR traffic to validate against yet. The unit/wiring test suite covers the same scenarios (identical images, unrelated images via a mocked vision response, SSRF-unsafe URLs, reputation/quota gating) that manual validation would otherwise spot-check.