skills / code

postmortem-writing

what it does

Write effective blameless postmortems with root cause analysis, timelines, and action items. Use when conducting incident reviews, writing postmortem documents, or improving incident response processes.

Verified Before and after: 2 models 1 try per case View source Download .zip

Compare the answers

Answers from Claude Sonnet 5.5 and GPT-6.1 Sol · try 1 of 1

Same request, same model, once with the skill and once without. Read both and judge for yourself.

“Write the postmortem for yesterday's incident from these notes.”

Source material given with the request

Parcelly incident notes, 2026-10-14 (all times UTC). Pasted from #inc-label-api. 13:58 Deploy 2026.10.14-3 runs migration 0142: CREATE INDEX on shipments(carrier_id, created_at). shipments has ~48M rows. Index built without CONCURRENTLY, so writes to shipments block. 14:03 First support ticket (Bramble Goods): "label creation hanging". 14:06 11 more tickets. 14:09 Support posts in #eng asking if anything is down. 14:12 PagerDuty: label-api p99 latency > 10s for 10 min. On-call Dana Okafor paged. 14:15 Dana acks, starts looking at label-api pods (look healthy). 14:24 Dana finds ~3,900 queries waiting on a lock in pg_stat_activity, traces it to the index build. 14:27 Decision: cancel the index build. 14:29 Build cancelled. Writes resume, backlog starts draining. 14:41 p99 back to normal. Incident closed. Impact: ~6,200 label requests failed or timed out; 214 merchants affected; 57 support tickets. Client retries created 38 duplicate labels, voided by hand ($412 in carrier fees refunded). No data lost. From the debrief: - Ravi Menon wrote migration 0142. Someone wrote "Ravi should have known not to run this during
Show the rest (8 more lines)
business hours, rookie mistake." - Migrations run automatically on deploy. No lock_timeout is set for migrations. - Our migration linter checks naming only; it does not flag CREATE INDEX without CONCURRENTLY. - The latency alert needs 10 minutes over threshold before it fires. - No runbook for lock contention; Dana spent 9 minutes checking pods first. - Dana offered to add a lock_timeout to the migration runner. The Platform team (lead: Sofia Reyes) owns the linter.
with the skill
# Postmortem: Label Creation Disruption from Blocking Index Migration **Incident date:** October 14, 2026 **Status:** Draft for review **Severity:** Not recorded **Incident window:** 13:58–14:41 UTC — 43 minutes **Affected service:** Label API ## Executive Summary On October 14, deployment `2026.10.14-3` automatically ran migration `0142`, which created an index on `shipments(carrier_id, created_at)`. The table contains approximately 48 million rows. Because the index was created without `CONCURRENTLY`, the operation blocked writes to `shipments`, causing label requests to hang, fail, or time out. The incident affected 214 merchants and approximately 6,200 label requests. Client retries created 38 duplicate labels, which were manually voided; $412 in carrier fees was refunded. No data was lost. The on-call engineer identified the blocking index build at 14:24. It was canceled at 14:29, allowing writes to resume. The backlog drained and p99 latency returned to normal at 14:41. The failure was enabled by automated execution of a write-blocking migration without safeguards appropriate for a large production table. Migration linting checked naming only, and there was no lock-contention runbook. Detection also lagged customer reports because the latency alert required ten minutes above threshold. ## Customer Impact
Show the rest (84 more lines)
- Approximately **6,200 label requests** failed or timed out. - **214 merchants** were affected. - Support received **57 tickets**. - Client retries created **38 duplicate labels**, subsequently voided manually. - **$412 in carrier fees** was refunded. - **No data was lost.** Writes resumed at 14:29, but customer-facing latency did not fully recover until 14:41 as the backlog drained. ## Timeline All times UTC on October 14, 2026. | Time | Event | |---|---| | 13:58 | Deployment `2026.10.14-3` runs migration `0142`: `CREATE INDEX` on `shipments(carrier_id, created_at)` without `CONCURRENTLY`. Writes to the approximately 48-million-row table block. | | 14:03 | First support ticket arrives from Bramble Goods reporting that label creation is hanging. | | 14:06 | Eleven additional support tickets have arrived. | | 14:09 | Support asks in `#eng` whether a service is down. | | 14:12 | PagerDuty alerts on label-api p99 latency exceeding ten seconds for ten minutes. Dana Okafor is paged. | | 14:15 | Dana acknowledges and begins investigating label-api pods, which appear healthy. | | 14:24 | Dana finds approximately 3,900 queries waiting on a lock in `pg_stat_activity` and traces the contention to the index build. | | 14:27 | Decision made to cancel the index build. | | 14:29 | Index build canceled. Writes resume and the backlog begins draining. | | 14:41 | p99 latency returns to normal. Incident closed. | ## Cause and Contributing Factors ### Direct cause Migration `0142` used a non-concurrent index build on `shipments`. That operation blocked writes for the duration of the build, causing requests dependent on those writes to accumulate. ### Systemic contributing factors - **Deployment automatically executed migrations.** A potentially long-running, write-blocking schema change ran as part of the normal deployment path. - **Migration checks did not assess operational safety.** The linter checked naming but did not flag `CREATE INDEX` without `CONCURRENTLY`. - **No migration `lock_timeout` was configured.** Migration lock acquisition had no configured wait bound. However, adding `lock_timeout` alone would not prevent this incident: it limits time spent *waiting to acquire* a lock, not how long an acquired lock is held during an index build. - **Latency alerting delayed the page.** The alert required ten minutes above threshold. The page arrived 14 minutes after the migration began and nine minutes after the first customer report. - **No lock-contention runbook existed.** Initial investigation focused on apparently healthy application pods for nine minutes before database lock contention was identified. - **Retries amplified the impact.** Client retries produced duplicate labels. The notes do not establish which retry or duplicate-prevention behavior allowed this; that requires follow-up investigation. The corrective focus is on migration controls and response tooling, not individual judgment. The deployment path allowed this migration to reach production without detecting or containing its operational risk. ## Detection and Response ### What worked - Support surfaced customer symptoms before the automated page. - Database inspection exposed the lock queue and its blocking operation. - Canceling the index build restored writes. - Recovery was confirmed through normalized p99 latency. - Duplicate labels were identified and manually voided. ### What could improve - Automated paging followed customer reports rather than preceding them. - Healthy pods did not rule out a blocked database dependency; responders lacked a runbook directing them to lock diagnostics. - Approximately 3,900 queries accumulated behind the blocking operation. - Recovery required manual cancellation and a further 12 minutes of backlog drainage. - Duplicate-label remediation required manual work. ## Follow-up Actions **Owners below are proposed unless explicitly noted; assignments and due dates must be confirmed before this postmortem is finalized.** | Priority | Action and completion criteria | Owner | Due | |---|---|---|---| | P0 | Add migration linting that rejects non-concurrent index creation on existing production tables unless an explicit exception is approved. Include regression tests for migration `0142`’s pattern. | Platform — Sofia Reyes, proposed accountable lead | TBD | | P0 | Add migration `lock_timeout` handling with a safe failure path. Verify that a migration unable to acquire its lock exits rather than waiting indefinitely. | Dana Okafor — volunteered; assignment to confirm | TBD | | P0 | Define and implement a safe production index-build procedure, including concurrent execution outside a transaction where required, monitoring, cancellation, and cleanup of failed builds. Validate before retrying migration `0142`. | TBD | TBD | | P1 | Define execution-time safeguards for migrations, separate from lock-acquisition timeouts, and test cancellation behavior for long-running operations. | TBD | TBD | | P1 | Create a lock-contention runbook covering blocker identification, `pg_stat_activity`, cancellation decisions, and recovery verification. Validate it in a response exercise. | TBD | TBD | | P1 | Review the ten-minute latency-alert window and add actionable detection for sustained write blocking or growing lock queues. Test detection with a representative blocking scenario. | TBD | TBD | | P1 | Investigate the 38 duplicate labels and implement or strengthen retry-safe label creation. Verify that retrying a timed-out request cannot create an additional label. | TBD | TBD | ## Key Lessons 1. Schema changes must be evaluated for locking behavior at production scale, not just SQL correctness. 2. Automated deployment paths need automated migration-safety checks. 3. Lock-acquisition timeouts and limits on long-running operations address different risks; both require deliberate design. 4. Database-lock diagnostics should be part of the initial response to hanging requests, even when application pods appear healthy. 5. Request timeouts and retries can create secondary customer impact unless label creation is retry-safe. **Before finalization:** Confirm severity, action owners, and due dates; attach available deployment logs, lock diagnostics, latency graphs, and remediation records.
without
# Postmortem: Label creation disruption caused by a blocking database migration **Incident date:** October 14, 2026 **Incident window:** 13:58–14:41 UTC (43 minutes) **Status:** Resolved **Service affected:** Label API **On-call responder:** Dana Okafor ## Summary Deployment `2026.10.14-3` automatically ran migration `0142`, which created an index on `shipments(carrier_id, created_at)`. The index was built without `CONCURRENTLY` on a table containing approximately 48 million rows, blocking writes to `shipments`. Label requests accumulated behind the database lock, causing failures and timeouts. The index build was canceled at 14:29 UTC, allowing writes to resume. The backlog drained, and p99 latency returned to normal at 14:41 UTC. The incident exposed gaps in migration safety checks, lock-wait safeguards, alerting speed, and lock-contention response documentation. ## Customer impact - Approximately **6,200 label requests failed or timed out**. - **214 merchants** were affected.
Show the rest (70 more lines)
- Support received **57 tickets**. - Client retries created **38 duplicate labels**, which were manually voided. - **$412 in carrier fees** was refunded. - **No data was lost.** ## Timeline All times UTC on October 14, 2026. | Time | Event | |---|---| | 13:58 | Deployment `2026.10.14-3` runs migration `0142`. The non-concurrent index build blocks writes to `shipments`. | | 14:03 | First support ticket arrives from Bramble Goods reporting that label creation is hanging. | | 14:06 | Support has received 11 additional tickets. | | 14:09 | Support asks in `#eng` whether a service is down. | | 14:12 | PagerDuty fires after label-api p99 latency exceeds 10 seconds for 10 minutes. Dana Okafor is paged. | | 14:15 | Dana acknowledges the page and begins investigating label-api pods, which appear healthy. | | 14:24 | Dana identifies approximately 3,900 queries waiting on a database lock and traces the contention to the index build. | | 14:27 | Decision made to cancel the index build. | | 14:29 | Index build is canceled. Writes resume and the backlog begins draining. | | 14:41 | p99 latency returns to normal; incident is closed. | ## Cause and contributing factors ### Direct cause Migration `0142` used `CREATE INDEX` without `CONCURRENTLY` on the approximately 48-million-row `shipments` table. The operation blocked writes for 31 minutes, until it was canceled. ### Contributing factors - **Automatic migration execution:** Migrations run automatically during deployment, so this blocking operation entered production through the normal release path. - **Insufficient static checks:** The migration linter checks naming only and did not flag the non-concurrent index build. - **Missing migration lock-wait safeguard:** No `lock_timeout` is configured for migrations. This is a safety gap, although it would not by itself limit how long an index build holds a lock once acquired. - **Delayed paging:** The latency alert requires 10 minutes above threshold before firing. Customer reports began nine minutes before the page. - **Missing diagnostic runbook:** There was no lock-contention runbook. Initial investigation focused on healthy application pods for nine minutes before the database lock was identified. This was a failure of deployment safeguards and operational tooling, not an individual failure. The normal migration workflow allowed a write-blocking operation without identifying or preventing its production impact. ## Response assessment ### What worked - Support surfaced customer symptoms before the automated page. - Database inspection identified the lock contention and its source. - Canceling the index build restored writes without data loss. - Duplicate labels were manually voided and associated carrier fees refunded. ### What could improve - Migration validation did not catch an unsafe index-build pattern. - Paging lagged behind customer-visible impact. - Responders lacked a documented path for diagnosing database lock contention. - Client retries produced duplicate labels, adding customer impact and manual cleanup. ## Follow-up actions These actions are proposed from the debrief; completion dates and unassigned owners still need to be confirmed. | Action | Owner | Status | |---|---|---| | Add an appropriate `lock_timeout` to the migration runner to bound lock-acquisition waits. Document that this does not bound lock-holding duration. | Dana Okafor volunteered | Proposed | | Extend the migration linter to flag `CREATE INDEX` without `CONCURRENTLY`, with an explicit review path for exceptions. | Platform team; lead: Sofia Reyes | Proposed | | Define and enforce a safe production index-build procedure, including migration-runner compatibility and review requirements. | TBD | Proposed | | Review the latency alert’s 10-minute duration and evaluate earlier signals for blocked queries or lock waits. | TBD | Proposed | | Create a lock-contention runbook covering diagnosis, blocker identification, and safe cancellation criteria. | TBD | Proposed | | Investigate retry and idempotency behavior to prevent duplicate labels after timeouts. | TBD | Proposed | ## Key lesson Production migration safety must be enforced by the release system rather than depend on individual recall. Preventing a recurrence requires checks for blocking database operations, faster detection, and a clear response procedure for lock contention.

