Skip to content

feat(webhook): recover lost auto-plans from check_suite.requested - #1002

Draft
Kiran01bm wants to merge 2 commits into
kiran01bm/wh-8a-inbox-retry-after-not-beforefrom
kiran01bm/wh-8c-check-suite-recovery
Draft

feat(webhook): recover lost auto-plans from check_suite.requested#1002
Kiran01bm wants to merge 2 commits into
kiran01bm/wh-8a-inbox-retry-after-not-beforefrom
kiran01bm/wh-8c-check-suite-recovery

Conversation

@Kiran01bm

Copy link
Copy Markdown
Collaborator

Summary

Feed GitHub's check_suite.requested delivery into the durable inbox as a grace-delayed, redundant convergence signal that recovers lost auto-plan deliveries for open PR heads. Stacked on #999 (needs Create persisting retry_after); only the last commit is new here.

Why

Every push to a PR branch produces two independent deliveries: pull_request (what auto-plan acts on) and check_suite.requested. When the pull_request delivery is lost upstream of the inbox, the check suite sits queued with zero check runs and the PR is blocked with no recourse until the reconciler's next scan. The check_suite delivery is GitHub's purpose-built "populate your check runs for this SHA now" signal — handling it converges a lost auto-plan in minutes instead of waiting for the reconcile interval.

What

  • Ingress routes check_suite.requested only (durable-gated, allowlisted, default-branch pushes filtered, fork heads pass through) and enqueues it with a not-before time — the recovery grace — so the organic pull_request delivery wins the race.
  • Processing re-resolves the suite head against GitHub's current PR state (payload pull_requests[] with mandatory current-head equality; bounded open-PR scan fallback for empty/fork payloads) and synthesizes a recovery delivery only for open PRs still at that head whose auto-plan coverage is missing.
  • Recovery rows reuse the reconciler's deterministic synthesized-delivery GUID, so the two recovery producers dedupe against each other naturally.
  • Kill switch WEBHOOK_CHECK_SUITE_RECOVERY=false (default on, fail-safe to disabled on malformed values); honored at ingress and re-validated fail-closed at processing so already-queued rows stop synthesizing too.
  • New outcome counter schemabot.webhook.check_suite_recovery_total (covered / synthesized / resynthesized / already_queued / no_open_pr).
Before: pull_request delivery lost upstream of the inbox
┌────────┐  pull_request (lost)   ┌───────┐
│ GitHub │ ──────────X            │ inbox │  ...PR blocked until the
│        │  check_suite.requested └───────┘  reconciler's next scan
└────────┘ ──────────X (ignored)             (up to the scan interval)

After: check_suite.requested is a grace-delayed recovery signal
┌────────┐  pull_request (lost)     ┌───────┐
│ GitHub │ ──────────X              │ inbox │
│        │  check_suite.requested   │       │
└────────┘ ───────────────────────▶ │  row  │ not claimable until
                                    └───┬───┘ grace passes
                                        ▼
                       re-resolve head vs current open PRs
                                        │
                     coverage present? ─┼─ yes → no-op ("covered")
                                        └─ no  → synthesize recovery row
                                                 (same GUID as reconciler)
                                                 → auto-plan converges

Every push to a PR branch produces two independent deliveries:
pull_request (what auto-plan acts on) and check_suite.requested. When
the pull_request delivery is lost upstream of the inbox, the PR sits
blocked until the reconciler's next scan. Feed the check_suite signal
into the durable inbox with a grace-delayed not-before time so the
organic delivery wins the race, and synthesize a recovery delivery only
for open PRs still at the suite head whose auto-plan coverage is
missing. Recovery rows reuse the reconciler's deterministic GUID so the
two producers dedupe naturally. Kill switch:
WEBHOOK_CHECK_SUITE_RECOVERY=false.
GitHub names a same-repo head's open PRs in the check_suite payload, so
a non-fork suite with an empty list has nothing to recover: drop it at
ingress and never walk the open-PR listing for it — the walk now serves
only fork heads. Replaces the default-branch filter, which wrongly
dropped suites for PRs whose head is the default branch. Also documents
the Check suite App event subscription, without which the feature
receives no deliveries and is silently inert.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant