Skip to content

feat(review): wire up pixel-diff into a Diff column (self-host) - #3677

Merged
JSONbored merged 2 commits into
mainfrom
feat/selfhost-visual-diff-column
Jul 6, 2026
Merged

feat(review): wire up pixel-diff into a Diff column (self-host)#3677
JSONbored merged 2 commits into
mainfrom
feat/selfhost-visual-diff-column

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Wires an existing, previously-unused screenshot-comparison module (under src/visual-agent/) into the before/after capture pipeline. It was ported during an earlier convergence effort with full test coverage but zero production callers.
  • The module depends on Node's Buffer and a native-leaning PNG-decode step, which the Cloudflare Workers runtime doesn't guarantee — test/unit/worker-entry-boundary.test.ts forbids importing (or even naming) it from the Worker entry. This PR introduces a module-swap seam (src/review/visual/pixel-diff.ts, a Worker-safe no-op by default) that scripts/build-selfhost.mjs's existing esbuild plugin swaps for a real implementation only when bundling the self-host entry — the exact same substitution pattern already used for @cloudflare/puppeteer in that same build. The Worker's own (wrangler) bundle never applies that swap, so hosted mode is byte-identical: isVisualDiffAvailable() stays false, zero added cost.
  • capturePage (in capture.ts) now optionally returns the raw screenshot bytes alongside the URL — including on a cache hit, which is the common case for "before" (the same production shot is reused across many PR reviews) — only when a caller opts in, so every existing call site is unaffected.
  • buildCapture compares before/after per viewport when diffing is available and uploads the resulting diff image through the same store screenshots already use.
  • unified-comment-bridge.ts's "Visual preview" table gains a 5th Diff column: a clickable diff-overlay thumbnail when the comparison clears the noise threshold, a dash otherwise — making a visual change self-evident instead of requiring a reviewer to eyeball two full-page images side by side.
  • Self-host only for this phase — hosted-mode diff-overlay needs its own follow-up (a Workers-compatible PNG-decode path). Covers the maintainer's own self-hosted reviews (this repo + metagraphed) end to end today.

Part of #3607. Closes #3674.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked an issue, or this is small enough that the summary explains why an issue is not needed.

