Incident Postmortem Writer
MIT↓ 0 downloadsA single Claude Code agent turns an incident timeline and the last week of commit history into a blameless postmortem — summary, impact, timeline, root causes, contributing factors, what went well, and action items with owners and due dates — written to postmortem/POSTMORTEM.md.
Topology
Disclosures
Everything below runs on your machine or inside the sandbox when you use this workflow. Mismatches between these declarations and the actual code block publishing.
Host hooks
Commands executed on YOUR host machine by Sandcastle lifecycle hooks.
None declared.
Sandbox hooks
Commands executed inside the sandbox container.
None declared.
Network access
None. The agent operates only on the local repository inside the sandbox; the prompt runs `git log` locally to seed recent commit context.
Shell expansion
Prompt files contain !`command` blocks — the agent CLI executes these commands at prompt-load time. They are highlighted amber in the prompt files below.
Files
Diff vs the stock Sandcastle 0.12.0 template Dockerfile — green lines were added by the author, red lines were removed from stock.
+# Sandbox image for the Incident Postmortem Writer workflow.+# Node 22 + git (the prompt seeds `git log` context) + the Claude Code CLI,+# running as a non-root `agent` user.FROM node:22-bookworm# System dependencies.RUN apt-get update && apt-get install -y --no-install-recommends \git \curl \jq \ca-certificates \&& rm -rf /var/lib/apt/lists/*# Claude Code CLI (the agent runtime).RUN npm install -g @anthropic-ai/claude-code# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to# the host user via --build-arg to avoid permission errors on bind mounts.+# node:22-bookworm already ships a "node" user at UID/GID 1000, so we RENAME it+# (the stock Sandcastle template pattern) — groupadd/useradd would collide with+# the existing IDs on a default build.ARG AGENT_UID=1000ARG AGENT_GID=1000-RUN groupadd --gid ${AGENT_GID} agent \- && useradd --uid ${AGENT_UID} --gid ${AGENT_GID} --create-home --shell /bin/bash agent+RUN groupmod -o -g ${AGENT_GID} node \+ && usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node-USER agent-WORKDIR /workspace+USER ${AGENT_UID}:${AGENT_GID}+WORKDIR /home/agent++# Sandcastle bind-mounts the worktree and sets the working directory at+# container start; the container just needs to stay alive until then.+ENTRYPOINT ["sleep", "infinity"]
Show full Dockerfile (highlighted)
# Sandbox image for the Incident Postmortem Writer workflow.
# Node 22 + git (the prompt seeds `git log` context) + the Claude Code CLI,
# running as a non-root `agent` user.
FROM node:22-bookworm
# System dependencies.
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
jq \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Claude Code CLI (the agent runtime).
RUN npm install -g @anthropic-ai/claude-code
# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to
# the host user via --build-arg to avoid permission errors on bind mounts.
# node:22-bookworm already ships a "node" user at UID/GID 1000, so we RENAME it
# (the stock Sandcastle template pattern) — groupadd/useradd would collide with
# the existing IDs on a default build.
ARG AGENT_UID=1000
ARG AGENT_GID=1000
RUN groupmod -o -g ${AGENT_GID} node \
&& usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node
USER ${AGENT_UID}:${AGENT_GID}
WORKDIR /home/agent
# Sandcastle bind-mounts the worktree and sets the working directory at
# container start; the container just needs to stay alive until then.
ENTRYPOINT ["sleep", "infinity"]
# Auth for the Claude Code writer agent.
# Run `claude setup-token` on your host to generate a token, then paste it here
# in your local .sandcastle/.env (never commit the real .env).
CLAUDE_CODE_OAUTH_TOKEN=
import { run, claudeCode } from "@ai-hero/sandcastle";
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
// A single-pass postmortem writer. One Opus agent reads the incident.md timeline
// and the recent git history (seeded into the prompt via a `!`git log`` shell
// expansion block), then writes a blameless postmortem. maxIterations is 1
// because this is a one-shot document, not a loop. Default `head` branch
// strategy on docker means the commit lands on the current branch.
const result = await run({
name: "incident-postmortem",
agent: claudeCode("claude-opus-4-8", { effort: "high" }),
sandbox: docker(),
promptFile: ".sandcastle/prompt.md",
maxIterations: 1,
});
console.log(`Postmortem written in ${result.commits.length} commit(s) on ${result.branch}.`);
Incident postmortem
You are an incident reviewer writing a blameless postmortem. The goal is to learn and prevent recurrence, never to assign fault to individuals. Describe what the system and process allowed to happen, not who to blame.
Recent commit history
!git log --since="7 days ago" --oneline
Input
Read incident.md in the repository root — the incident timeline, symptoms,
detection, and remediation notes. Cross-reference the commit history above to
identify changes that may have contributed. If incident.md is missing, say so
and stop.
Your task
Write postmortem/POSTMORTEM.md in a standard blameless format:
- Summary — 2–3 sentences: what happened, when, and the headline impact.
- Impact — who and what was affected, for how long, and severity. Quantify where the notes allow (users affected, duration, error rate, revenue).
- Timeline — a chronological table (timestamp, event) from first signal through detection, mitigation, and resolution.
- Root cause(s) — the underlying technical and/or process cause(s). Use the commit history to point at specific changes where relevant.
- Contributing factors — conditions that made the incident more likely, worse, or slower to resolve (monitoring gaps, missing tests, unclear owners).
- What went well — detection, response, and recovery strengths worth keeping. A real blameless postmortem always credits these.
- Action items — a table of concrete follow-ups, each with an owner and a
due date. Prefer systemic fixes (guardrails, alerts, tests) over "be more
careful". Where an owner or date is unknown, write
TBD.
Keep the tone factual and neutral. Do not invent timestamps, metrics, or names
that are not supported by incident.md or the commit history. Commit the file
with a message like ops: incident postmortem.
README
Incident Postmortem Writer
Turn a raw incident timeline into a polished, blameless postmortem your team can review and act on. One Claude Code agent, one Docker sandbox, no hooks.
What it does
The agent reads an incident.md timeline from your repository root and seeds
itself with the last seven days of commit history, then writes a complete
blameless postmortem to postmortem/POSTMORTEM.md. The document follows the
standard structure teams expect: summary, quantified impact, a chronological
timeline table, root cause(s), contributing factors, what went well, and a table
of action items — each with an owner and a due date, biased toward systemic
guardrails over "be more careful".
Because it cross-references your recent commits, it can point root-cause analysis at the specific changes that likely contributed, instead of hand-waving. The tone stays factual and neutral, and it never invents timestamps, metrics, or names that the inputs do not support.
How it works
main.ts calls run() once with maxIterations: 1 against an Opus agent — a
one-shot document, not a loop. The prompt uses a shell-expansion block
(!`git log --since="7 days ago" --oneline`) to inline recent commit
context at run time, which is why usesShellExpansion is declared true. All work
is local: git log reads your repository and there is no network access.
Commits land directly on your current branch through the Docker bind mount.
Requirements
Add an incident.md (timeline, symptoms, detection, remediation) to your repo
root, set CLAUDE_CODE_OAUTH_TOKEN in .sandcastle/.env (run
claude setup-token on your host), then build the image once with
npx @ai-hero/sandcastle docker build-image and run it with
npx tsx .sandcastle/main.ts. Read the result at postmortem/POSTMORTEM.md.