feat(seer): Add failing check run URLs to autofix overview API - #122486
Merged
NicoHinderling merged 3 commits intoAug 24, 2026
Conversation
The autofix overview surfaces failing PR checks by name only, so the
frontend cannot link a failing check to its run. Capture each check's
run URL from GitHub (detailsUrl for check runs, targetUrl for legacy
statuses) and expose it on the API.
Add a new failedCheckDetails field ({name, url}) alongside the existing
failedChecks name list rather than changing failedChecks in place, so
the currently-deployed frontend keeps working across the deploy window.
A follow-up frontend PR will read failedCheckDetails to link each check.
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ae2991b. Configure here.
A failing check's run URL comes verbatim from GitHub's detailsUrl / targetUrl. StatusContext.targetUrl is set by third-party CI apps, so it is attacker-influenceable and could be a javascript:/data: URI. Sentry's frontend link primitives do not sanitize the scheme, so once the follow-up frontend renders these as anchor hrefs an unsafe scheme becomes DOM XSS in the sentry.io origin. Make http(s)-only a construction invariant of FailedCheck: any other scheme (or non-string) is coerced to None, protecting every consumer of the field.
This was referenced Aug 24, 2026
NicoHinderling
added a commit
that referenced
this pull request
Aug 24, 2026
get_pull_request_statuses caches PullRequestStatusResult for 60s. This branch changed failed_checks from tuple[str, ...] to tuple[FailedCheck, ...], but the isinstance(cached, PullRequestStatusResult) guard still accepts an entry pickled by the old code, whose failed_checks are bare strings. The overview serializer would then call .name/.url on a str and 500 for up to the cache TTL after deploy. Bump the cache-key namespace to /v2 so the new code never reads old-shape entries; they expire on their own. Flagged by Cursor Bugbot and the Sentry PR bot on #122486.
NicoHinderling
added a commit
that referenced
this pull request
Aug 24, 2026
get_pull_request_statuses caches PullRequestStatusResult for 60s. This branch changed failed_checks from tuple[str, ...] to tuple[FailedCheck, ...], but the isinstance(cached, PullRequestStatusResult) guard still accepts an entry pickled by the old code, whose failed_checks are bare strings. The overview serializer would then call .name/.url on a str and 500 for up to the cache TTL after deploy. Bump the cache-key namespace to /v2 so the new code never reads old-shape entries; they expire on their own. Flagged by Cursor Bugbot and the Sentry PR bot on #122486.
NicoHinderling
force-pushed
the
nico/feat/seer-overview-failing-check-run-urls
branch
from
August 24, 2026 17:53
f985830 to
643ff03
Compare
get_pull_request_statuses caches PullRequestStatusResult for 60s. This branch changed failed_checks from tuple[str, ...] to tuple[FailedCheck, ...], but the isinstance(cached, PullRequestStatusResult) guard still accepts an entry pickled by the old code, whose failed_checks are bare strings. The overview serializer would then call .name/.url on a str and 500 for up to the cache TTL after deploy. Bump the cache-key namespace to /v2 so the new code never reads old-shape entries; they expire on their own. Flagged by Cursor Bugbot and the Sentry PR bot on #122486.
NicoHinderling
force-pushed
the
nico/feat/seer-overview-failing-check-run-urls
branch
from
August 24, 2026 17:59
643ff03 to
c3a6043
Compare
mtopo27
approved these changes
Aug 24, 2026
NicoHinderling
deleted the
nico/feat/seer-overview-failing-check-run-urls
branch
August 24, 2026 19:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
The Autofix Overview page shows a "N Checks Failing" pill whose tooltip lists each failing PR check by name. Those names are dead text — there's no way to jump from a failing check to its CI run.
This backend change captures each failing check's run URL from GitHub and exposes it on the autofix-overview API so a follow-up frontend PR can link each check to its run.
What changed
pull_request_status.py): fetchdetailsUrlonCheckRunnodes (the external run page) andtargetUrlon legacyStatusContextnodes.PullRequestStatusResult.failed_checks: now a tuple of a newFailedCheck(name, url)dataclass instead of bare strings. URL isNonewhen the provider doesn't give one.failedCheckDetails: [{name, url}]field onPullRequestPayload.Deploy safety
Frontend and backend don't deploy atomically. Rather than change the shape of the existing
failedChecks: string[]field (which would make the currently-deployed frontend try to render objects as text and crash the tooltip during the deploy window), this addsfailedCheckDetailsand leavesfailedChecksuntouched. The follow-up frontend PR reads the new field; a later cleanup PR can dropfailedChecksonce the frontend is out.Tests
detailsUrl, status-contexttargetUrl, and null URLs.failedChecks(names, unchanged) and the newfailedCheckDetails.All affected backend suites pass; prek + mypy clean.
Security
The check
urlis provider/CI-controlled (StatusContext.targetUrlis set by third-party CI apps), and Sentry's frontend link primitives don't sanitize the URL scheme. To prevent ajavascript:/data:link from becoming a DOM-XSS sink once the frontend renders it as an anchor,FailedCheckenforces an http(s)-only invariant at construction — any other scheme (or a non-string) becomesNone.