At the close of an epic (or a difficult issue), post a postmortem roundup comment β what went well, what was hard (backed by evidence), and 1β3 concrete proposals to improve the skills/flows β then offer to turn accepted proposals into their own `workflow`-labeled tasks, and end with a `π Next` handoff (a paste-ready next-session prompt + the next epic to start). Use at epic close, after the "verify the whole thing" rollup, or when asked to "do a retro", "postmortem this epic", "what did we ...
Installs into .claude/skills of the current project.
Are you the author of Postmortem?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/friendlyinternet-postmortem)
---
name: postmortem
layer: method
description: At the close of an epic (or a difficult issue), post a postmortem roundup comment β what went well, what was hard (backed by evidence), and 1β3 concrete proposals to improve the skills/flows β then offer to turn accepted proposals into their own `workflow`-labeled tasks, and end with a `π Next` handoff (a paste-ready next-session prompt + the next epic to start). Use at epic close, after the "verify the whole thing" rollup, or when asked to "do a retro", "postmortem this epic", "what did we learn", "how do we improve the workflow".
allowed-tools: mcp__github__issue_read, mcp__github__add_issue_comment, mcp__github__issue_write, mcp__github__sub_issue_write, mcp__github__list_issues, mcp__github__search_issues, mcp__github__list_commits, Read, Bash
---
# Postmortem β learn from the epic, tighten the loop
Turns a just-finished epic (or a difficult issue) into a short, honest **retro
comment on the epic**, plus an offer to spin the good ideas into tracked
`workflow` tasks. The point isn't ceremony β it's that **the loop only gets
tighter if we feed each run's friction back into the skills and flows**. See
epic #403.
This runs **after** the `github-tasks` "## π§ͺ Verify the whole thing" rollup
(verify = *does it work?*) and **before** the epic is closed (postmortem =
*how did building it go, and how do we improve?*).
## When to use
- **Default:** at epic close, once all sub-issues have merged.
- A single **difficult issue** worth a retro on its own (lots of blocks, re-work).
- On ask: "do a retro / postmortem on #NN", "what did we learn".
- **Skip** for a trivial chore β not every issue needs a retro.
## What it produces
A single **comment on the epic** (`add_issue_comment`) with three sections, and
β on your go-ahead β one or more new issues labelled `workflow`.
## Step 1 β Resolve scope
- Input is an **epic number** (default) or a single issue number.
- Read it + its children: `issue_read` (`get`, `get_sub_issues`). Note which
children merged cleanly vs. needed re-work.
## Step 2 β Mine the difficulty signals (evidence, not vibes)
For each child issue (and its PR), pull what the available tools can give. Each signal
below lists **how** to get it and how **precise** it is β be honest about the gaps, and
**never imply a measurement you didn't make**.
| Signal | How to get it (GitHub MCP) | Precision |
|---|---|---|
| **Time-in-progress** | `get_sub_issues` β per child `created_at` β `closed_at` | rough (proxy for build effort) |
| **Sign-off rounds** | `issue_read get_comments` on the child's PR β count revisions on the `<!-- ui-proposal:* -->` / `<!-- schema-review:* -->` sticky thread + review replies | good (markers are explicit) |
| **Asked-for-help / blocks** | `get_comments` β count `@`-mention + "blocked" comments; `get_labels` β is `status:blocked` still on it | good for *count* |
| **Blocked duration** | not exposed directly (label add/remove timestamps aren't in these MCP methods) β approximate from the gap between the blocking comment and the resuming comment | weak β **state it's an approximation** |
| **Re-work / commit churn** | `list_commits` on the PR branch β many "fix"/"oops" rounds = friction | available now |
| **Fix-bot attempts** | the `claude/issue-*` retry count (from #336 watch-to-merge) | only when #336 is live β say "n/a" otherwise |
| **Reopens** | `issue_read get` per child β reopened-after-close | approx |
Aggregate into **3β5 "what was hard" lines**, each tied to a specific child + its signal.
Prefer a few true signals over a complete-looking guess; where a signal is unavailable,
write "n/a (not measurable with current tooling)" rather than inventing a number.
## Step 3 β Write the roundup comment
Keep it terse and human-first (the `github-tasks` two-audience convention). Three
sections:
- **β What went well** β what was smooth / worth keeping. Name it concretely.
- **β οΈ What was hard** β the friction, each line tied to a signal or a specific
issue (e.g. *"#NN sat blocked 3 days waiting on a sibling; the schema gate took
2 revision rounds"*).
- **π§ Proposals (1β3)** β concrete, actionable changes to a **skill / agent /
flow** that would remove that friction next time. Each proposal = a candidate
`workflow` task with a one-line "why".
- **π Next** β the handoff (composed in Step 6): the next epic to start + a
paste-ready next-session prompt. End the roundup with this so it's the first
thing visible when the epic is reopened.
**Lead the comment with a machine-readable marker** so automation can detect that
this epic has had its postmortem without parsing the prose. Put it on the very
first line (it's an HTML comment β invisible in the rendered view, present in the
raw body via the API):
```
<!-- postmortem:done epic=#NN -->
```
The "ready to close vs needs postmortem" labeller (#763, `label-ready-epics.mjs`)
greps the epic's comments for `postmortem:done`: present β `status:ready-to-close`,
absent β `status:needs-postmortem`. Use the real epic number for `#NN`.
Post it with `add_issue_comment` on the epic.
**Flip the labels immediately (don't wait for the daily cron).** Right after posting,
add `status:ready-to-close` and remove `status:needs-postmortem` on the epic
(`issue_write` / label tools) so the review queue updates *now* β `label-ready-epics`
stays as the daily safety net. The epic is then closeable by the owner simply commenting
**`/close-epic`** on it (`close-epic-on-comment.yml` closes it, gated on the
`status:ready-to-close` label, #856) β surface that in the `π Next` handoff so they know
the one-gesture close is available.
## Step 4 β Dedup against existing follow-ups, then offer to turn proposals into work
**First, dedup β don't mint duplicates.** Before proposing anything new, search for an
existing home for each proposal: an open follow-up / "harden X" **epic** or issues that
already capture it. Use `search_issues` with the proposal's keywords (and `label:epic`),
and check the source epic's own linked follow-ups. If a proposal is already tracked,
**link to that issue/epic in the roundup instead of opening a new one**; add only a
*genuinely-new* proposal to that epic as a sub-issue (`sub_issue_write`). The roundup
should state, per proposal, **where it's tracked** (existing #NN, or "new").
> Why this matters: the #274 dogfood found its proposals already lived in epic #291 β
> the right move was to link #291, not open parallel `workflow` issues.
Then ask the human (plain): *"Open the remaining (untracked) proposals as `workflow`
tasks?"* On a yes, for each accepted, **not-already-tracked** proposal create an issue
with `issue_write`:
- Labels: **`workflow`** + **`meta:agents`** + one `type:*` (usually `type:chore` or `type:feat`).
- Body: the proposal as a small **hypothesis** (what we change, why, how we'll know it helped). Reference the source epic.
- If several relate, group them under a small tracking parent via `sub_issue_write`.
Don't open issues the human didn't accept; don't pad with filler β 1β3 real ones beat ten
generic ones; and **never open a `workflow` issue that duplicates an existing follow-up
epic** β link it instead.
## Step 5 β Propagate the change to neighbouring issues
The epic's decisions often land on *other* tickets β a sibling epic, a consumer, something it
supersedes. Tell them, so nobody works around a change they didn't know happened.
- **Find neighbours:** issues/PRs referenced by the epic, its sub-issues, and their PR bodies;
plus a keyword `search_issues` on the change (reuse the Step-4 dedup search).
- **Open neighbour β always comment:** one line of *what changed for you* + the link
(e.g. "#515's release now wires to this App β see #NN"). This is how live work wires *into* the
change instead of around it.
- **Closed neighbour β comment ONLY if the change supersedes/contradicts what it decided** β a
terse "superseded / updated by #NN" forward-pointer for later archaeology. **Never** for mere
topical relevance (dead-ticket noise).
- Keep it to the genuinely-affected few β a couple of true cross-links beat ten tangential
mentions (same spirit as the Step-4 dedup). Each comment carries the `π€` provenance header and
is informational (won't trip agent-resume triggers).
## Step 6 β Hand off the next session (the `π Next` block)
A postmortem must not dead-end β it points at what to do next, so there's no "now what?" gap
between sessions (#615). Compose a **`π Next` block** and include it as the closing section of
the Step-3 roundup (so it's visible the moment the epic is reopened), **and echo the same block in
your chat reply** so it can be pasted straight into a new session.
1. **Pick the next epic.** From the initiative tree this epic belongs to (e.g. the `[Initiative]`
parent) and the open `label:epic` list, choose the most sensible next one:
- prefer an **unblocked sibling under the same initiative** (no open `Blocked-by:`), then the
highest-priority open flow epic;
- skip epics already in progress with an owner.
State the choice + a one-line **why**. If there's **no obvious next epic**, say so and offer
2β3 candidates rather than guessing.
2. **Emit a paste-ready prompt.** A fenced block a human can drop into a fresh session, containing:
- **CONTEXT (done, don't redo):** 2β4 lines β what this epic delivered + any live state the next
session needs (branch, preview URL, an open PR left as a testbed).
- **NEXT:** the chosen issue `#NN` + its full URL, and "read it first, then propose a short plan".
- **RULES:** ISSUE-FIRST Β· `/commit` skill, reference `(#NN)` Β· PRs not direct-to-`main` Β· never
touch `packages/` without approval Β· deploy STAGING only.
3. **Post + echo.** The `π Next` block is the last section of the roundup comment (Step 3); repeat
it verbatim in the chat reply.
Then **report:** the epic comment URL, the `workflow` issues opened (with URLs), the neighbour
issues cross-linked, and the chosen next epic. This closes the learning loop *and* opens the next β
the proposals are on the board, the change is wired to everything it touches, and the next session
has a runway.
## Conventions & gotchas
- **After verify, before close.** Don't close the epic until the postmortem is
posted (the `github-tasks` epic-close walk-up sequences this).
- **Headless = no dialogs.** If running unattended, post the roundup + proposals
and @mention the owner rather than blocking on a prompt; the owner converts
proposals to issues by replying.
- **Bot-comment filter.** The roundup is informational; it should not itself
trip the agent-resume triggers (`resume-on-comment.yml` ignores bot authors).
- **Signals are best-effort.** Prefer a smaller, true set of evidence over a
complete-looking but guessed one.
- **Dedup first.** A proposal already owned by a follow-up/hardening epic gets a
link in the roundup, not a fresh `workflow` issue (proven by the #274 dogfood,
whose learnings already lived in #291).