The Netlify webhook dispatches one workflow run per accepted event. Bursty repositories generate far more runs than the dashboard needs, and job-level concurrency then cancels most of them.
Measured on 2026-07-28 against opentelemetry-collector-contrib:
- GitHub Actions opens a separate check suite per workflow, so one push to a PR produces ~17
check_suite.completed events, hence 17 dispatches.
- Worst burst observed: 53 dispatches for a single PR in 77 seconds.
- Across a sample of 1,501 dashboard-eligible events, 82% were
check_suite.completed.
- 81% of completed dashboard runs in the sampled window were cancelled by concurrency.
Every dispatch in a burst produces the same dashboard output, because each run re-reads the full statusCheckRollup. Only the last one carries new information.
Proposal: collapse events per repository + PR number before dispatching.
- On each accepted webhook, record
lastEventAt in a Netlify Blobs entry keyed by ${repository}#${pr_number} instead of dispatching.
- A scheduled function runs every minute and dispatches once a PR has been quiet for ~45s, then records
lastDispatchAt.
- Flush on
lastDispatchAt < lastEventAt rather than a boolean, so a lost update from concurrent writes costs one extra tick instead of a dropped refresh.
Simulated against the real event stream, a 60s trailing debounce on all events removes 66% of dispatches. Leading-edge and check-suite-only variants both scored worse (54% and 52%), so the simplest policy is also the best one.
Trade-off: refresh latency goes from near zero to roughly 45-105 seconds. The hourly cron remains the backstop, and duplicate dispatches stay harmless because the existing concurrency group absorbs them.
The Netlify webhook dispatches one workflow run per accepted event. Bursty repositories generate far more runs than the dashboard needs, and job-level concurrency then cancels most of them.
Measured on 2026-07-28 against opentelemetry-collector-contrib:
check_suite.completedevents, hence 17 dispatches.check_suite.completed.Every dispatch in a burst produces the same dashboard output, because each run re-reads the full
statusCheckRollup. Only the last one carries new information.Proposal: collapse events per repository + PR number before dispatching.
lastEventAtin a Netlify Blobs entry keyed by${repository}#${pr_number}instead of dispatching.lastDispatchAt.lastDispatchAt < lastEventAtrather than a boolean, so a lost update from concurrent writes costs one extra tick instead of a dropped refresh.Simulated against the real event stream, a 60s trailing debounce on all events removes 66% of dispatches. Leading-edge and check-suite-only variants both scored worse (54% and 52%), so the simplest policy is also the best one.
Trade-off: refresh latency goes from near zero to roughly 45-105 seconds. The hourly cron remains the backstop, and duplicate dispatches stay harmless because the existing concurrency group absorbs them.