Skip to content

Fix GitHub webhook delivery + restore PR freshness in installed runtimes#691

Merged
arul28 merged 8 commits into
mainfrom
ade/pr-state-sync-webhook-fallback
Jul 3, 2026
Merged

Fix GitHub webhook delivery + restore PR freshness in installed runtimes#691
arul28 merged 8 commits into
mainfrom
ade/pr-state-sync-webhook-fallback

Conversation

@arul28

@arul28 arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

PR #687 showed as merged in one panel but open everywhere else, and GitHub-backed PR state had silently degraded since webhooks were added. Three stacked root causes, all fixed here plus the already-deployed relay-side heal:

  1. Hosted webhook delivery was broken — the GitHub App's webhook secret drifted from the Cloudflare worker's GITHUB_WEBHOOK_SECRET on 2026-06-30; every delivery since 401'd. The worker gains POST /github/repos/:owner/:name/webhook/heal (repo-admin gated; re-syncs the App's webhook secret to the worker's own via PATCH /app/hook/config — converge-only, idempotent) and GET .../webhook/deliveries (push+ gated, repo-filtered fail-closed delivery-log proxy). Heal was executed against production and a live pull_request delivery verified end-to-end (GitHub → worker 202 → D1 → repo events route).
  2. The daemon never consumed webhooks or polled PRs — production windows are runtime-bound, so the daemon is the service host, but it created the automation ingress without ever starting it, had no prPollingService at all, and packaged builds nulled the whole ingress stack via the automations feature gate. The ingress now runs in a PR-freshness-only mode when automations are unavailable (relay poll + prService.ingestGithubWebhook; rule dispatch/local webhook server/status stay gated), with kv-backed cursor persistence, and the daemon starts both loops.
  3. Stale-precedence UI bugs — a stale non-terminal GitHub snapshot row could override a terminal (merged/closed) local PR state. Fixed in the Lanes PR tag (shouldPreferGithubPrTag), the GitHub tab (reconcileLinkedPrState over displayedItems for list/filter/counts/selection), and the same inherited bug in the iOS lane chips (LaneHelpers.shouldPreferGithubTag, full simulator build verified).

Cheap-by-default guarantees for machines and rate limits: zero-tracked-PR discovery (a forced full-snapshot fetch) is throttled to a 10-minute cadence; missing GitHub App authorization puts the relay poll into a quiet 5-minute auth-pending cooldown (one info log, status disabled, pollNow() bypasses); poll ticks make zero GitHub calls when rows are fresh. The pre-webhook read-driven freshness path (renderer coalesced reads + 120s snapshot TTL) is untouched and remains the default for repos without the GitHub App.

Validation

  • webhook-relay: 29/29 tests (5 new: heal auth/success/not-installed, deliveries filtering fail-closed)
  • desktop: 109 affected tests green (ingress PR-freshness-only mode + auth-pending cooldown, discovery throttle contract, Lanes/GitHubTab precedence)
  • ade-cli: full suite 1365/1365 + typecheck
  • iOS: full simulator xcodebuild BUILD SUCCEEDED; regression test added
  • Live production verification: relay heal executed, fresh delivery accepted 202 and queryable on the events route the app polls
  • /quality dual-review: 0 Blocker / 0 High; all verified findings applied

🤖 Generated with Claude Code

Greptile Summary

