Summary
Extend the existing before/after AI-vision regression check (src/review/visual/visual-findings.ts, gated on pixel-diff + reputation + a vision-capable provider) with a review.visual.bugAnalysis config: when enabled, the SAME vision call also sees the PR's own stated title/description and classifies each observation as:
regression — a genuine defect this PR's own change introduced (identical rendering/behavior to today)
unrelated — a real visual problem visible in either screenshot that has nothing to do with this PR's stated change (a pre-existing bug on the same page). Surfaced as its own advisory finding with a suggestion to open a separate issue to track it — never phrased as if this PR broke it.
Both finding types stay strictly advisory (never a gate blocker), matching the existing visual_regression_finding contract.
Scope
VISUAL_BUG_ANALYSIS_SYSTEM_PROMPT + buildVisualBugAnalysisUserPrompt — a separate, PR-context-aware prompt (not a mutation of the existing default prompt, so a repo that hasn't opted in sees byte-identical model behavior)
parseVisualVisionResponse parses an optional category field; absent/unrecognized defaults to regression (today's behavior)
- New
VISUAL_UNRELATED_ISSUE_FINDING_CODE finding, rendered in the same "Visual findings" comment section
- Global + per-repo configurable via
.loopover.yml (review.visual.bugAnalysis, config-as-code), off by default
Motivation
Catch more real problems earlier without expanding what counts as "this PR's fault" — a screenshot-based review already has the pixels in front of it; today it only ever asks "did THIS PR break something."
Summary
Extend the existing before/after AI-vision regression check (
src/review/visual/visual-findings.ts, gated on pixel-diff + reputation + a vision-capable provider) with areview.visual.bugAnalysisconfig: when enabled, the SAME vision call also sees the PR's own stated title/description and classifies each observation as:regression— a genuine defect this PR's own change introduced (identical rendering/behavior to today)unrelated— a real visual problem visible in either screenshot that has nothing to do with this PR's stated change (a pre-existing bug on the same page). Surfaced as its own advisory finding with a suggestion to open a separate issue to track it — never phrased as if this PR broke it.Both finding types stay strictly advisory (never a gate blocker), matching the existing
visual_regression_findingcontract.Scope
VISUAL_BUG_ANALYSIS_SYSTEM_PROMPT+buildVisualBugAnalysisUserPrompt— a separate, PR-context-aware prompt (not a mutation of the existing default prompt, so a repo that hasn't opted in sees byte-identical model behavior)parseVisualVisionResponseparses an optionalcategoryfield; absent/unrecognized defaults toregression(today's behavior)VISUAL_UNRELATED_ISSUE_FINDING_CODEfinding, rendered in the same "Visual findings" comment section.loopover.yml(review.visual.bugAnalysis, config-as-code), off by defaultMotivation
Catch more real problems earlier without expanding what counts as "this PR's fault" — a screenshot-based review already has the pixels in front of it; today it only ever asks "did THIS PR break something."