Answers are shown exactly as the model wrote them, from the first try, and nothing is picked by hand.

The models

2 models

Claude Sonnet 5.5

made by Anthropic

claude-sonnet-5-5 · revision not reported by the provider

1 try per case · tested 3 Nov 2026

GPT-6.1 Sol

made by OpenAI

gpt-6.1-sol · revision not reported by the provider

1 try per case · tested 3 Nov 2026

The tasks

written by Carte Blanche
blameless-rewrite-tls

“My manager says this postmortem draft is too blamey. Can you rewrite the root cause and action items?”

fix-weak-action-items

“Our postmortem's action items feel weak. Can you rewrite them?”

full-postmortem-from-notes

“Write the postmortem for yesterday's incident from these notes.”

quick-sev3-writeup

“Can you write a short postmortem for this small incident? We don't need the full template, just something for the incident log.”

root-cause-past-disk-full

“Our draft root cause just says 'disk filled up on search-node-3'. Is that enough? Help me write a better root cause section.”

Install

Download the .zip and unzip it. Put its files, in a folder named postmortem-writing, here:

Claude Code~/.claude/skills/Every project. For one project only, use .claude/skills/ inside it.
Codex~/.agents/skills/Every project. For one repository only, use .agents/skills/ inside it.
Cursor~/.cursor/skills/Cursor also reads the Claude Code and Codex folders above.

Then start a new session and ask for what the skill does. Other agents that read SKILL.md have their own skills folder; check their docs.

Coming after launch: get skills from inside your agent, no download needed.