This PR fixes three root causes behind stale/invisible PR state: a drifted webhook secret causing delivery 401s since 2026-06-30 (fixed with a new relay heal endpoint + deliveries proxy), the daemon never running ingress or PR polling loops in production (fixed in both bootstrap.ts and main.ts), and stale GitHub snapshot rows overriding terminal local PR states in the UI.

  • Relay (relay.ts): Adds POST /github/repos/:owner/:name/webhook/heal (admin-gated, idempotent secret re-sync via PATCH /app/hook/config) and GET .../webhook/deliveries (push-gated, repo-filtered delivery log proxy that fails closed on unresolvable repository_id).
  • Daemon ingress + polling (bootstrap.ts, main.ts, automationIngressService.ts, prPollingService.ts): automationIngressService is now always constructed with a PR-freshness-only mode (relay poll + ingestGithubWebhook) when automationService is null; construction enforces ingressCursorStore presence in that mode. Auth-pending cooldown replaces per-tick errors. prPollingService is added to both the daemon and the desktop default background task set, with a 10-minute throttle on zero-tracked-PR discovery.
  • UI precedence (GitHubTab.tsx, lanePageModel.ts, LaneHelpers.swift, prState.ts): A new shared isTerminalPrState helper powers reconcileLinkedPrState (GitHub tab list/filter/counts/selection) and fixes shouldPreferGithubPrTag (Lanes + iOS chips) so terminal ADE states always win over stale non-terminal GitHub snapshots.

Confidence Score: 5/5

Safe to merge; all three root causes are addressed with matching test coverage, and no regressions were found in the changed paths.

The PR addresses targeted, well-understood bugs with clear before/after behavior. The relay endpoints are properly auth-gated and idempotent. The daemon ingress and polling startup paths are tested end-to-end. The stale-state precedence fix is correctly symmetric across TypeScript (Lanes + GitHub tab) and Swift (iOS). No incorrect data paths, missing required side effects, or broken contracts were found in the diff.

No files require special attention. The if (automationIngressService) guard in apps/desktop/src/main/main.ts around scheduleBackgroundProjectTask is always-true dead code now that the service is unconditionally constructed — noted in the previous review cycle and left for a follow-up cleanup.

Important Files Changed

Filename Overview
apps/webhook-relay/src/relay.ts Adds heal + deliveries admin endpoints; access-level refactor on assertGitHubRepoAuthorized properly gates admin vs write; repositoryId plumbed through for fail-closed delivery filtering
apps/desktop/src/main/services/automations/automationIngressService.ts PR-freshness-only mode (automationService=null) with enforced cursor store, auth-pending cooldown, and relay poll correctly isolated from automation dispatch
apps/desktop/src/main/main.ts automationIngressService now always constructed; ADE_ENABLE_PR_POLLING and ADE_ENABLE_AUTOMATION_INGRESS added to default background task set; if (automationIngressService) guard is now always-true dead code
apps/ade-cli/src/bootstrap.ts Daemon now starts prPollingService and automationIngressService unconditionally; correct for headless runtime which lacks background-task scheduling infrastructure
apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx displayedItems reconciles stale GitHub rows; withReconcileDelta correctly adjusts snapshot badge counts for items that changed state
apps/desktop/src/renderer/components/lanes/lanePageModel.ts shouldPreferGithubPrTag correctly guards against stale non-terminal GitHub state overriding terminal ADE state for the same PR
apps/ios/ADE/Views/Lanes/LaneHelpers.swift Swift port of the same terminal-state-precedence fix; guard-early pattern mirrors the TS change correctly
apps/desktop/src/main/services/prs/prPollingService.ts Discovery throttle initialized to epoch (0) so first tick always discovers; 10-minute cadence prevents rate pressure for zero-PR projects
apps/desktop/src/renderer/lib/prState.ts New shared utility; correctly identifies merged/closed as terminal (irreversible without explicit reopen)

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant GH as GitHub
    participant Relay as Cloudflare Worker
    participant Daemon as ADE Daemon (bootstrap.ts)
    participant Ingress as automationIngressService
    participant PR as prService
    participant UI as Desktop Renderer

    GH->>Relay: POST /github/webhook (pull_request event)
    Relay->>Relay: HMAC verify + D1 store

    loop Every ~60s (automationIngressService relay poll)
        Daemon->>Ingress: pollGithubRelayOnce()
        Ingress->>Relay: "GET /github/repos/:owner/:repo/events?after=cursor"
        Relay-->>Ingress: "{ events, nextCursor }"
        Ingress->>PR: ingestGithubWebhook(event)
        Ingress->>Ingress: setIngressCursor(kv store)
    end

    loop Every ~60s (prPollingService)
        Daemon->>PR: listAll() / discoverLanePullRequests()
        PR-->>Daemon: PR list with updated states
        Daemon->>UI: emit prs-updated event
    end

    UI->>UI: reconcileLinkedPrState(githubItem, adesLocalPr)
    Note over UI: terminal ADE state wins over stale GitHub snapshot
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant GH as GitHub
    participant Relay as Cloudflare Worker
    participant Daemon as ADE Daemon (bootstrap.ts)
    participant Ingress as automationIngressService
    participant PR as prService
    participant UI as Desktop Renderer

    GH->>Relay: POST /github/webhook (pull_request event)
    Relay->>Relay: HMAC verify + D1 store

    loop Every ~60s (automationIngressService relay poll)
        Daemon->>Ingress: pollGithubRelayOnce()
        Ingress->>Relay: "GET /github/repos/:owner/:repo/events?after=cursor"
        Relay-->>Ingress: "{ events, nextCursor }"
        Ingress->>PR: ingestGithubWebhook(event)
        Ingress->>Ingress: setIngressCursor(kv store)
    end

    loop Every ~60s (prPollingService)
        Daemon->>PR: listAll() / discoverLanePullRequests()
        PR-->>Daemon: PR list with updated states
        Daemon->>UI: emit prs-updated event
    end

    UI->>UI: reconcileLinkedPrState(githubItem, adesLocalPr)
    Note over UI: terminal ADE state wins over stale GitHub snapshot
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/main/main.ts, line 3772-3784 (link)

    P2 automationIngressService is now always constructed (never null), so the if (automationIngressService) guard is dead code — the branch is always taken. The surrounding scheduleBackgroundProjectTask call is still correct because ADE_ENABLE_AUTOMATION_INGRESS gates execution, but the conditional wrapper adds noise and could mislead future readers into thinking the service can still be absent here.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/main.ts
    Line: 3772-3784
    
    Comment:
    `automationIngressService` is now always constructed (never `null`), so the `if (automationIngressService)` guard is dead code — the branch is always taken. The surrounding `scheduleBackgroundProjectTask` call is still correct because `ADE_ENABLE_AUTOMATION_INGRESS` gates execution, but the conditional wrapper adds noise and could mislead future readers into thinking the service can still be absent here.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Claude Code

