Skip to content

feat(seer): Run-anchored notify_seer_pr_created RPC for PR attribution (CW-1719)#120583

Open
vaind wants to merge 1 commit into
masterfrom
ivan/cw-1719-notify-seer-pr-created
Open

feat(seer): Run-anchored notify_seer_pr_created RPC for PR attribution (CW-1719)#120583
vaind wants to merge 1 commit into
masterfrom
ivan/cw-1719-notify-seer-pr-created

Conversation

@vaind

@vaind vaind commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Problem

Seer records PR attribution and a run↔PR link only as a side effect of the autofix completion hook: send_seer_webhookprocess_autofix_updates handles the seer.pr_created event, but that path requires both a run_id and a group_id (it bails early on missing_identifiers). Any PR produced by a run that isn't tied to an issue (group_id is None) never reaches the attribution/linking code, so those PRs silently lose attribution and their run link.

What this does

Adds a dedicated, run-anchored, issue-optional inbound seer-rpc method, notify_seer_pr_created, that Seer's PR writer will call for every PR it opens. The receiver:

  • Resolves the org exactly like send_seer_webhook (active-org lookup; returns a typed error response if missing/inactive).
  • Records the SENTRY_APP/SEER_DATA attribution — still gated on organizations:pr-metrics-attribution.
  • Always links the PR to its SeerRun — still gated only on its own seer.pull-request-linking.killswitch.enabled killswitch.
  • Emits seer.pr_created_notify, tagged with whether a group was present.

Both gates are inherited by reusing the existing attribute_seer_created_pull_requests / link_seer_run_pull_requests helpers via a small shared record_seer_created_pull_requests wrapper — no new killswitch or option.

RPC contract

notify_seer_pr_created(
    organization_id: int,
    run_id: int,                    # Seer's DbRunState.id / seer_run_state_id
    pull_requests: list[dict],      # single-element list per created PR
    group_id: int | None = None,
)

Each pull_requests entry:

{"provider": <str|None>, "repo_name": "owner/name",
 "pull_request": {"pr_id": <int>, "pr_number": <int>, "pr_url": <str>}}

This is byte-for-byte the entry shape attribute_seer_created_pull_requests and link_seer_run_pull_requests already read.

Why ungated / no broadcast

This is deliberately independent of the completion hook. It does not check SeerAutofixOperator.has_access, and does not broadcast to sentry-apps, create an Activity, or run analytics — those concerns stay on the completion hook. The point is that attribution + linking must happen for a superset of PRs (including group-less runs), so this path must not be gated on autofix operator access.

Legacy write intentionally kept

The existing attribution/link write inside process_autofix_updates (the SEER_PR_CREATED branch) is kept as-is in this PR. The stacked Phase-2 draft PR removes it, and must stay draft until this PR is deployed and Seer's writer path is live. The shared record_seer_created_pull_requests wrapper is introduced here so Phase 2 is a trivial redirect + delete.

Tests run vs deferred

Run locally against the provisioned test DB (all green):

  • tests/sentry/seer/test_pull_requests.py — 14 passed (6 new NotifySeerPrCreatedTest cases: group-less link+attribution, group_ids populated, idempotent redelivery, links regardless of operator access, links-without-attribution when flag off, org-not-found error).
  • tests/sentry/seer/endpoints/test_seer_rpc.py -k "registry or method or send_seer_webhook" — 27 passed (registry/dispatch unaffected).
  • mypy on all changed source files — passed (seer-rpc plugin accepts the concrete union return).
  • prek run (ruff, ruff-format, flake8, mypy) on the full diff — passed.

Deferred to CI: the full backend suite.

🤖 Generated with Claude Code

@vaind
vaind requested a review from a team as a code owner July 24, 2026 19:03
@linear-code

linear-code Bot commented Jul 24, 2026

Copy link
Copy Markdown

CW-1719

@vaind

vaind commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Cross-repo stack for CW-1719:

Deploy this first; then un-draft/merge the Seer PR; then, once the writer path is confirmed live, merge #120584.

…ution

Seer records PR attribution and a run<->PR link only via the autofix
completion hook, which fires from the SEER_PR_CREATED webhook and requires
both a run_id and a group_id. Any PR from a run that isn't tied to an issue
(group_id is None) never reaches the attribution/linking code, so those PRs
silently lose attribution and their run link.

Add a dedicated, run-anchored, issue-optional inbound RPC that Seer's PR
writer can call for every PR it opens. It resolves the org, records the
SENTRY_APP/SEER_DATA attribution (still behind organizations:pr-metrics-attribution),
and always links the PR to its SeerRun (still behind the linking killswitch),
inheriting both gates from the existing helpers. It deliberately does not
broadcast to sentry-apps, create an Activity, run analytics, or check
SeerAutofixOperator.has_access -- those concerns stay on the completion hook.

The shared record_seer_created_pull_requests wrapper encapsulates the
flag-guarded attribute+link so the legacy write in process_autofix_updates
can be pointed at it and removed in a follow-up once this path is live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vaind
vaind force-pushed the ivan/cw-1719-notify-seer-pr-created branch from 958f2d7 to e2da0b8 Compare July 25, 2026 11:05
@vaind
vaind requested review from a team and giovanni-guidini July 25, 2026 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant