skills/learn-pj-incident/SKILL.md
07-03 障害対応シミュレーション。Issue 起票 → 原因調査 → 修正 → Risk 登録 → 振り返りの全サイクルを体験する。
npx skillsauth add novel-jp/projsight-plugin learn-pj-incidentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
障害発生から解決・再発防止までのインシデント対応サイクルを体験する演習です。
所要時間: 実装パス 約 90 分 / シミュレーションパス 約 45 分
前提: /learn-pj-execute(07-02)完了済み(または進行中)
スキル対応: 統合演習(Spec Eng. + AI Collab 重点)
「障害対応シミュレーションです。
実際のプロジェクトでは、デプロイ後に予期しない問題が発生することがあります。
この演習では、障害発生から解決・再発防止までの全サイクルを ProjSight で管理しながら体験します。
使うスキル:
- Spec Eng. — Issue ライフサイクル管理(04-05)
- AI Collab — AI デバッグ(05-03)
- Intent Eng. — リスクの振り返りと登録(04-03)」
まず、受講者の状況を確認して 実装パス か シミュレーションパス かを決定する:
受講者に確認する:
「まず確認させてください。07-02 で実装したコードはありますか?
- ある場合 → そのコードに対する障害を体験します(実装パス)
- ない場合 → 提供するサンプル素材を使って演習します(シミュレーションパス)」
以下の3つのシナリオを受講者に提示し、取り組みたいものを選んでもらう:
「3つの障害シナリオを用意しています。どれに取り組みたいですか?
A. 入力エラー — 特定の入力でエラーが発生するとユーザーから報告(難易度: 低〜中)
B. CI 失敗 — CI が突然失敗し始めた。依存ライブラリのバージョン不整合の疑い(難易度: 中)
C. パフォーマンス劣化 — レスポンスが許容範囲を超えるようになった(難易度: 中〜高)
迷う場合のおすすめ: 初めての障害対応演習なら A、CI やビルド周りに興味があれば B、実務に近い体験をしたければ C がおすすめです。」
| シナリオ | 内容 | 難易度 | | -------- | ---------------------------------------------------------------- | ------ | | A | 特定の入力でエラーが発生するとユーザーから報告 | 低〜中 | | B | CI が突然失敗し始めた。依存ライブラリのバージョン不整合の疑い | 中 | | C | パフォーマンスが劣化し、レスポンスが許容範囲を超えるようになった | 中〜高 |
ベテラン向け追加チャレンジ: シナリオ A を選んだ場合、修正後に「同種のバグを防ぐテスト戦略」まで考えてみましょう(例: プロパティベーステスト、入力バリエーションの網羅方針)。
シミュレーションパスの場合、以下の素材を受講者に提示する。受講者はこの情報を構造化して Issue に記録し、AI にデバッグを依頼する練習を行う。
<details> <summary>シナリオ A: 入力エラー</summary>ユーザー報告: 「タスクのタイトルに絵文字を含めると保存時にエラーになります」
エラーログ:
2026-04-04T10:23:45.123Z ERROR [web-api] ValidationError: Invalid character in title field
at validateInput (/src/handlers/task.ts:42)
at createTask (/src/handlers/task.ts:18)
at Router.handle (/node_modules/express/lib/router.js:234)
Request body: { "title": "🚀 新機能テスト", "deliverableId": "del-001" }
Status: 400 Bad Request
問題のあるコード:
// src/handlers/task.ts
function validateInput(title: string): void {
const VALID_TITLE = /^[a-zA-Z0-9\s\-_]+$/;
if (!VALID_TITLE.test(title)) {
throw new ValidationError('Invalid character in title field');
}
}
</details>
<details>
<summary>シナリオ B: CI 失敗</summary>
状況: 昨日まで通っていた CI が今朝から失敗している。コードの変更はしていない。
CI ログ:
npm warn deprecated [email protected]: Please upgrade to v7 or higher
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR! react@"^19.0.0" from the root project
npm ERR! peer react@"^18.0.0" from [email protected]
npm ERR! node_modules/some-ui-lib
npm ERR! some-ui-lib@"^2.0.0" from the root project
npm ERR! Fix the upstream dependency conflict
問題のある package.json(抜粋):
{
"dependencies": {
"react": "^19.0.0",
"some-ui-lib": "^2.0.0"
}
}
</details>
<details>
<summary>シナリオ C: パフォーマンス劣化</summary>
監視アラート: 「API レスポンスタイム p95 が 5 秒を超過(閾値: 2 秒)」
パフォーマンスログ:
2026-04-04T09:00:12Z INFO [api] GET /api/tasks?projectId=proj-001 200 4823ms
2026-04-04T09:00:15Z INFO [api] GET /api/tasks?projectId=proj-001 200 5102ms
2026-04-04T09:01:22Z INFO [api] GET /api/tasks?projectId=proj-002 200 312ms
2026-04-04T09:02:45Z INFO [api] GET /api/tasks?projectId=proj-001 200 6201ms
2026-04-04T09:03:00Z WARN [db] DynamoDB consumed capacity: 847 RCU (proj-001)
2026-04-04T09:03:00Z WARN [db] DynamoDB consumed capacity: 12 RCU (proj-002)
問題のあるコード:
// src/handlers/listTasks.ts
async function listTasks(projectId: string) {
const allEntities = await db.query({
pk: `PROJECT#${projectId}`,
});
// フィルタリングをアプリ側で実行(SK条件なし)
return allEntities.filter((e) => e._type === 'task');
}
</details>
「以下の障害が発生したと仮定しましょう:
{選択したシナリオの詳細を提示。シミュレーションパスの場合はサンプル素材を含める}
さて、最初に何をしますか?」
受講者の対応を見守り、以下のフローに沿って誘導する(ただし最小限の介入に留める)。
初学者フォールバック: 受講者の回答が曖昧・的外れな場合、または受講者が詰まりを申告した場合、以下の段階的ヒントを出す:
- 「ヒント: 障害が起きたとき、まず何を記録に残すべきでしょうか?」
- それでも詰まる場合: 「選択肢で考えてみましょう。a) Issue を起票して問題を記録する b) すぐにコードを修正する c) 上司に報告する — どれが最初のステップとして適切ですか?」
- 回答後: 「そうですね。まず Issue として記録を残すことで、対応の追跡が可能になります。では起票してみましょう。」
「まず障害を Issue として記録しましょう。
04-05 で学んだように、upsert_issue で起票してください。
category、severity、description を適切に設定しましょう。」
受講者が upsert_issue で起票する。以下を含むよう誘導:
bug / tech-debt / limitation / documentation / other| category | 判断基準 | | ----------------- | ---------------------------------------------------------------------- | | bug | 意図した動作と異なる不具合。再現手順がある | | tech-debt | 動作はするが保守性・拡張性に問題がある。パフォーマンス劣化もここに含む | | limitation | 現在の設計上の制約。仕様通りだが将来的に問題になりうる | | documentation | ドキュメントの誤り・不足 | | other | 上記に当てはまらないもの |
| severity | レベル | 基準 | 例 | | -------- | --------- | ------------------------------------------ | -------------------------------------- | | 5 | very high | サービス全体が停止、またはデータ損失の恐れ | 全ユーザーがログインできない | | 4 | high | 主要機能が使えない | タスク作成が一切できない | | 3 | medium | 一部機能に障害があるが回避策がある | 特定の入力でエラーになるが他の入力は可 | | 2 | low | 軽微な不具合、表示崩れ等 | UIの文字が一部はみ出す | | 1 | very low | ほぼ影響なし | ログのフォーマットが不統一 |
呼び出し例(シナリオ A の場合):
upsert_issue(
projectId,
title: "タスクタイトルに絵文字を含めると保存時に 400 エラー",
category: "bug",
severity: 3,
description: "## 症状\nタスク作成時、タイトルに絵文字(例: 🚀)を含めると ValidationError が発生し保存できない。\n\n## 影響範囲\n- タスク作成・更新のタイトル入力\n- 絵文字を使わなければ回避可能\n\n## 再現手順\n1. タスク作成画面を開く\n2. タイトルに「🚀 新機能テスト」を入力\n3. 保存ボタンを押す → 400 Bad Request"
)
「次に原因を調査しましょう。
05-03 で学んだように、AI にデバッグを依頼する際は情報を構造化して渡してください:
- エラーメッセージ(あれば)
- 再現手順
- 期待される動作と実際の動作
- 最近の変更内容」
受講者と AI が協力して実際のコードの原因を特定し、修正を実装する。
まず、サンプル素材のログ情報を受講者に提示し、原因の仮説を立ててもらう:
「ログやエラーメッセージから、何が原因だと考えますか? まず仮説を立ててみてください。」
受講者が仮説を述べたら、問題のあるコードを開示して検証させる:
「では、該当するコードを見てみましょう。仮説と照らし合わせてみてください。
{問題のあるコードを提示}」
分析結果は Issue の description を更新して「原因分析」セクションを追記する。
重要:
upsert_issueの description は全文置換です。まず現在の Issue の description を確認し、その末尾に原因分析セクションを追記した全文を渡してください。既存の内容(症状・影響範囲・再現手順)が消えないよう注意しましょう。
upsert_issue(issueId, description: "(既存の description 全文をここにコピー)\n\n## 原因分析\n- 根本原因: ...\n- 影響範囲: ...\n- 修正方針: ...")
create_linkの方向ルール:sourceがtargetを[type]する関係です。 例: Task が Issue を resolves する →source=taskId, target=issueId
「原因が特定できたら、修正タスクを作成しましょう。
障害の修正にも専用タスクを作るのは、『何のために・いつ・誰が修正したか』を追跡可能にするためです。」
upsert_task で修正タスクを作成create_link(projectId, sourceType: 'task', sourceId: <taskId>, targetType: 'issue', targetId: <issueId>, linkType: 'resolves')
start_work(taskId) で修正に着手complete_work(taskId) で完了upsert_issue(status: 'resolved') で Issue をクローズ「修正が完了しました。最後に再発防止を考えましょう。
この障害はどうすれば防げましたか?
リスクとして登録し、今後の対策を明記しましょう。」
upsert_risk で再発防止リスクを登録(mitigation に具体的な対策を記述し、status: 'planned' を設定する)
upsert_risk(projectId, title: "入力バリデーション不備による障害再発", mitigation: "入力バリデーションのテストケースに Unicode・絵文字を含める", status: "planned")upsert_risk(projectId, title: "依存ライブラリの互換性破壊", mitigation: "package.json に依存ライブラリの互換バージョンを明記し、CI で依存解決チェックを追加する", status: "planned")upsert_risk(projectId, title: "DynamoDB フルスキャンによるパフォーマンス劣化", mitigation: "DynamoDB クエリに SK 条件を追加し、フィルタ式でのフルスキャンを防止する。p95 レスポンスタイムの監視アラートを設定する", status: "planned")create_link(projectId, sourceType: 'risk', sourceId: <riskId>, targetType: 'issue', targetId: <issueId>, linkType: 'caused_by')
source が target を [type] する関係です「障害対応シミュレーションお疲れさまでした!
インシデント対応のフロー:
1. Issue 起票 — 問題を正確に記録する(症状・影響・再現手順)
2. 原因調査 — AI に構造化された情報を渡してデバッグ
3. 修正タスク — Issue と紐づけて追跡可能にする
4. 振り返り — リスク登録で再発防止を仕組み化
ポイント:
- 障害対応中も ProjSight に記録を残すことで、後から振り返れる
- Issue → Task → Risk のリンクが『なぜこの修正が入ったか』の追跡を可能にする
- AI は原因調査の強力な助手だが、情報の構造化は人間の責務
次は /learn-pj-retrospect(07-04)でカリキュラム全体の振り返りを行いましょう。」
受講者のタスクを complete_work(taskId) で完了にする。
tools
タスクを作成・紐づけ・開始する。コーディング前に必ずこのコマンドを実行すること。
data-ai
AI ガイド付きリスクアセスメント。未対応リスクを1件ずつレビューし、選択肢・推奨・具体的対応内容を提示する。
tools
タスク記述の品質をレビューし、改善提案を出す。学習・本番共用の汎用スキル。
testing
DR(Decision Record)の品質をレビューし、改善提案を出す。学習・本番共用の汎用スキル。