Skip to main content

PR gate

The GitHub Action that replays real incidents against a rules.yml change before it merges.

What it does

On a pull request that touches rules.yml, the action replays the last window-days (7 by default) of incidents from .brs/incidents against the PR's proposed rules and alertmanager config — the same deterministic replay engine the brs CLI uses — and posts one sticky comment with the verdict. It never pushes a commit and never changes your alerting stack; it only reports.

Workflow

.github/workflows/pr-gate.yml
on:
  pull_request:
    paths: ['rules.yml']
permissions:
  contents: read
  pull-requests: write
jobs:
  pr-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: BurnRateSurgeon/burnratesurgeon/packages/pr-gate@v1
        with:
          rules-path: rules.yml
          alertmanager-path: alertmanager.yml
          window-days: '7'
          fail-on-miss: 'true'

Inputs

InputDefaultMeaning
rules-pathrules.ymlPrometheus recording and alerting rules to replay.
alertmanager-pathalertmanager.ymlRouting and inhibit rules for the replay.
history-path.brs/incidentsWhere the incident history to replay against lives in the repo.
window-days7How many days of incident history to replay.
fail-on-misstrueWhether a missed incident fails the check, or only the comment reports it.
commenttrueWhether to post/update the sticky PR comment at all.

replay-api-url, replay-token, replay-timeout-seconds and replay-poll-seconds switch the replay from running inside the runner to queuing it against the BurnRateSurgeon API (Cloudflare Queues) and polling for the verdict — useful when a snapshot is too large for the runner's own resources.

The comment

One comment per PR, found and edited in place on every push rather than reposted — a hidden <!-- burnratesurgeon:pr-gate --> marker is how it is found again. It opens with the headline, then one row per rule:

PR comment (excerpt)
## BurnRateSurgeon PR gate

rules.yml replayed against **42 incidents** from the last **7 days** (Aug 30 → Sep 6).
**2 of 42 incidents would have been missed.**

| Rule | Verdict |
| --- | --- |
| CheckoutErrorBudgetBurn | would have failed in 2 of the last 6 incidents |
| APILatencyBurn | caught all 12 incidents it was expected to catch |

### `CheckoutErrorBudgetBurn` — 2 misses

| Incident | Occurred at | Why it did not page |
| --- | --- | --- |
| inc_9f2a1c | 2026-09-02T03:14:00Z | never fired during the incident |

engine 1.4.0 · input checksum a1b2c3d4 · head 7e9f0ab
The gate only reports: it never pushes commits and never changes your alerting stack.

A window with zero incidents still gets a comment saying nothing could be graded, rather than silence a reader could mistake for the check not having run.

Outputs

skipped, incidents, missed-incidents, failing-rules, checksum, run-id, cache-hit, comment-id — available to a later step in the same job, for example to gate a deploy on failing-rules == '0'.