Reviews (3): Last reviewed commit: "Address review: reconciled rows move in ..." | Re-trigger Greptile

arul28 and others added 6 commits July 2, 2026 17:21
…snapshot precedence

- Start GitHub PR polling at project startup (prs.polling_start) so the
  fallback feeds Work/Lanes/PRs via prs-updated without requiring a PR
  tab read first; allow ADE_ENABLE_PR_POLLING and
  ADE_ENABLE_AUTOMATION_INGRESS in startup stability mode.
- prsGetForLane now ensures polling is running (Work tab lane reads).
- Lane PR tag precedence: a stale non-terminal GitHub snapshot state can
  no longer override a terminal ADE linked PR state.
- GitHubTab reconciles linked local terminal state (merged/closed) over
  stale open GitHub list items for filtering, counts, selection, render.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…times

Hosted relay (apps/webhook-relay):
- Add POST /github/repos/:owner/:name/webhook/heal (repo-admin gated):
  re-syncs the GitHub App's webhook secret to the worker's current
  GITHUB_WEBHOOK_SECRET via PATCH /app/hook/config. Recovers from the
  secret drift that made GitHub sign every delivery with a stale secret
  (all deliveries 401-rejected since 2026-06-30).
- Add GET /github/repos/:owner/:name/webhook/deliveries (push+ gated):
  proxies the GitHub App delivery log filtered to the repo so delivery
  failures are observable without the GitHub App settings UI.
- assertGitHubRepoAuthorized gains an access level (write|admin) and
  returns the repository id for delivery filtering.

