learn · improvement loop
Trigger to action runbooks
What fires, who acts, which test opens.
In Improvement loop
A firing without a runbook becomes a notification someone ignores. Each family of triggers here has the same shape: what fires it, the first move (usually automatic and reversible), how to diagnose, which test opens, who signs off, how to close it, and what gets logged so the threshold learns.
threshold fires → runbook · contain, diagnose, test, close → hypothesis registry → label
what it is, in industry terms
Incident runbooks, extended into experiments.
Reliability teams write runbooks so whoever is paged knows the first five minutes without thinking. Growth teams rarely do, so metric drops get argued about instead of handled. These runbooks borrow the incident shape (contain, diagnose, fix, review) and add one step: every diagnosis that isn't plain breakage ends in a hypothesis, so the fix is tested, not guessed.
System — Contains: pause, shadow, alert, mark stalled. Only actions that stop or narrow something and are easy to undo.
Agent — Diagnoses with the data, queues offline replays, drafts hypotheses and tests, re-fits thresholds. Never changes what a person sees.
Founder — Approves live changes, decides retire or keep, labels firings real or false alarm, picks the Monday constraint.
how a runbook runs
Contain first, then understand, then change one thing.
- Fire — threshold crosses
- Contain — pause · shadow alert (system)
- Diagnose — slice the data (agent)
- Fix or — hypothesis breakage → fix drop → test
- Approve — founder, for anything live
- Close — label firing log outcome
the runbook object
Runbooks are versioned like everything else.
| Field | Type | What it holds |
|---|---|---|
runbook_id | text | Stable id, e.g. rb.detector_quality |
version | int | Bumped on any step change |
fires_on | threshold ids | Which thresholds start it |
severity | enum | Inherits the highest severity of its thresholds |
steps | list | Ordered steps, each with who (system, agent, founder), what, and a time limit |
test_template | ref | The hypothesis or test it opens, prefilled |
close_when | rule | What must be true to close: metric back inside limits, test read out, or founder decision |
logs | list | Which fields every run must write |
the detector quality runbook
{
"runbook_id": "rb.detector_quality",
"version": 2,
"fires_on": [
"thr.det.precision",
"thr.det.spike"
],
"severity": "warn",
"steps": [
{
"who": "system",
"do": "move detector to shadow",
"within": "1 tick"
},
{
"who": "agent",
"do": "sample 20 recent matches; compare vocab hits vs labels",
"within": "24h"
},
{
"who": "agent",
"do": "queue offline replay of vocab v-next on stored matches",
"within": "24h"
},
{
"who": "founder",
"do": "approve go-live if precision recovers and recall holds",
"within": "72h"
}
],
"test_template": "hyp_template.detector_vocab",
"close_when": "precision ≥ floor on 50 new labels, or detector retired",
"logs": [
"alert_id",
"replay_id",
"decision",
"label"
]
}runbooks
Ten trigger families, ten runbooks.
Detector quality — warn · fires on: Precision under its floor, or a match spike past the control limit
- systemMove the detector to shadow. It keeps logging, stops filling the list.
- agentPull 20 recent matches; mark which vocabulary terms or rule fired on the ones labeled not real.
- agentDraft vocab or rule v-next and queue an offline replay on stored matches with labels.
- founderApprove go-live if precision clears the floor and labeled-real matches aren't lost.
test it opens
Offline replay, then shadow beside live for a week
closes when
Precision back above the floor on 50 new labels, or the detector is retired
logs
alert, replay id, old and new version, precision and recall on the replay, decision
Source stale or failing — warn · fires on: Newest item older than max staleness, or over 10% of runs partial or error
- systemMark the source stalled or degraded; alert. Detectors on it are marked as missing input, so their silence isn't read as no demand.
- agentCheck the last good run, the error text and whether the feed URL or API changed.
- founderDecide: fix, swap to another source, or retire. A swap keeps the same row shape.
test it opens
None for breakage; a replacement source runs in shadow until its matches look like the old one's
closes when
Fresh items flowing for 24 hours, or source retired
logs
alert, error class, downtime, items missed estimate, replacement source id
Reputation guardrail — critical · fires on: Stop requests over 2 per 100 touches, a touched holdout, or a spam report
- systemPause the campaign immediately.
- agentList the touches that drew stops: play, step, template, channel, time.
- founderRead them. Decide: kill the step, rewrite the template, or narrow the segment.
- founderApprove a restart only with the change in place.
test it opens
The rewritten step restarts as a new variant against the old (if kept) with a fresh stop-rate guardrail
closes when
Campaign resumed with the change, or ended
logs
alert, touches involved, template versions, decision, restart date
Play underperforming — warn · fires on: Reply rate per touch below the play's lower control limit (4-week window)
- agentSplit the drop by step, channel, lead tier and trigger age. Rule out a source or lead-mix change first.
- agentIf the mix is stable, draft an opener test: current opener vs one challenger, same play.
- founderApprove the test; it runs with a holdout share and a pre-set win rule.
test it opens
Randomized opener test on the play's next touches
closes when
Readout done and decision logged
logs
alert, diagnosis notes, hypothesis id, readout id
Draft drift — warn · fires on: Median edit distance up a third week over week, or unedited share down 25%
- agentGroup the week's edits by edit locus (hook, body, ending) to see where the founder rewrites.
- agentQueue an offline replay: current prompt vs a v-next tuned on the edits, scored against what was actually posted.
- founderApprove v-next if the replay brings drafts closer to what was posted.
test it opens
Offline replay against posted text, then a live slot test via the randomized draft slot
closes when
Edit distance back within its limits for 2 weeks
logs
alert, edit locus breakdown, prompt versions, replay scores
Lead ranking off — warn · fires on: Top-10 reply rate below its lower control limit 2 weeks running, or founder splits over 5% of merges
- agentReplay the scorer on frozen features: does a reweighted version rank past repliers higher?
- agentFor merges: list the split pairs and the rule that joined them; tighten and replay.
- founderApprove the new scorer or merge rule.
test it opens
Offline replay first; then a randomized slice of the daily list ranked by the new scorer
closes when
Readout done
logs
alert, replay metrics, scorer versions
Funnel rung below limits — warn · fires on: One rung's conversion below its lower control limit 2 weeks running
- agentConfirm the rung's input volume and mix didn't change; find the step that feeds it.
- founderMonday review: make it the constraint for the week.
- agentDraft one hypothesis aimed at that rung.
test it opens
One randomized test on the rung (offer, copy or step order)
closes when
Rung back inside limits, or test read out
logs
alert, constraint choice, hypothesis id
Behind the goal pace — warn · fires on: Waitlist joins behind the straight line to the goal
- systemPut the pace line and the rung chart at the top of the Monday review.
- founderPick the weakest rung as the week's constraint.
- agentOpen one hypothesis on that constraint; park other open ideas.
test it opens
Whatever the constraint's hypothesis needs
closes when
The week's review is done
logs
pace, constraint chosen, hypothesis id
Readout or label overdue — info · fires on: A test past its judge-by date, a campaign past its readout date, or a firing unlabeled after 7 days
- systemNudge the owner with the one action needed.
- agentPre-compute the readout so it only needs a decision.
test it opens
None
closes when
Readout written or label set
logs
overdue item, days late
Threshold crying wolf — info · fires on: Over half of a threshold's last 10 firings labeled false alarm
- agentRe-fit the level from the last 8 clean weeks; draft it as a new version.
- agentRun the new level silent beside the old for 2 weeks.
- founderApprove the swap if it would have caught the real firings with fewer false ones.
test it opens
Silent side-by-side of two threshold versions
closes when
New version live, or old kept with a reason
logs
old and new levels, back-test firings vs labels
illustrative example
Nov 13: Reddit RSS goes quiet.
Say the Reddit feeds you read stop on Nov 13. Here is the source-stale runbook running on that day, written ahead of time.
Fire — thr.src.stale: the Reddit sources' newest item passes max staleness. The run-error line may fire first, as fetches start failing.
Contain — The system marks the sources stalled. Reddit detectors are flagged missing input, so the drop in Reddit signals isn't read as Reddit demand falling.
Diagnose — The agent confirms the feeds return nothing, not an error page, and that the end date matches the known Nov 13 cutoff.
Decide — The founder picks the replacement source. It runs in shadow first, writing rows in the same shape so detector history stays comparable.
Close — Once the replacement's matches look like the old ones on labels, it goes live. The firing is labeled real; the runbook logs downtime and leads missed.
failure modes
How runbooks fail.
Fixing without containing — Debugging a flooding detector while it keeps filling the founder's list. · Containment is always step 1 and always automatic.
Changing three things — The play underperforms, so opener, timing and segment all change at once. Nothing is learned. · Every runbook ends in one hypothesis; the others go to the queue.
No close rule — Runbooks stay open forever, and nobody labels the firing. · Each runbook has close_when; the overdue runbook chases it.
Auto-acting on people — An agent rewrites a live template because a metric dipped. · Only the founder approves changes to what a person sees.
data to capture
Every runbook run is a training example.
record — Every runbook run: which firing started it, each step's who, what and when, the replay or test it opened, the decision.
label — Whether the firing was real, whether containment was needed, whether the fix moved the metric by +7 and +28 days.
trains — Time-to-contain and time-to-close per family, and the improvement agent's diagnosis accuracy.
test — A runbook change is tested by comparing time-to-close and recovery rate before and after its version.