The Incident Drill
Project14 min · Scope → Isolate → Supervise → Land · Updated 2026-09-15
You end this lesson with: fleet/incident-report.md — a simulated fleet incident, contained and written up from the run record.
Chapters
- Brief01
- The Rule This Lab Tests02
- Steps03
- Acceptance04
- Rubric05
- Key Takeaways06
Project 05. The Incident Drill
Illustrative composite. A four-agent fleet at a seed-stage logistics startup shipped a $8-per-task spend cap that existed only on paper — a number in a planning doc, not a check anything actually enforced at runtime. One overnight task got stuck retrying the same failing test for thirty-one minutes; by the time an engineer noticed the next morning, the run had spent $94, twelve times the cap that was supposed to stop it at eight dollars.
Somewhere earlier in this module you built a runbook, fleet/runbook.md, that nobody has actually run against a real incident. A runbook that has never fired is a document, not a capability, and the difference only shows up at 2am, which is a bad time to discover it. This project closes that gap on purpose, before it closes it on you. You will stage one of three incidents against your own fleet or against the sample setups given below, run your runbook against it for real, contain it, and then write the postmortem from what your run record actually shows, not from what you remember happening, because what you remember and what happened diverge exactly the way lesson 16 warned they would. The deliverable is fleet/incident-report.md — a simulated fleet incident, contained and written up from the run record.
Brief
Pick one of the three staged scenarios below, or run all three if you have the time; the rubric is the same either way. Each scenario gives you the exact setup so you do not need a live fleet already misbehaving; you build the failure on purpose, in a sandbox, the same way a fire drill builds a fire nobody actually wants. Use your own fleet/runbook.md, fleet/audit/ and fleet/rollback.md from earlier lessons; if you have not built one of those yet, use the minimal versions given inline here. Do not write the postmortem from memory once the incident is over. Treat that as a rubric failure, because it is the exact anti-pattern this project exists to catch you doing under time pressure.
Scenario 1: a runaway loop against a spend cap
Task: "Fix the flaky test in tests/ingest/dedup.test.ts"
Environment: agent runs unattended; per-agent budget cap set to $8 for
the task, per fleet/cost-model.md's per-task ceiling; no loop detector
wired to enforce it at runtime
Trigger: seed the repo with tests/ingest/dedup.test.ts asserting a value
the implementation cannot produce (a genuinely unfixable assertion, so
the agent cannot succeed no matter what it tries) — this reliably
reproduces "the same failed approach, repeated" without needing a
real bug
Expected failure shape: the agent retries variations of the same fix
for twenty-plus minutes, spend climbs past the cap before anything
stops it, because the cap exists on paper in fleet/cost-model.md but
nothing enforces it at runtime
Scenario 2 — an agent writing to the wrong branch
Task: two agents dispatched at once. Agent A launched normally through
fleet/isolate.sh onto its own branch. Agent B launched with its
branch argument to isolate.sh deliberately omitted, so its launch
falls through to a plain checkout of main instead of a private branch
Trigger: let both agents run to completion and commit
Expected failure shape: Agent B's commits land directly on main, ahead
of any gate, ahead of any review — the isolation launcher's one job,
a private branch per agent, silently did not happen for one agent,
and nothing notices until someone looks at main's history
Scenario 3 — a self-report you cannot trust
Task: "Backfill the committed_stock column using the new calculation,
and roll back if the row count changed unexpectedly"
Environment: a staging database where the backfill's own row-count
check has an off-by-one bug you plant on purpose, so the agent's own
verification step reports "rolled back, 0 rows affected" while the
backfill actually committed and changed every row
Trigger: run the task, then ask the agent directly whether the backfill
is still live — it will self-report that it rolled back, because its
own, buggy, verification told it so
Expected failure shape: the agent's self-report and the database's
actual state disagree, and the only way to know which one is true is
to check the database and the run record, not to ask the agent again
Containment checklist
Work through these in order; the order matters, because doing step three before step one can destroy the evidence step three needs.
- Preserve the run record before doing anything else that might overwrite it. Copy the relevant
fleet/audit/<run-id>.mdentry out of the working tree. - Enforce the cap or kill switch by hand if it did not trigger on its own. Kill the process, revoke the run's credentials.
- Isolate quota and credentials for the offending agent so containment does not stall the rest of the fleet.
- Verify state independently of the agent's self-report. Check main's history, the database, the actual spend dashboard, never the agent's own summary.
- Confirm the blast radius. What else touched the same branch, table, or budget pool while this was running.
- Only after the first five steps, decide revert-and-rerun or patch-forward per
fleet/rollback.md.
The three scenarios above are three symptoms; containment starts by naming which class a symptom belongs to, then acting on the class, the same collapse the runbook lesson argued for.
Postmortem template
# Incident: <one line>
## Detected
- When: <timestamp>
- How: <alert / manual check / customer report — cite the source, not "I noticed">
## Run record
- Run ID / audit file: <path into fleet/audit/>
- Task as given: <verbatim>
- Tools and files touched: <from the record, not from memory>
- Gate verdicts: <pass/fail, verbatim from the record>
- Spend at time of containment: <number, with source>
## What the agent reported vs. what the record shows
- Agent's self-report: <verbatim>
- Run record / independent check: <verbatim>
- Discrepancy: <yes/no, and exactly what differs>
## Containment
- First action taken: <what, and at what timestamp>
- Who or what took it: <human, hook, cap>
- Time to containment: <detected timestamp to contained timestamp>
## Root cause
<One paragraph, cited to the run record and the containment checklist
findings only. No sentence in this section may begin with "the agent
probably" or "it seems like".>
## Decision: revert-and-rerun or patch-forward
- Age / dependents / migration / record: <the four fleet/rollback.md
conditions, answered>
- Decision: <which, and the one condition that decided it>
## Follow-up
- [ ] <fix that prevents this exact incident>
- [ ] <runbook, hook or gate change, if any>
The Rule This Lab Tests
Decide revert-and-rerun once containment step 4 (verify independently) and step 5 (confirm blast radius) come back clean — no irreversible migration, no real work already built on the offending run. Decide patch-forward only when one of those checks turns up an irreversible migration or dependents that are themselves real, not from a general sense of how bad the incident felt before you checked. Y — what steps 4 and 5 actually show once you check the record instead of the agent's account of itself — is the same condition fleet/rollback.md and lesson 16 already named as the one that can flip the decision on its own.
| Scenario | What independent verification (step 4) turns up | Rollback decision |
|---|---|---|
| Scenario 1, runaway loop | Kill stopped the spend; no migration, nothing downstream depends on the wasted run | Revert-and-rerun: correct the task spec, rerun once the cap is enforced |
| Scenario 2, wrong-branch write | Commits landed on main but nothing has merged on top of them yet | Revert-and-rerun: revert before anything downstream depends on it |
| Scenario 3, bad self-report | The backfill already committed against live data — an irreversible migration | Patch-forward: the migration condition decides alone, regardless of the other three |
Steps
Step 2 below is fully worked, with a filled log excerpt showing exactly what "note every timestamp as you go" produces. Step 3 is yours to work out: the checklist raises a real judgment call mid-containment, and the answer is in the fence beneath it, not before it. Steps 4 through 6 are open — no filled answer, no scenario script, just the template and your own record.
-
Pick a scenario (or all three) and stage it, following the exact setup given above. Nothing is produced yet; this is the drill's fixture.
-
Run
fleet/runbook.mdagainst the live incident, in real time, following its first-action rule for the matching symptom. Note every timestamp as you go; do not reconstruct them afterward. A filled example, live-noted against Scenario 1:Illustrative composite. A real run's own timestamps and dollar figures will differ; the shape — noted live, not reconstructed — is what step 6 depends on.
09:14 — task dispatched: "Fix the flaky test in tests/ingest/dedup.test.ts" 09:19 — fifth commit on the same assertion, no behavior change 09:31 — spend at $7.40, cap is $8, no stop yet 09:33 — spend at $8.60, past cap, still running 09:34 — killed process by hand per containment checklist step 2Produces a raw incident log, even scratch notes like the one above, that step 6 turns into the report.
-
Contain it using the checklist above. Preserve the run record before you do anything destructive. Produces a preserved copy of the relevant
fleet/audit/entry. Confirming blast radius (checklist step 5) is a real judgment call, not a rote box to check — work out your own answer first: -
Independently verify what actually happened. Check the database, git history, or spend dashboard directly, never the agent's own summary of itself. Produces the "agent reported vs. record shows" comparison.
-
Decide revert-and-rerun or patch-forward using
fleet/rollback.md's four conditions, and name the one condition that decided it. -
Write
fleet/incident-report.mdfrom the postmortem template above, using only the raw log, the preserved run record, and the independent check from steps 2 through 5 as sources. If a fact is not in one of those three, mark it "unknown" rather than filling it from memory.
Acceptance
- The incident was staged from one of the three scenarios' exact setup, not an ad hoc variant.
- Every timestamp in the report is from the raw log or the run record, not reconstructed after the fact.
- The run record was preserved before containment actions could overwrite it.
- The report includes an explicit "agent's self-report vs. record" comparison, even when they agree.
- Containment followed the checklist order: preserve, enforce, isolate, verify independently, check blast radius, then decide.
- The revert-or-patch-forward decision names one of the four
fleet/rollback.mdconditions as the one that decided it. - The root cause paragraph cites the run record and contains no sentence guessing at what the agent was thinking.
- At least one follow-up item changes a hook, gate or runbook step, not just "be more careful."
Rubric
| Criterion | Weak | Strong |
|---|---|---|
| Sourcing | Timeline and root cause reconstructed from memory after the fact, or from the agent's own summary | Every claim traces to the raw log, the preserved run record, or an independent check named in the report |
| Containment discipline | Contained ad hoc, run record possibly overwritten, agent's self-report trusted as the containment signal | Followed the checklist order; run record preserved before any destructive action; state verified independently |
| Rollback decision | Revert-or-patch-forward asserted with no named condition, or decided before checking the run record | Decision names the one fleet/rollback.md condition that settled it, consistent with lesson 16's rule |
| Postmortem completeness | Missing sections, vague follow-ups like "be more careful," no discrepancy section | All template sections filled or explicitly marked unknown; follow-ups change a mechanism, not a habit |
Key Takeaways
- Test a runbook against a real incident before the incident that actually needs it does.
- Preserve the run record before any containment step that could overwrite it.
- Compare the agent's self-report against an independent check; treat a match as evidence, not an assumption.
- Write timestamps as the incident happens; never reconstruct them for the report afterward.
- Name the one condition that decided revert-or-patch-forward, per
fleet/rollback.md.
What You Have Now
fleet/incident-report.md — a simulated fleet incident, contained and written up from the run record. It sits in fleet/ next to fleet/runbook.md, whose first-action rules it tested for real, and fleet/rollback.md, whose four conditions it applied to an incident you can point at instead of a hypothetical. If you ran more than one scenario, keep every report; a runbook that has only ever been tested against one incident shape has not been tested very hard.
fleet/
topology.md
task-template.md
cost-model.md
readiness-audit.md
isolate.sh
port-map.md
containment.md
permissions/
access.md
collision-report.md
remote.md
venues.md
watch/
cutover.md
review-policy.md
gates/
merge-policy.md
landing-pipeline.md
audit/
runbook.md
rollback.md
incident-report.md
The Fleet Kit above is what you actually hold now, not what you scored on paper in lesson one. topology.md still carries your original guess at where you sat on Concurrency, Isolation, Verification and Locus of control; the twenty files after it are the instruments that replaced the guess with a measurement. Isolation is no longer a claim, it is isolate.sh, containment.md and permissions/ doing the containing. Locus of control is no longer a claim, it is access.md deciding what an agent can reach. Verification is no longer a claim, it is gates/, review-policy.md, and the incident you just contained, where the record, not the agent, said what happened. Open topology.md again and re-score the four axes honestly, with those files in front of you instead of a guess. For most fleets that reach this point, the honest score is still lowest on Verification: concurrency and isolation force the issue the first time two agents collide, but nothing forces review capacity to keep pace the same way, until a change nobody can explain merges anyway and lands you back in this project. Move that axis next, not by adding a fifth agent, but by widening what review-policy.md will accept without you reading every line. That is the one move this course has been arguing for since lesson one: raise what you can verify before you raise how much you generate.
NO-QUIZ
Quiz coming to this lesson
This lesson doesn't have a graded quiz yet — completion is still tracked, just not verified against questions.
Reading is free — your progress isn't being saved
Nothing you did on this page is recorded. Paid students get saved progress and verified completions, recorded quiz answers, the spaced-repetition review queue, labs, and a certificate for each course they finish.
30-day money-back guarantee. One-time payment — lifetime access and updates.