App-side consumption (desktop + daemon):
- automationIngressService now accepts a null automationService: the
  GitHub relay poll and prService.ingestGithubWebhook (PR freshness)
  run even where the automations feature is unavailable (packaged
  builds, installed daemons); rule dispatch, the local webhook server,
  and ingress status stay gated. Cursor persistence falls back to an
  injected kv-backed store.
- ade-cli daemon: actually start the ingress (it was constructed but
  never started) and add the missing prPollingService so runtime-bound
  windows get background GitHub polling + prs-updated events. Polling
  had been dead in installed builds since PR reads moved off the
  in-process IPC path (~May 21).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- prPollingService: throttle zero-tracked-PR discovery (a forced full
  repo snapshot fetch) to a 10-minute cadence instead of every 60s
  tick; read-driven surfaces still discover instantly on their own.
- automationIngressService: treat a missing GitHub App user token as a
  quiet auth-pending state — skip hosted relay polls for 5 minutes, log
  the transition once at info, report status "disabled" instead of
  warning every tick. pollNow() bypasses the cooldown so post-authorize
  checks are instant.

Keeps the pre-webhook read-driven freshness path (renderer coalesced
reads + 120s snapshot TTL) untouched as the default for repos without
the GitHub App.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ldown status, dedupe shared helpers

- relay deliveries endpoint drops repo-scoped rows when the repo id
  cannot be proven to match the authorized repo (fail closed).
- Relay poll skips before the "polling" status write during the
  auth-pending cooldown so the "disabled" status stays accurate.
- createKvIngressCursorStore owns the kv cursor key template once
  (desktop main + daemon bootstrap both consume it).
- isTerminalPrState hoisted to renderer/lib/prState.ts (was duplicated
  in lanePageModel.ts and GitHubTab.tsx).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g row, docs parity

- prAsync.test.ts: pin the empty-cache discovery throttle (first tick
  discovers, 60s ticks inside the 10-min window do not, the crossing
  tick does; no tracked-PR refresh and a single prs-updated on the
  empty path).
- relay.test.ts: delivery-log filtering drops a repo-scoped row whose
  repository_id cannot be parsed (fail closed).
- docs: pull-requests README documents daemon-owned background polling,
  the discovery throttle, and the shared terminal-state precedence rule
  (GitHubTab reconciliation + lane PR tag); automations README documents
  the ingress PR-freshness-only mode, kv cursor store, auth-pending
  cooldown, and the relay webhook heal/deliveries routes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lane chips

shouldPreferGithubTag was a copy of the desktop logic with the same bug:
a stale non-terminal GitHub snapshot row could override a merged/closed
ADE PR for the SAME PR, flipping a lane chip back to "open" after merge.
Mirror the desktop rule (terminal ADE state is never superseded by a
non-terminal GitHub state for a matched PR; unmatched behavior
unchanged) and add the regression case to ADETests.

Verified with a full simulator xcodebuild (BUILD SUCCEEDED).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview Jul 2, 2026 11:56pm

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@arul28, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1dcc8da6-75b0-45ea-9e20-1590484de56a

📥 Commits

Reviewing files that changed from the base of the PR and between e15e974 and bf7158f.

