post-mortem について
post-mortemは、検証済みのバグ修正を記録するエンジニア向け文書作成スキルです。症状、根本原因、修正、検証の証拠を確認してから草案を作り、バグ修正記録とインシデント報告を区別します。
主な機能
- 01再現・根本原因・修正・検証を必須化
- 02症状と原因の連鎖を記録
- 03デバッグと検証の証拠を整理
- 04必要に応じてフォローアップを記載
- 05投稿前に草案を確認
活用シーン
- 01検証済みバグ修正を後続エンジニア向けに記録
- 02コード上の原因から症状に至る経緯を説明
- 03再現可能な検証内容を添えてバグをクローズ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ thananon/ 9arm-skills --skill post-mortemインストール後、Claude Code で/post-mortemを入力して読み込まれたか確認します。
詳細
事後分析
バグ修正の正式なエンジニアリング記録です。デバッグの結果、実際の修正が行われた後に、他のエンジニア(そして6か月後にはすべて忘れている未来の自分)のために書きます。ここではコード識別子を使ってかまいません。次の担当者がメンタルモデルをすばやく取り戻すための記録です。
同じ内容を組織の上層部向けにまとめる場合は、完成した事後分析をmanagement-talkに渡してください。事後分析はエンジニアリング上の事実を記録し、management-talkはそれをリーダー向けに再構成します。
使用するタイミング
- 「/post-mortem」
- 「事後分析/postmortem/RCA/根本原因分析を書いて」
- 「この修正を文書化して」/「根本原因をまとめて」/「このバグの対応を記録に残して」
- デバッグセッションで明確に修正が完了した後、事後分析の作成を自発的に提案します。
使用しないタイミング
- バグがまだ修正されていない、または修正が検証されていない場合。 仮説に基づく事後分析は誤解を招きます。作成を断り、何が不足しているかを伝えてください。
- 顧客に影響する停止/インシデントの場合。 これらには別途、インシデントレポート(タイムライン、影響範囲、ページング履歴、コミュニケーション記録)が必要です。このスキルの対象はバグ修正です。作成する前に、その点を伝えて確認してください。
- 軽微な修正(誤字、明らかな一行修正)。記録にはPRの説明を使います。無用な儀式を作らないでください。
必須入力 — これらがなければ作成を断る
一行も書く前に、以下の4項目すべてを確認してください。不足があれば、不足項目を列挙してそこで止めます。
- 信頼できる再現手順がある(「時々起きる」ではなく、次の担当者が実行できる決定的な再現手順、または高頻度で不安定さを再現できる手順)。
- 根本原因が判明している(メカニズムが特定されており、仮説ではない)。
- 修正が特定されている(PR/コミット/ブランチへの参照)。
- 修正が検証されている(元の再現手順が通るようになり、顧客のワークロード/失敗していたテストが成功する)。
これらはdebug-mantraのステップ1~4に直接対応しています。debug-mantra経由で作業を始めた場合は、ステップ4の手掛かり記録が素材になります。そこから情報を取り出してください。
構成
以下のブロックをこの順序で使います。Summary、Root cause、Fix、Validationは必須です。 その他は条件に応じて含めますが、通常は記載します。
1. Summary (必須)
1段落で記述します。ユーザー/ワークロードの観点から、何が壊れたのか。何によって修正されたのかを1文で。JIRAキー、PR番号、担当者も記載します。ここだけ読む人にも、正しい答えが伝わるようにします。
2. Symptom
実際に観測された内容を記載します。テスト出力、エラーメッセージ、ログ行、パフォーマンスの数値、顧客からの報告などです。具体的な識別子を使い、障害の状況を言い換えないでください。
3. Root cause (必須)
実際のバグのメカニズムを記載します。コード識別子の使用を歓迎します。むしろ期待されています。 関数名、ファイルパス、構造体フィールド、分岐条件、問題を起こした変更のコミットSHAなどです。原因の連鎖を最初から最後までたどります。ここが最も重要なセクションであり、事後分析を作成する理由そのものです。未来の自分がうまく対処できるかどうかは、この内容をどれだけ明確に書けるかにかかっています。
4. 症状が発生した理由
根本原因と症状を結び付けます。原因がすぐにはわからないこともよくあります。たとえば、tadaLaunchPrepareにバグがあるのに、目に見える障害は数時間後に顧客のトレーニング実行が停止することです。症状だけを知る読者が原因を自力で導き直さなくても関連付けられるよう、連鎖をたどって説明します。
5. Fix (必須)
何を変更したのか、そしてなぜこの変更が症状を隠すのではなく、根本原因に対処するのかを説明します。PR/コミットへのリンクを記載します。以前の修正の試みが症状を一時的に覆い隠しただけなら、その内容を挙げ、何が問題だったのかを説明します。その経緯も原因の一部です。
6. 見つけた方法
簡潔に、デバッグの過程を記載します。
- 何によって再現が決定的になったのか。
- 何のツールが問題を解明したのか(デバッガー、ソースコードの追跡、設定値の列挙、コード内の計測など。
debug-mantraのステップ2の手順)。 - 試した仮説と、それぞれを棄却した理由を一言で記載します。(手掛かり記録から取り出してください。)
- 原因を確認できた唯一の実験。
このセクションは次にデバッグする人のためのものです。後から学べる内容にしてください。
7. なぜ見逃されたのか
このバグがブランチ/リリース/顧客にまで到達した原因です。実際の理由を選んでください。
- CI のギャップ(この経路/構成を実行するテストがありません)。
- 潜在的なコード(作成時には正しかったものの、後に別ファイルで行われた変更によって壊れました)。
- ワークロードのギャップ(今回まで、実際のワークロードがこのコードパスに到達しませんでした)。
- 以前の修正が不完全(防御的なチェックで症状を隠しただけで、根本原因は解消されていません)。
- レビューでの見落とし(変更自体はレビュー可能でしたが、その影響が見過ごされました)。
率直な答えが「妥当な理由はありません。検出すべきでした」であれば、そう書いてください。非難しないこと — 個人ではなく、ギャップについて説明してください。
8. 検証 (必須)
修正が機能することを確認した方法です。具体的に記載してください。
- 元の失敗テストが成功するようになりました(テスト名、リンク)。
- 顧客のワークロードが完了するようになりました(ワークロード識別子、実行リンク)。
- パフォーマンスのリグレッションが解消されました(修正前の数値、修正後の数値)。
- ストレス/ソーク/ファズテストが問題なく完了しました(期間、規模)。
- 影響を受ける他の構成/ワークロードもテストしました。
1 つの構成しか検証していない場合は、その旨を明記してください — 「Llama-2-70B/8 GPUs/DeepSpeed で検証済み。他のワークロードでは再テストしていません。」 実際より広い範囲を検証したかのように書かないでください。
9. アクションアイテム/フォローアップ
修正 PR 自体には含まれない、具体的な次のステップです。各項目に、内容、担当者、追跡用の成果物を記載してください。
- <seam> にリグレッションテストを追加。(担当者、テスト名。)
- この種のバグを防ぐためにリファクタリングする。(担当者、チケット。)
- CI のギャップを解消:<新しいチェック>。(担当者、PR。)
- ドキュメント/ランブックを更新。(担当者、リンク。)
- 気付いた <関連する問題> のチケットを起票。(担当者、キー。)
アクションアイテムがない場合は、「なし — 修正で十分であり、この種のバグに関するフォローアップは不要です。」 と書いてください。十分に見せるためにアクションアイテムを作らないでください。
トーン
エンジニア同士のやり取りです。management-talk とは異なります。
- コード識別子をそのまま扱います。
tadaLaunchPrepare、tada/prim.h::syncWaitPeer、scratchBuf、コミット SHA、行番号などは保持してください。将来のエンジニアが grep で変更箇所をたどれることが重要です。 - 説明よりメカニズムを重視します。 実際の原因の連鎖をたどってください。「同期の問題」と曖昧にせず、どの関数がどの条件のもとでどのイベントをスキップしたかを述べてください。
- 能動態、具体的な主語、短い段落を使います。 これはどこでも同じです。
- 曖昧な表現は使いません。 「私たちはそう考えています」/「〜のようです」/「〜かもしれません」は削除してください。断言するか、書かないでください。
- 非難しません。 バグ、ギャップ、修正について説明してください。「X が見つけるべきだった」とは決して書かないでください。CI のギャップが失敗の要因であり、個人ではありません。
- 主張を持ち込まないでください。 ポストモーテムには、起きたことと次の対応を記録します。リファクタリングを提案したい場合は、別の提案として作成し、アクションアイテムからリンクしてください。
出力の流れ
- 必須の入力が 4 つすべてそろっていることを確認します。 足りないものがあれば列挙して終了してください。下書きは作成しないでください。
- 掲載先を確認します(デフォルト:元チケットへの JIRA コメント)。その他の有効な掲載先は、PR の説明、
docs/postmortems/<ticket>.md、社内 wiki ページです。形式は同じで、変わるのは外側の書式だけです。 - 下書きを作成します。単一のチャットブロックにしてください。
- 投稿前に承認を得ます。 JIRA に投稿する場合は、正確な ADF ペイロードを提示し、「投稿してください」/「進めてください」/「はい」という明示的な返答を待ってから、
POST /rest/api/3/issue/<KEY>/commentを実行してください。出力のみの場合は承認不要です。 - management-talk への引き継ぎを提案します: 「リーダー向けのトーンにした版も必要ですか?
management-talkに引き継げます。」 自動で実行しないでください。
具体例 — dumbModel での Tada のハング(JIRA-12345)
概要。 Tada の単一ストリーム高速パスで、必要なストリーム間同期が省略されていたため、スクラッチバッファへの書き込みが可視になる前にカーネルが起動していました。LLM-7B のファインチューニングで dumbModel を使うと確実に発生し、評価のたびにワークロードが停止していました。危険な高速パスを削除し、デバイス側のチェックを強化して修正しました。JIRA-12345、PR org/platform#5751、担当者 Alex(Tada チーム)。
症状。 dumbModel による 8 GPU の LLM-7B ファインチューニングが、最初の評価ステップで無期限に停止しました。エラーもタイムアウトもなく、
tadaKernel_AllReduce_f32_RING内でビジーウェイトしていました。毎回再現しました。根本原因。
tadaLaunchPrepare/tadaLaunchKernel/tadaLaunchFinishの単一ストリーム高速パス(scheduler->numStreams == 1 && !plan->persistentが条件)では、launchStreamとhandle->shared->deviceStream間のイベント同期が省略されていました。dumbModel はこの条件に正確に合致します。deviceStream上での IPC 公開/スクラッチバッファへの書き込み(scratchBufを初期化する)がlaunchStreamから可視になる前に、カーネルが起動していました。カーネル内では、scratchBuf == NULL→ 不正なポインター参照 → リングの ready フラグをガベージメモリから読み取り → 到達することのない ready シグナルを待ってスレッドが無限にスピンする、という状態になっていました。症状が発生した理由。 停止は all-reduce のリング待機ループで起きており、これがコールスタック上で最後に見える処理です。しかし、実際のバグは数フレーム前の起動準備にあります。省略された同期は、ワークロードが特定の条件(単一ストリームかつ非永続)を満たすまで表面化しません。dumbModel の reduce-scatter パターンは、評価のたびにこの条件に合致します。
修正。 PR #5751 では、単一ストリーム高速パスを完全に削除しました(省略される安全策に比べ、性能上の利点はわずかでした)。また、参照前に
scratchBufが null でないことをデバイス側で確認するようにしました。これにより、同じ種類のバグが黙ってスピンするのではなく、明示的に失敗します。以前の試み(PR #5612)では、IPC 公開後にホスト側の防御的なチェックを追加して、一部の経路で症状を隠していましたが、根本にある競合状態は残っていました。この変更も元に戻しました。発見の経緯。 再現条件を「8 GPU の LLM-7B がときどき停止する」から、2 GPU のサブセットで評価ステップを 1 回に絞った再現可能な 30 秒のテストへと特定しました。最初の仮説は、
launchStream上でのカーネル起動順序でしたが、デバッガーで否定されました。カーネルは正しくキューに登録されていました。2 つ目の仮説は、スクラッチバッファの初期化競合でした。tadaLaunchPrepareに[DBG-7af3]の計測を追加し、scratchBufとdeviceStreamのイベント記録タイムスタンプを出力したところ、公開が完了する前に起動していることを確認しました。決め手となった実験は、numStreams = 2に強制したところバグが消えたことです。これにより、問題の条件を特定できました。見逃された理由。 潜在していたコードパスでした。単一ストリーム高速パスは 3 月に追加されましたが、dumbModel の経路は常に複数ストリームを使うという前提に基づいていました。当時、その前提は正しかったのです。5 月の dumbModel ランチャーの変更で、評価ステップを単一ストリームにまとめるようになり、その時点で条件に合致するようになりました。Tada の CI では、単一ストリーム+IPC+スクラッチバッファの組み合わせをテストしていませんでした。顧客のワークロードで初めて問題が発生しました。
検証。 元の LLM-7B/8 GPU/dumbModel のワークロードは、評価を最後まで問題なく完了するようになりました(2 時間の実行を 3 回連続で完了)。
tada-testsのall_reduce_perf回帰テストスイートも成功しました。8 GPU で 6 時間の連続稼働テストを行い、停止はありませんでした。他のモデルサイズや dumbModel 以外のワークロードでは再テストしていません。どちらも複数ストリーム経路を通り、影響を受けていなかったためです。対応項目。
- 回帰テストを追加しました:
tests/single_stream_ipc_publish_test.cppで、これまでテストされていなかった条件を検証します。(Alex、PR #5751 でマージ済み。)- CI の不足:夜間テストに単一ストリーム+IPC の組み合わせを追加します。(Alex、JIRA-12346。)
- ドキュメントの更新:Tada の起動高速パスの不変条件を
docs/launch_synchronization.mdに記載しました。(Alex、PR #5752。)- 関連項目:他の
numStreams == 1高速パスにも同じ種類のバグがないか監査します。(JIRA-12347 として起票済み。) このポストモーテムで、マネジメント向けの説明版になかったこと:
- すべてのコード識別子(
tadaLaunchPrepare、scratchBuf、numStreams、handle->shared->deviceStream)を明記しています。 - 原因の連鎖を最初から最後までたどり、読者が該当行を grep で探せるようにしています。
- 以前の修正の試み(PR #5612)と、何が問題だったのかを明記しています。
- 原因を特定した正確な実験(
numStreams = 2にすると問題が消えた)を記録しています。 - 検証範囲を正直に記載しています。「他のモデルサイズでは再テストしていない」というのは情報であり、欠落ではありません。
- アクションアイテムには担当者と追跡用の記録があります。
ルール
- 必須の入力が4つすべてそろっていない場合、作成を拒否してください。 仮説に基づくポストモーテムは、ポストモーテムがないよりも悪いものです。
- 根本原因、担当者、検証実行、アクションアイテムを決して捏造しないでください。 セクションに必要な事実がなければ、確認してください。もっともらしい文章で穴を埋めてはいけません。
- エンジニアリング記録からコード識別子を決して削除しないでください。 それらは索引です。リーダー向けに書き換えるのは
management-talkの役割であり、あなたの役割ではありません。 - 非難しないでください。 人ではなく、ギャップやバグについて説明してください。
- 検証範囲を正直に記載してください。 1つの構成しかテストしていない場合は、そのように書いてください。より広い範囲で検証したかのように示すことが、回帰を繰り返す原因になります。
- JIRA に投稿する前に承認を得てください。 印刷のみの出力には承認は不要です。このスキルから JIRA 以外の宛先に投稿してはいけません。
- 1回の修正は普通ですが、3回目は要注意です。 ユーザーが3回目の修正を求めている場合は、何のセクションに具体的な問題があるのか尋ねてください。何も考えずに修正を続けてはいけません。