Validation

  • git diff --check
  • npm run actionlint (not run — no workflow files changed)
  • npm run typecheck
  • npm run test:coverage locally; codecov/patch requires ≥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:workers (not run — no Worker-runtime-specific code beyond the existing capture.ts/unified-comment-bridge.ts modules, both already covered by the targeted suite below)
  • npm run build:mcp (not run — no MCP package changes)
  • npm run test:mcp-pack (not run — no MCP package changes)
  • npm run ui:openapi:check (not run — no API/OpenAPI changes)
  • npm run ui:lint (not run — no apps/gittensory-ui/** changes)
  • npm run ui:typecheck (not run — no apps/gittensory-ui/** changes)
  • npm run ui:build (not run — no apps/gittensory-ui/** changes)
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • Only src/review/visual/capture.ts, src/review/unified-comment-bridge.ts, src/review/visual/pixel-diff.ts (new), src/selfhost/stubs/pixel-diff.ts (new), and scripts/build-selfhost.mjs changed — no UI, MCP, wrangler binding, OpenAPI, or workflow files touched. Ran npx tsc --noEmit -p . (full project, clean) and the full affected test suite (95 tests across 10 files: the new pixel-diff.test.ts and selfhost-pixel-diff-stub.test.ts, extended visual-capture.test.ts and visual-collapsible.test.ts, plus every neighboring visual-* test and worker-entry-boundary.test.ts to prove the boundary wasn't violated) — all passing.
  • Real end-to-end verification beyond unit tests: ran the actual node scripts/build-selfhost.mjs build and grepped the resulting dist/server.mjs bundle, confirming the real comparison module (compareRouteScreenshots, plus its own dependencies) is genuinely present in the self-host bundle — not just that the esbuild resolver rule looked correct on paper.

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.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. (N/A — no auth/session/CORS changes.)
  • API/OpenAPI/MCP behavior is updated and tested where needed. (N/A — no API/OpenAPI/MCP surface touched.)
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. (N/A — no UI changes; this is the review-engine's own comment-rendering logic.)
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots. (N/A — no visible UI surface of gittensory itself changed; this changes what the bot posts in a GitHub comment, not a UI page.)
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. (No docs changes needed.)

Notes

Wires the existing, previously-unused screenshot-comparison module
under src/visual-agent/ into the before/after capture pipeline via a
module-swap seam (src/review/visual/pixel-diff.ts, a Worker-safe
no-op by default) that scripts/build-selfhost.mjs's esbuild plugin
swaps for a real implementation when bundling the self-host entry --
the same substitution pattern already used for @cloudflare/puppeteer
in that build. The Worker's own bundle never applies that swap, so
hosted mode is byte-identical (isVisualDiffAvailable() stays false,
zero added cost).

capturePage now optionally returns the raw screenshot bytes alongside
the URL -- including on a cache hit, the common case for "before"
(the same production shot is reused across many PR reviews) -- only
when a caller opts in, so every existing call site is unaffected.
buildCapture compares before/after per viewport when diffing is
available and uploads the result through the same store screenshots
already use.

unified-comment-bridge.ts's "Visual preview" table gains a 5th Diff
column: a clickable diff-overlay thumbnail when the comparison clears
the module's own noise threshold, a dash otherwise -- making a visual
change self-evident instead of requiring a reviewer to eyeball two
full-page images side by side.

Verified with a real `node scripts/build-selfhost.mjs` build: grepped
the resulting bundle to confirm the real comparison module (not the
no-op) is what actually gets included for self-host.

Part of #3607. Closes #3674.
@superagent-security

Copy link
Copy Markdown
Contributor

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

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 6, 2026
@loopover-orb

loopover-orb Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-06 01:50:30 UTC

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

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review: This PR changes guardrail-protected path(s): src/selfhost/stubs/pixel-diff.ts (matched src/selfhost/**).

Review summary
This PR wires the previously dead-code visual-diff module into the capture pipeline via a clean seam pattern (Worker-safe no-op swapped for a real self-host implementation at build time via esbuild's onResolve, mirroring the existing @​cloudflare/puppeteer substitution), then threads a diffUrl/diffUrlMobile through buildCapture into a new 'Diff' column in the unified comment. The wiring is correct: capturePage's includeBytes opt-in is truly zero-cost for every existing caller (false by default), diffAvailable is resolved once and gates both the extra cached-bytes read and the compare call, and uploadDiffImage degrades safely (no REVIEW_AUDIT/shotBase, failed put, null diffImagePng) without ever breaking the existing before/after cells. Test coverage is unusually thorough for this kind of pipeline change (cache-hit bytes, fresh-render bytes, broken-body degrade, failed put, unavailable-diff no-op path), though codecov/patch still landed at 89.28% against a 99% target.

Nits — 6 non-blocking
  • The codecov/patch check failed at 89.28% vs the repo's 99% patch-coverage target — worth checking which specific branches in capture.ts/unified-comment-bridge.ts (e.g. the REVIEW_AUDIT-present-but-shotBase-absent combination in uploadDiffImage, or the mobile-only hasAnyDiff branch) are the uncovered lines before merging.
  • The PR description says 'Diff' is only shown when the comparison 'clears the noise threshold' but the actual gating in capture.ts is simply `diff?.diffImagePng` being non-null — if the self-host module's own noise threshold already suppresses diffImagePng for sub-threshold changes this is fine, but it's worth confirming that threshold lives in `compareRouteScreenshots` (src/visual-agent/visual-diff) rather than being asserted without a visible check in this diff.
  • Add a coverage-focused pass over src/review/visual/capture.ts and unified-comment-bridge.ts to find the ~11% of patch lines/branches codecov flagged as uncovered before merge.
  • Consider a short code comment at src/review/visual/capture.ts's uploadDiffImage on why the diff-image key derives from headSha/prNumber rather than mirroring capturePage's own fingerprint inputs, so a future reader doesn't assume it's a copy-paste inconsistency.
  • 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.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #3674
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High 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: 55 registered-repo PR(s), 46 merged, 457 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 55 PR(s), 457 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: Python, TypeScript, JavaScript, Ruby, Go, Kotlin, MDX, Shell
  • Official Gittensor activity: 55 PR(s), 457 issue(s).
  • Related work: Titles/paths share 5 meaningful terms. (issue #2189, issue #1681)
  • Related work: Titles/paths share 5 meaningful terms. (issue #1683, issue #1681)
  • Related work: Titles/paths share 5 meaningful terms. (issue #1680, issue #1681)
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • No action.
  • Check active issues and PRs before submitting.
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

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.33%. Comparing base (33a7d4b) to head (3073cbb).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3677      +/-   ##
==========================================
+ Coverage   93.29%   93.33%   +0.04%     
==========================================
  Files         314      315       +1     
  Lines       32058    32080      +22     
  Branches    11749    11762      +13     
==========================================
+ Hits        29907    29942      +35     
+ Misses       1517     1507      -10     
+ Partials      634      631       -3     
Files with missing lines Coverage Δ
src/review/unified-comment-bridge.ts 99.35% <100.00%> (+0.01%) ⬆️
src/review/visual/capture.ts 86.59% <100.00%> (+17.46%) ⬆️
src/review/visual/pixel-diff.ts 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 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 manual-review Gittensor contributor context label Jul 6, 2026
codecov/patch flagged 89.29% on the prior commit -- precise per-branch
analysis (not the human-readable summary, which turned out unreliable
for pinpointing exact gaps) found 4 real branches new to this PR that
no existing test reached:
- a cache hit with includeBytes=false (diffing unavailable, the
  common/default case) returning just {url}, no bytes
- a cache hit whose body fails to read back, degrading to no bytes
  rather than throwing
- a fresh successful render's two return shapes: with bytes (diffing
  available) and without (the default), not just the cache-hit sides
  of that same branch
- uploadDiffImage's put() failing, degrading to still returning the
  (deterministic, key-derived) URL rather than losing it -- mirrors
  capturePage's own identical fire-and-forget persistence pattern

Verified via raw v8 coverage-final.json branch counts cross-referenced
against Codecov's own added-lines list, not the summary text alone.
@JSONbored
JSONbored merged commit bb04de7 into main Jul 6, 2026
11 checks passed
@JSONbored
JSONbored deleted the feat/selfhost-visual-diff-column branch July 6, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(review): wire up existing pixel-diff into a 'Diff' column (self-host)

1 participant