Min Participants + PagerDuty Collector
Enforce Min Participants using data collected by PagerDuty Collector. Automatically check operational readiness standards on every PR.
How PagerDuty Collector Powers This Guardrail
The PagerDuty Collector gathers metadata from your incident management systems. This data flows into Lunar's Component JSON, where the Min Participants guardrail evaluates it against your standards.
When enabled, this check runs automatically on every PR and in AI coding workflows, providing real-time enforcement with actionable feedback.
Quick Start Configuration
Add both the collector and policy to your lunar-config.yml to enable this guardrail.
# Step 1: Enable the PagerDuty Collector
collectors:
- uses: github://earthly/lunar-lib/collectors/pagerduty@v1.0.5
# with: ...
# Step 2: Enable the On-Call Guardrails
policies:
- uses: github://earthly/lunar-lib/policies/oncall@v1.0.5
include: [min-participants]
# with: ...
What PagerDuty Collector Collects
This collector gathers the following data that the Min Participants guardrail evaluates.
oncall
Queries the PagerDuty REST API for the component's service on a code hook
— it runs when commits are pushed (PRs and the default branch). Resolves
the service ID in order: the component's pagerduty/service-id meta
annotation (set by a cataloger via
lunar catalog component --meta pagerduty/service-id <id>), then the
explicit service_id input, then — when backstage_discovery is enabled
— the component's own catalog-info.yaml (read from the fresh checkout
the code hook provides, so no clone-code is needed), reading the service
ID directly from a pagerduty.com/service-id / pagerduty/service-id
annotation. Fetches service details, on-call schedule participants, and
escalation policy. Writes normalized data to .oncall.service,
.oncall.schedule, .oncall.escalation, and .oncall.summary. Stores the raw
API response under .oncall.native.pagerduty.
oncall-cron
Same PagerDuty query as oncall, but on a daily cron instead of on push —
it re-collects on a schedule so .oncall stays fresh as on-call schedules
and escalation policies rotate between commits (they change on their own
clock, independent of code). Resolves the service ID identically and writes
the same normalized .oncall.service, .oncall.schedule, .oncall.escalation,
and .oncall.summary (raw responses under .oncall.native.pagerduty).
clone-code gives it the checkout so backstage_discovery can still read
catalog-info.yaml. Include oncall for on-push evaluation, oncall-cron
for always-fresh scheduled refresh, or both.
Example Data Flow
Here's an example of the data that PagerDuty Collector writes to the Component JSON, which Min Participants then evaluates.
{
"oncall": {
"source": {
"tool": "pagerduty",
"integration": "api"
},
"service": {
"id": "PXXXXXX",
"name": "Payment API",
"status": "active"
},
"schedule": {
"exists": true,
"participants": 4,
"rotation": "weekly"
},
"escalation": {
"exists": true,
"levels": 3,
"policy_name": "Payment API Escalation"
},
"summary": {
"has_oncall": true,
"has_escalation": true,
"min_participants": 4
},
"native": {
"pagerduty": { "...full PagerDuty API response..." }
}
}
}
Configuration Options
PagerDuty Collector Inputs
| Input | Required | Default | Description |
|---|---|---|---|
service_id |
Required | — | PagerDuty service ID to query (e.g. PXXXXXX). Optional if the component has a `pagerduty/service-id` meta annotation set by a cataloger. |
pagerduty_base_url |
Optional |
https://api.pagerduty.com
|
PagerDuty API base URL |
backstage_discovery |
Optional |
false
|
When "true", and no service ID is found via component meta or the service_id input, discover it from the component's own catalog-info.yaml: the collector reads the file from the runner's checkout (oncall runs on a code hook, which clones; oncall-cron sets clone-code) and takes the PagerDuty service ID from the annotation keys listed in backstage_annotations. Opt-in, and needs no extra secret — the runner's checkout is used, not a token. Lets the oncall guardrails work off the standard PagerDuty Backstage annotation without a cataloger or component meta. |
backstage_annotations |
Optional |
pagerduty.com/service-id,pagerduty/service-id
|
Comma-separated annotation keys to read the service ID from, tried in order (first non-empty value across the file's Component entities wins). Defaults to both conventional spellings — PagerDuty's Backstage integration guide uses `pagerduty.com/service-id`; Lunar's meta uses `pagerduty/service-id`. Only used when backstage_discovery is "true". |
backstage_catalog_paths |
Optional |
catalog-info.yaml,catalog-info.yml
|
Comma-separated catalog-info file paths to try in the checked-out repo (first match wins). Only used when backstage_discovery is "true". |
On-Call Guardrails Inputs
| Input | Required | Default | Description |
|---|---|---|---|
min_participants |
Optional |
2
|
Minimum number of on-call rotation participants required |
Ready to Automate Your Standards?
See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 200+ built-in guardrails.