⛔ Files ignored due to path filters (2)
  • docs/features/automations/README.md is excluded by !docs/**
  • docs/features/pull-requests/README.md is excluded by !docs/**
📒 Files selected for processing (16)
  • apps/ade-cli/src/bootstrap.ts
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/automations/automationIngressService.test.ts
  • apps/desktop/src/main/services/automations/automationIngressService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/prs/prAsync.test.ts
  • apps/desktop/src/main/services/prs/prPollingService.ts
  • apps/desktop/src/renderer/components/lanes/LanesPage.test.ts
  • apps/desktop/src/renderer/components/lanes/lanePageModel.ts
  • apps/desktop/src/renderer/components/prs/tabs/GitHubTab.test.tsx
  • apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx
  • apps/desktop/src/renderer/lib/prState.ts
  • apps/ios/ADE/Views/Lanes/LaneHelpers.swift
  • apps/ios/ADETests/ADETests.swift
  • apps/webhook-relay/src/relay.ts
  • apps/webhook-relay/test/relay.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade/pr-state-sync-webhook-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arul28

arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@copilot review but do not make fixes

@mintlify

mintlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
ade-ac1c6011 🟢 Ready View Preview Jul 2, 2026, 11:11 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment thread apps/webhook-relay/src/relay.ts Outdated
- Deliveries endpoint always fetches the max GitHub page (per_page=100)
  and applies the caller's limit to the repo-filtered output, so busy
  sibling repos cannot starve the requested repo out of a small page.
- automationIngressService throws at construction when automationService
  is unavailable and no ingressCursorStore is provided — a silent
  in-memory cursor would reset on every restart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d58c2bcb10

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// only leaves via an explicit reopen), so surfaces treat a terminal local
// state as authoritative over a stale non-terminal GitHub snapshot row.
export function isTerminalPrState(state: PrSummary["state"]): boolean {
return state === "merged" || state === "closed";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t treat closed PRs as irreversible

When a PR is reopened on GitHub, the repo snapshot can legitimately show the linked item as open while ADE's cached PrSummary still says closed. Including closed here makes reconcileLinkedPrState and the lane tag logic rewrite that fresh snapshot back to closed, so the PR can disappear from the Open filter and keep a closed lane badge until a separate background refresh fixes the local row. Only merged is irreversible, or the closed-vs-open case needs a freshness check before overriding the GitHub snapshot.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considered and deliberately kept closed in the terminal set. Tradeoff: dropping it would make a lane badge / list row flip back to "open" for up to ~2 minutes after every user-initiated close (a very common ADE action, and the exact stale-flicker class this PR fixes), to protect the rare external-reopen case. That reopen case is transient and now self-heals fast: pull_request.reopened is a subscribed webhook event (marks the PR hot → refresh within seconds), and the daemon polling loop bounds it to ≤2 min even without webhooks. A timestamp-freshness comparison was considered and rejected — it introduces cross-source clock/format comparison fragility for a rare, bounded case.

Comment thread apps/desktop/src/renderer/components/prs/tabs/GitHubTab.tsx
Snapshot-derived filter counts now apply the reconciliation delta, so a
stale open row that displays under Merged is also counted under Merged
(Greptile P1). Regression assertions added to the stale-open test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arul28

arul28 commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf7158fbd0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// only leaves via an explicit reopen), so surfaces treat a terminal local
// state as authoritative over a stale non-terminal GitHub snapshot row.
export function isTerminalPrState(state: PrSummary["state"]): boolean {
return state === "merged" || state === "closed";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not treat reopened PRs as terminal

When ADE has a PR locally as closed and that PR is reopened on GitHub, the fresh GitHub row is open, but this helper classifies closed as terminal, so reconcileLinkedPrState and shouldPreferGithubPrTag keep showing the stale closed row instead of the reopened PR. That hides the PR from the open tab/lane chip until a separate local refresh catches up; only merged is irreversible, while closed can transition back via GitHub's reopen action. The mirrored Swift lane helper needs the same distinction.

Useful? React with 👍 / 👎.

// after the repo filter — otherwise busy sibling repos could starve the
// requested repo out of a small page.
const limit = Math.min(100, parseLimit(new URL(request.url)));
const response = await fetch(`${githubApiBaseUrl(env)}/app/hook/deliveries?per_page=100`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Follow delivery pagination before filtering by repo

When the GitHub App has more than 100 newer deliveries for other repositories, the requested repo's first matching delivery can be on a later cursor page. This endpoint fetches only the first app-wide page and then filters locally, so it can report no failures even though matching deliveries exist; it should keep following the delivery API cursor/Link pages until the per-repo limit is filled or pagination is exhausted.

Useful? React with 👍 / 👎.

@arul28 arul28 merged commit 037a438 into main Jul 3, 2026
29 checks passed
@arul28 arul28 deleted the ade/pr-state-sync-webhook-fallback branch July 3, 2026 00:22
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