Runbook Author
Purpose
Turn an alert definition (a Prometheus rule, a Datadog monitor, a
PagerDuty service config) OR a postmortem into a runbook entry the
on-call engineer can follow at 3am. Used by the sre agent. The
output is a single-page runbook stub the on-call edits as needed —
not a final doc, but a structured starting point that prevents the
"blank page at 3am" problem.
Scope
- Reads ONE of:
- an alert definition file (Prometheus
*.rules.yaml, Datadog
monitor JSON export, generic YAML with name + query)
- a postmortem markdown (
postmortems/YYYY-MM-DD-*.md)
- Produces a runbook entry at
--out (default
runbooks/<alert-name>.md).
- Does NOT auto-publish to a wiki; the operator commits or pastes
the file themselves.
- Does NOT execute any of the actions it suggests — the runbook is
text, not a script.
When to use
- A new alert was added — runbook entry should land in the same PR.
- A postmortem identified a missing runbook (action item: "write
runbook for the disk-full alert").
- During on-call rotation handoff, to fill in stub runbooks for
alerts that exist but lack docs.
When NOT to use
- For an alert whose root cause is genuinely unknown. "Investigate"
is not a first action. If the team doesn't know the symptom-to-
cause mapping yet, run a game-day or shadow an incident first.
- For automation / auto-remediation. Runbooks are human-targeted;
if every step is a script, the work belongs in code, not docs.
- As a substitute for SLO definition. A runbook tells the on-call
what to do; the SLO tells them whether to wake up.
Automated pass
Detect input mode:
if [ -n "${ALERT:-}" ]; then mode=alert
elif [ -n "${POSTMORTEM:-}" ]; then mode=postmortem
else echo "pass --alert or --postmortem" >&2; exit 2
fi
Extract the salient fields per mode:
alert mode — pull name, expr/query, for, severity,
summary annotation, runbook_url annotation if already set.
yq '.groups[].rules[] | select(.alert)' "$ALERT"
postmortem mode — pull title, root cause section, detection
section, mitigation steps, action items.
Render the runbook template. The skill enforces the section
order — the on-call's eye expects the same order on every entry:
# Runbook: <alert name>
**Severity:** sev-1 | sev-2 | sev-3
**Owner team:** <team>
**Pages:** yes | no
**SLO impact:** <which SLO budget this burns>
## Symptom
What the alert means in one sentence. What the user sees, not
the metric.
## First 5 actions
1. <single concrete check or action>
2. <next>
3. <next>
4. <next>
5. <next>
## If the first 5 don't help
Escalate to <secondary on-call / team lead>. Page <service-owner>
if the symptom is still active after <N> minutes.
## Known false positives
- <condition under which this fires but isn't real>
## Related
- Dashboard: <link>
- SLO: <link>
- Recent incidents: <links to postmortems>
For alert-mode, populate the Symptom section from the alert's
summary annotation; populate First 5 actions with stubs:
- confirm the alert by checking the dashboard at
- check recent deploys (
kubectl rollout history / equivalent)
- check the dependency graph for upstream alerts firing simultaneously
- check error logs for the affected service in the last 15m
- if customer-facing, post to #incidents and start an incident channel
These are STUBS — the operator overwrites them with the real
symptom-specific actions. The skill marks them with a
<!-- TODO: replace --> comment to make that obvious.
For postmortem-mode, populate First 5 actions from the
postmortem's mitigation section. If the postmortem has 8
mitigation steps, take the first 5 and list the rest under
"If the first 5 don't help."
Write the file at --out (or
runbooks/<slug-of-alert-name>.md). Refuse to overwrite if the
path exists; require --force.
Manual pass
For a one-off entry, the operator copies the template above into a
new file and fills it in. Five sections, ordered. The point of the
skill is consistency across runbooks, not novel content per entry.
Known gotchas
- Stub actions get committed verbatim. The biggest failure mode
is the operator commits the skill output without replacing the
TODOs. The skill emits the file with a banner at the top:
> [!WARNING] This runbook contains TODO stubs. Replace before on-call relies on it. The CI lint step in the runbooks
repository should reject files with <!-- TODO: replace -->
still present.
- Runbook URL not wired into the alert. The runbook lives in
the repo, but the alert needs
annotations.runbook_url pointing
at the rendered page (e.g., a generated GitHub Pages URL). The
skill does NOT update the alert def — that's a separate edit.
Easy to forget; the runbook is useless if the on-call can't find
it from the page.
- First 5 actions get long. The "5" is a forcing function.
If the on-call needs 12 steps, the alert is too coarse —
split it into multiple alerts each with a tighter symptom.
Resist the urge to bullet-list 14 items.
- Postmortem-derived runbooks go stale. A runbook generated
from a 2024 postmortem describes the 2024 architecture. Re-
validate runbooks during on-call shadowing; mark stale ones with
a
Last reviewed: trailer.
- Pages: yes for everything. Reviewing the corpus of runbooks,
if every alert pages, the team is at burnout risk. The skill
does not enforce this — it's a culture call — but the runbook's
Pages: field surfaces the count.
References
- Google SRE Book chapters 11 (on-call) and 12 (effective
troubleshooting).
lib/skills/postmortem-write/SKILL.md — companion: produces
the postmortem this skill can read.
runbooks/ — project-level runbook directory.
- The alerting repo's CI lint that catches unreplaced TODO stubs.
1---2name: runbook-author3description: Given an alert definition (or a postmortem), produce a runbook entry — symptom, first 5 actions, escalation. Use when an alert lacks a response procedure or a postmortem action item calls for one.4---56# Runbook Author78## Purpose910Turn an alert definition (a Prometheus rule, a Datadog monitor, a11PagerDuty service config) OR a postmortem into a runbook entry the12on-call engineer can follow at 3am. Used by the `sre` agent. The13output is a single-page runbook stub the on-call edits as needed —14not a final doc, but a structured starting point that prevents the15"blank page at 3am" problem.1617## Scope1819- Reads ONE of:20 - an alert definition file (Prometheus `*.rules.yaml`, Datadog21 monitor JSON export, generic YAML with `name` + `query`)22 - a postmortem markdown (`postmortems/YYYY-MM-DD-*.md`)23- Produces a runbook entry at `--out` (default24 `runbooks/<alert-name>.md`).25- Does NOT auto-publish to a wiki; the operator commits or pastes26 the file themselves.27- Does NOT execute any of the actions it suggests — the runbook is28 text, not a script.2930## When to use3132- A new alert was added — runbook entry should land in the same PR.33- A postmortem identified a missing runbook (action item: "write34 runbook for the disk-full alert").35- During on-call rotation handoff, to fill in stub runbooks for36 alerts that exist but lack docs.3738## When NOT to use3940- For an alert whose root cause is genuinely unknown. "Investigate"41 is not a first action. If the team doesn't know the symptom-to-42 cause mapping yet, run a game-day or shadow an incident first.43- For automation / auto-remediation. Runbooks are human-targeted;44 if every step is a script, the work belongs in code, not docs.45- As a substitute for SLO definition. A runbook tells the on-call46 what to do; the SLO tells them whether to wake up.4748## Automated pass49501. Detect input mode:51 ```sh52 if [ -n "${ALERT:-}" ]; then mode=alert53 elif [ -n "${POSTMORTEM:-}" ]; then mode=postmortem54 else echo "pass --alert or --postmortem" >&2; exit 255 fi56 ```57582. Extract the salient fields per mode:5960 **alert mode** — pull `name`, `expr`/`query`, `for`, severity,61 summary annotation, runbook_url annotation if already set.62 ```sh63 yq '.groups[].rules[] | select(.alert)' "$ALERT"64 ```6566 **postmortem mode** — pull title, root cause section, detection67 section, mitigation steps, action items.68693. Render the runbook template. The skill enforces the section70 order — the on-call's eye expects the same order on every entry:7172 ```markdown73 # Runbook: <alert name>7475 **Severity:** sev-1 | sev-2 | sev-376 **Owner team:** <team>77 **Pages:** yes | no78 **SLO impact:** <which SLO budget this burns>7980 ## Symptom81 What the alert means in one sentence. What the user sees, not82 the metric.8384 ## First 5 actions85 1. <single concrete check or action>86 2. <next>87 3. <next>88 4. <next>89 5. <next>9091 ## If the first 5 don't help92 Escalate to <secondary on-call / team lead>. Page <service-owner>93 if the symptom is still active after <N> minutes.9495 ## Known false positives96 - <condition under which this fires but isn't real>9798 ## Related99 - Dashboard: <link>100 - SLO: <link>101 - Recent incidents: <links to postmortems>102 ```1031044. For alert-mode, populate the **Symptom** section from the alert's105 `summary` annotation; populate **First 5 actions** with stubs:106 1. confirm the alert by checking the dashboard at <link>107 2. check recent deploys (`kubectl rollout history` / equivalent)108 3. check the dependency graph for upstream alerts firing simultaneously109 4. check error logs for the affected service in the last 15m110 5. if customer-facing, post to #incidents and start an incident channel111112 These are STUBS — the operator overwrites them with the real113 symptom-specific actions. The skill marks them with a114 `<!-- TODO: replace -->` comment to make that obvious.1151165. For postmortem-mode, populate **First 5 actions** from the117 postmortem's mitigation section. If the postmortem has 8118 mitigation steps, take the first 5 and list the rest under119 "If the first 5 don't help."1201216. Write the file at `--out` (or122 `runbooks/<slug-of-alert-name>.md`). Refuse to overwrite if the123 path exists; require `--force`.124125## Manual pass126127For a one-off entry, the operator copies the template above into a128new file and fills it in. Five sections, ordered. The point of the129skill is consistency across runbooks, not novel content per entry.130131## Known gotchas132133- **Stub actions get committed verbatim.** The biggest failure mode134 is the operator commits the skill output without replacing the135 TODOs. The skill emits the file with a banner at the top:136 `> [!WARNING] This runbook contains TODO stubs. Replace before137 on-call relies on it.` The CI lint step in the runbooks138 repository should reject files with `<!-- TODO: replace -->`139 still present.140- **Runbook URL not wired into the alert.** The runbook lives in141 the repo, but the alert needs `annotations.runbook_url` pointing142 at the rendered page (e.g., a generated GitHub Pages URL). The143 skill does NOT update the alert def — that's a separate edit.144 Easy to forget; the runbook is useless if the on-call can't find145 it from the page.146- **First 5 actions get long.** The "5" is a forcing function.147 If the on-call needs 12 steps, the alert is too coarse —148 split it into multiple alerts each with a tighter symptom.149 Resist the urge to bullet-list 14 items.150- **Postmortem-derived runbooks go stale.** A runbook generated151 from a 2024 postmortem describes the 2024 architecture. Re-152 validate runbooks during on-call shadowing; mark stale ones with153 a `Last reviewed:` trailer.154- **Pages: yes for everything.** Reviewing the corpus of runbooks,155 if every alert pages, the team is at burnout risk. The skill156 does not enforce this — it's a culture call — but the runbook's157 `Pages:` field surfaces the count.158159## References160161- Google SRE Book chapters 11 (on-call) and 12 (effective162 troubleshooting).163- `lib/skills/postmortem-write/SKILL.md` — companion: produces164 the postmortem this skill can read.165- `runbooks/` — project-level runbook directory.166- The alerting repo's CI lint that catches unreplaced TODO stubs.