debug-investigator について
debug-investigator は、原因を特定しにくい不具合を調べる開発者向けの体系的なデバッグスキルです。再現可能なフィードバックループ、証拠に基づく仮説、回帰確認で調査を進めます。
主な機能
- 01症状と環境の記録
- 02再現可能なフィードバックループ
- 03順位付けした仮説の検証
- 04Git bisect と計測ポイント
- 05回帰テスト境界の検証
活用シーン
- 01断続的な障害の調査
- 02明確なスタックトレースがない不具合の追跡
- 03性能劣化の分析
- 04不具合を導入した変更の特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mathews-tom/ armory --skill debug-investigatorインストール後、Claude Code で/debug-investigatorを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.mdLICENSEevals/cases.yamlreferences/bisection-guide.mdhypothesis-templates.mdinstrumentation-points.mdlog-analysis.mdstacktrace-patterns.mdreferences/upstream/provenance.md詳細
デバッグ調査担当者
場当たり的な調査を、仮説に基づく調査に置き換える体系的なデバッグ手法です。症状を記録し、決定論的なフィードバックループを構築して、証拠(スタックトレース、ログ、状態)を分析し、優先順位を付けた仮説を生成し、二分探索の戦略を設計し、計測ポイントを特定して、最小限の再現ケースを作成します。行き止まりを再び調査することがないよう、すべての手順を記録します。
このスキルと標準のデバッグ機能の使い分け: 基盤モデルは、明確なスタックトレースや明白なエラーなど、単純なデバッグに標準で対応できます。断続的な失敗、明確なスタックトレースがないバグ、パフォーマンスの退行、または git の二分探索や仮説の優先順位付けを要する問題など、明白でないバグの体系的な調査には、このスキルを使用してください。
参照ファイル
| ファイル | 内容 | 読み込むタイミング |
|---|---|---|
references/stacktrace-patterns.md | 例外の分類、トレースバックの読み方、Python/JS でよく見られるエラーの特徴 | スタックトレースまたは例外がある場合 |
references/hypothesis-templates.md | バグカテゴリの一覧、確率による順位付け、確認・反証テスト | 常に |
references/bisection-guide.md | git bisect のワークフロー、二分探索によるデバッグ、絞り込み手法 | 変更後にバグが発生した場合 |
references/log-analysis.md | ログパターンの抽出、異常検出、タイムラインの相関分析 | ログ出力を利用できる場合 |
references/instrumentation-points.md | 戦略的なログ配置、ブレークポイント戦略、状態の確認手法 | 調査計画が必要な場合 |
references/upstream/provenance.md | ソースの固定、適応範囲、MIT 通知、手動での再同期方針 | 帰属・ライセンスの確認、または意図的な上流再同期の場合 |
前提条件
- git — 二分探索と履歴の分析に使用します
- ソースコードへのアクセス — 動作を確認できないバイナリはデバッグできません
- 再現可能な環境 — 最低限、エラー出力(スタックトレース、ログ)が必要です
実行するコマンドを選ぶ前に、プロジェクトのスクリプト、テスト設定、インストール済みツールのヘルプを調べて、再現手順とテストコマンドを確認してください。その情報源と関連する環境設定を記録してください。タスクで許可されたチェックと変更のみを実行してください。アクセス権、ツール、または実行権限が不足している場合は、該当する証拠を未観測として記録し、独立して進められる分析を完了してください。
プロジェクトコンテキスト
詳細な調査を始める前に、リポジトリ内のエージェント向けコンテキストを確認してください。
docs/agents/domain.mdでCONTEXT.md、CONTEXT-MAP.md、ADR の参照ルールを確認しますCONTEXT.mdまたは関連するコンテキスト内の用語集で、ドメインの用語を確認しますdocs/adr/とコンテキスト内の ADR で、障害が発生している領域に関する決定を確認します
仮説、再現ケース名、再発防止の推奨事項には、プロジェクトの用語集を使用してください。これらのファイルがリポジトリにない場合は、そのまま通常どおり進めてください。コンテキストの整備を理由にデバッグを中断しないでください。
ワークフロー
フェーズ 1:症状の記録
コードに手を加える前に、観察できる問題を記録します。
- 何が起きていますか? — 観察された動作を正確に説明します。「クラッシュする」だけでは不十分です。「有効なセッショントークンで
get_current_user()を呼び出すと、auth.pyの 42 行目でKeyError('user_id')が発生する」のように、対応に役立つ具体的な情報を記載します。 - 本来はどう動作すべきですか? — 期待される動作を定義します。不明な場合は、その旨を記載します。
- 再現性 — 常に発生しますか、断続的に発生しますか(頻度も記載してください)、それとも一度だけですか? 断続的なバグには、決定論的なバグとは異なる戦略が必要です。
- 発生時期 — いつから発生し始めましたか? 最近の変更と照らし合わせます:
git log --oneline -20。特定のコミットの後にバグが発生した場合は、二分探索が最短の解決策です。 - 環境 — Python のバージョン、OS、依存関係のバージョン、正常に動作する環境と問題のある環境の設定の違い。
後で作成する最小化した再現ケースとは別に、元の入力、セットアップ、呼び出し順序、期待される結果を保持してください。これらは、最後に元のシナリオを確認するために必要です。
フェーズ 2:フィードバックループの構築
仮説の順位付けや本番コードの変更を始める前に、報告されたバグに対する高速で決定論的な合否判定手段を作成します。このループでは、ユーザーが報告した症状を再現する必要があります。近似した別の問題ではいけません。
次の手段を順番に試します。
- 関連する呼び出し元同士の相互作用も含め、バグのパターン全体に到達する最小のパブリックインターフェースでの失敗するテスト。
- フィクスチャ入力と出力のアサーションを使った、CLI またはスクリプトの実行。
- ローカルサーバーに対する Curl または HTTP リクエストと、レスポンス、ログ、または状態のアサーション。
- DOM、コンソール、ネットワークのアサーションを使った、UI バグのブラウザー自動化。
- 実際のコードパスを通したトレース、イベントペイロード、HAR、またはログのフィクスチャの再生。
- 該当するパスをトリガーするために必要な最小限のサブシステムを起動する、一時的なハーネス。
- 断続的な失敗に対するプロパティベース、ファジング、またはストレステストのループ。
- 既知の正常なリビジョンと問題のあるリビジョンの間でバグが発生した場合の、
git bisect runハーネス。
先に進む前に、ループを改善します。
- セットアップを絞り込み、コストの高いフィクスチャをキャッシュして高速化します。
- 症状そのものを正確にアサートして、判定を明確にします。
- 時刻、シード、ファイルシステムのパス、ネットワークアクセスを固定し、より決定論的にします。
- 断続的なバグでは、失敗がデバッグに十分な頻度で発生するまで、実行の繰り返し、並行処理、ストレステスト、タイミングプローブなどで再現率を高めます。
信頼できるループを構築できない場合は、そこで停止し、試したことを説明します。不足している成果物を要求します。たとえば、環境へのアクセス、キャプチャしたペイロード、ログ、タイムスタンプ付きの画面録画、または一時的な計測の許可などです。推測に基づく修正を進めてはいけません。
フェーズ 3:証拠の分析
仮説を立てる前に、利用可能な証拠をすべて調べます。
-
スタックトレースの解釈 — トレースバックがある場合は、下から上へ読みます。最後のフレームはエラーが表面化した箇所ですが、原因は数フレーム前にあることがよくあります。次の点を特定します。
- 例外の種類とメッセージ
- エラーの原因が発生したフレームと、エラーが送出されたフレーム
- よく見られるパターン(
references/stacktrace-patterns.mdを参照)
-
ログパターンの抽出 — 次の内容をログから検索します。
- 時系列の異常(順序が逆のタイムスタンプ、空白期間)
- 繰り返されるエラー(同じエラーが集中して発生している)
- 完了しなかった状態遷移
- 外部イベント(デプロイ、設定変更)との相関
-
状態の調査 — システムが稼働中の場合は、次の内容を調査します。
- 失敗箇所での変数の値
- データベースの状態(欠落した行、予期しない値)
- 設定値(環境変数、設定ファイル)
- 外部依存関係の状態(API の可用性、DB 接続)
-
コード差分の分析 — 最近発生したバグの場合は、次の点を確認します。
git diff HEAD~5— 何が変わりましたか?- エラーの呼び出しチェーンにある変更対象のファイルに注目します。
- 誤記、変数名の誤り、null チェックの欠落がないか確認します。
フェーズ 4:仮説の生成
優先順位を付けた仮説を生成します。仮説なしに修正を始めてはいけません。
-
仮説を 3~5 個、可能性の高い順に列挙します。 各仮説には次の内容を含めます。
- 何が問題なのかについての具体的な主張
- それを裏付ける証拠
- それを確認するために実行できるテスト
- それを否定する証拠
-
次の基準で可能性の高い順に並べます。
- 最近の変更との近さ(バグの多くは新しいコードにあります)
- 単純さ(競合状態よりもタイプミスを優先します)
- 証拠との整合性(仮説ですべての症状を説明できるか)
-
よくあるバグのカテゴリ(
references/hypothesis-templates.mdを参照):- 状態バグ:値の誤り、初期化の欠落、古いキャッシュ
- ロジックバグ:オフバイワン、演算子の誤り、条件の反転
- 統合バグ:API 契約の不一致、シリアル化エラー
- 並行処理バグ:競合状態、デッドロック、リソースの枯渇
- 環境バグ:依存関係の欠落、設定の誤り、バージョンの不一致
フェーズ 5:調査計画
各仮説を検証するための具体的な手順を立てます。
- まず H1 をテストします — 常に最も可能性の高い仮説からテストします。それを確認または否定できる単一の操作を考案します。
- 二分探索 — 変更後にバグが発生し、H1 が否定された場合:
- 正常なコミットと問題のあるコミットを特定します
git bisect start <bad> <good>を実行します- 各コミットで実行するテストコマンドを定義します
- ワークフローについては
references/bisection-guide.mdを参照してください
- 切り分け — 変数を一度に 1 つずつ取り除きます。
- 入力データを簡素化します
- 機能やプラグインを無効にします
- 外部呼び出しをハードコードした値に置き換えます
- クリーンな環境で実行します
- 計測 — 対象を絞ってログやブレークポイントを追加します。
- 呼び出しチェーン内の関数の開始点と終了点
- 状態を変更する前と後
- 判断を行う箇所(if/else 分岐)
references/instrumentation-points.mdを参照してください
フェーズ 6:実行
証拠が得られるたびに仮説を更新しながら、調査計画を実行します。
- 一度に 1 つの変数だけをテストします — 複数の項目を同時に変更すると、結果を解釈できなくなります。
- 結果を記録します — 否定的な結果も含め、各テストで判明したことを記録します。行き止まりを記録しておくと、失敗した経路を再び調べずに済みます。
- 可能性を更新します — テストのたびに、仮説の順位を付け直します。H1 が否定された場合は、H2 を新たな優先事項にします。
- エスカレーションのタイミングを見極めます — すべての仮説を検証し終えた場合、バグはまだ検討していないカテゴリに属しています。いったん立ち止まり、前提を再検討します。
フェーズ 7:修正と回帰の検証
証拠から原因を特定したら、修正する前に回帰テストの境界を選びます。
- 実際のインタラクションをたどります。 影響を受ける呼び出し元、共有状態または依存関係、および不具合を引き起こす順序や並行処理を記録します。最も近くにある呼び出し可能な関数だけでなく、実際の実装を通してそのインタラクションを実行する継ぎ目を選びます。たとえば、プレビューと送信の呼び出し元が不完全なキーを持つキャッシュを共有している場合は、両方の呼び出し元を同じキャッシュに対して実行し、それぞれの呼び出し元で正しい結果が得られることを検証します。プレビューだけを新しいキャッシュでテストしても、共有キャッシュの不具合がカバーされることは証明できません。
- 候補となる継ぎ目を評価します。 各候補が対象の呼び出し元、状態、シーケンスを維持しているか確認します。単一の呼び出し元だけを扱う浅いテストや、不具合のある境界を置き換えるモックは除外します。どんな犠牲を払っても最小のテストにするのではなく、実際のパターンを再現できる最小の継ぎ目を維持します。
- 適切な継ぎ目がある場合は、失敗後に成功することを証明します。 実装を変更する前に、最小化した再現ケースを回帰テストにします。不具合のあるソースでテストを実行し、実際の失敗を記録します。期待値と実際の値、または状態の差異によって、この不具合が特定できる必要があります。インポートエラー、セットアップの失敗、任意の例外、テスト名、またはテストを実行したという事実だけでは、失敗の証拠になりません。原因となる境界を修正し、同じ不具合固有のアサーションを再実行して、観察された成功を記録します。不具合と、問題のない動作に対する広範な回避策とを区別するために必要な動作を含め、影響を受けるすべての呼び出し元をテストします。
- 適切な継ぎ目がない場合は、その調査結果を報告します。 試した候補となる継ぎ目、そのそれぞれから到達できないインタラクション、およびアーキテクチャ上またはアクセス上の制約を列挙します。利用可能な継ぎ目がないアーキテクチャと、ツール、フィクスチャ、権限、または環境へのアクセス不足で実行が妨げられている継ぎ目を区別します。浅い回帰テストをでっち上げたり、回帰が防止されると主張したりしてはいけません。信頼できるフェーズ 2 のループがあり、原因が特定されている場合は、そのループを通じて修正を評価できます。それでも、回帰テストの継ぎ目がないことは明示的な制約として扱い、推測に基づく成功を正当化する理由にしてはいけません。
条件付きの強制的な失敗確認: 修正前に自然に失敗する場合は、ミューテーションは不要です。テストの感度を評価するために意図的に不具合を再導入したり、フィクスチャを変更したりする場合は、実際の現在のソースまたはフィクスチャの変更前のコピーを、無関係な編集も含めて保持します。テスト結果を解釈する前に、変更した内容をそのコピーと比較し、意図して変更した行とテストした経路を特定します。置換の対象がない変更、使用されていないファイルの変更、または無関係な差分は、テストが成功したか失敗したかにかかわらず、有効なミューテーションの証拠ではありません。修正を復元して比較を確認してから再試行するか、感度が証明されていないと報告します。有効なミューテーションでは、構文エラーやセットアップエラーではなく、不具合固有のアサーションで失敗する必要があります。中断や失敗が発生した場合も含め、ミューテーション後は変更前の正確な内容に戻します。保持したコピーと比較し、復元したテストを再実行します。復元または実行が妨げられた場合は、その内容を明示的に報告します。ミューテーションは、修正前に自然に失敗し、修正後に成功することの証拠を補足するものであり、代わりにはなりません。すべての修正にミューテーションを要求してはいけません。
フェーズ 8:元のシナリオ、クリーンアップ、解決
バグが解決したと宣言する前に、次の手順を実施してください。
- 元のシナリオ — 保存済みの未最小化のフェーズ 2 のシナリオを、元の呼び出し順序と関連する環境で再実行し、フェーズ 1 で想定した結果を確認します。最小化したテストや単体テストが合格しただけでは不十分です。これを実行できない場合は、不足している前提条件を明記し、元のシナリオでの解決は未検証としてください。
- クリーンアップ — 一時的な診断ログ、ブレークポイント、プローブ、使い捨てのテストハーネスを削除します。変更したパスを調べ、使用した計測用識別子を確認してください。無関係なものやユーザーが設定した計測は保持します。恒久的な回帰テストと必要なフィクスチャは残します。クリーンアップ後、一時的な計測が合格の理由にならないよう、最終ソースに対して回帰テスト(該当する接点がある場合)と元のシナリオを再実行します。
- 解決の証拠 — 確認済みの原因、原因に対処する修正、回帰テストの対象範囲、観測された失敗/成功の結果(または接点がない/ブロックされたことの明示的な記録)、元のシナリオの結果、クリーンアップと復元の状況を記載します。実行した証拠と、提案したコマンドや推定した結果は分けてください。未実行または無効なチェックを成功として報告してはいけません。
- 再発防止と教訓 — 再発がどのようにカバーされるか、またはどのようなアーキテクチャ上の制約でカバーできないのかを説明し、この特定のバグ以外から得られた教訓も記載します。
出力形式
## Debug Investigation: {Brief Description}
### Symptom
**Observed:** {What is happening — precise description}
**Expected:** {What should happen}
**Reproducibility:** {Always | Intermittent (~N% of attempts) | Once}
**First noticed:** {Date/time or triggering event}
**Environment:** {Relevant versions and configuration}
### Evidence Analysis
#### Stacktrace
- **Exception:** {type}: {message}
- **Origin:** {file}:{line} in {function}
- **Call chain:** {caller} → {caller} → {failure point}
- **Key insight:** {What the traceback reveals about the cause}
#### Logs
- **Anomaly:** {What is unusual}
- **Timeline:** {When the anomaly started}
- **Correlation:** {Related events}
#### Code Changes
- **Recent commits:** {relevant commits since last known-good state}
- **Files in error path:** {which changed files appear in the traceback}
### Hypotheses
| # | Hypothesis | Likelihood | Confirming Test | Refuting Test |
|---|------------|------------|-----------------|---------------|
| H1 | {Specific claim} | High | {What to check} | {What would disprove} |
| H2 | {Specific claim} | Medium | {What to check} | {What would disprove} |
| H3 | {Specific claim} | Low | {What to check} | {What would disprove} |
### Investigation Plan
#### Step 1: Test H1 — {action}
- **Command/action:** {specific step}
- **If confirmed:** {next action — fix}
- **If refuted:** proceed to Step 2
#### Step 2: Bisection
- **Good commit:** {hash}
- **Bad commit:** {hash}
- **Test:** {command to verify each commit}
- **Command:** `git bisect start {bad} {good}`
#### Step 3: Isolation
- **Remove:** {variable to eliminate}
- **Expected change:** {what should happen}
### Instrumentation Points
1. {file}:{line} — log {variable/state} to observe {what}
2. {file}:{line} — breakpoint to inspect {what}
### Minimal Reproduction
```{language}
# Minimal code that triggers the bug
{code}
```
### Resolution
**Root cause:** {What was wrong}
**Fix:** {What was changed — file:line, diff summary}
**Regression boundary:** {Callers, shared state, trigger sequence, and why this seam reaches them; or attempted seams and explicit limitation}
**Red / green:** {Observed defect-specific pre-fix failure and post-fix result; or blocked/unobserved}
**Forced mutation, if used:** {Pristine comparison, exercised change, assertion outcome, restoration comparison and restored result}
**Original scenario:** {Unminimized invocation, expected/actual outcome on final source; or missing prerequisite}
**Cleanup:** {Temporary instrumentation/harness removal and final-source rerun results}
**Prevention:** {Permanent regression coverage or documented no-seam limit}
**Lessons:** {What generalizes beyond this bug}
スコープの設定
| モード | スコープ | 深さ | 使用する場面 |
|---|---|---|---|
quick | 単一のエラー | H1 のテスト+修正 | スタックトレースが明確で、原因が明白な場合 |
standard | 調査全体 | 3 つの仮説+二分探索の計画 | 原因が明白でないバグのデフォルト |
deep | システム全体の分析 | 5 つ以上の仮説+計測+再現 | 間欠的なバグ、スタックトレースがない場合、本番環境の問題 |
調整ルール
- コードを変更する前に仮説を立てます。 少なくとも 1 つの明示的な仮説なしに、コードの変更を始めてはいけません。「試してみましょう」はデバッグではなく、当てずっぽうです。
- 一度に変更する変数は 1 つだけです。 調査の各ステップでは、変更する項目を厳密に 1 つにします。2 つ変更してバグが消えた場合、どちらが修正したのか分かりません。
- 行き止まりを記録します。 失敗した仮説にも価値があります。探索範囲を絞り込めるためです。何をテストし、何が分かったかを記録してください。
- 最も単純な説明から検証します。 競合状態、コンパイラのバグ、宇宙線を検討する前に、タイプミス、誤った変数名、インポートの不足をテストします。
- 仮説を立てる前にフィードバックループを確立します。 制御された合否シグナルでバグを再現できない場合、どの修正も推測にすぎません。まずフィードバックループの整備に取り組みます。
- 症状ではなく根本原因に対処します。 症状(null チェックの追加)に対処するだけで、根本原因(なぜ null だったのか)を理解しない修正では、本当のバグが残ります。
エラー処理
| 問題 | 解決策 |
|---|---|
| スタックトレースを利用できない | ログ分析と状態の調査に集中します。計測を使って診断出力を生成します。 |
| バグが間欠的に発生する | 重要な判断箇所に永続的なログを追加します。ストレス条件(高負荷、同時リクエスト)の下で実行し、再現率を高めます。 |
| ローカルで再現できない | バージョン、設定、データ、タイミングなど、環境を体系的に比較します。本番環境を再現するために docker または VM を使用します。 |
| 複数の仮説が同程度に有力 | 仮説を区別できる単一のテストを設計します。「X なら H1、Y なら H2」という二択の判断にします。 |
| 修正を試したがバグが残る | 仮説が誤っていました。修正を元に戻し、仮説の優先順位を更新して、次の仮説に進みます。修正を積み重ねてはいけません。 |
| 依存関係にバグがある | 依存関係のみを使用する最小限の再現例で確認します。課題トラッカーを確認します。上流の修正を待つ間は、最後に正常だったバージョンに固定します。 |
調査すべきでない場合
次の場合は、調査を行わないよう提案してください。
- エラーメッセージにすでに解決方法が記載されている場合(「モジュール X が見つかりません」→ X をインストールする)
- 問題が既知の環境設定の問題である場合(Python のバージョンが違う、環境変数が不足しているなど)
- 「バグ」が実際には機能リクエストや設計上の意見の相違である場合 — ADR またはディスカッションに案内してください
- コードがユーザーの管理下にない場合(サードパーティの SaaS、マネージドサービスなど)— 代わりにサポートチケットを起票してください
- ユーザーが生成されたコードや圧縮されたコードのデバッグを求めている場合 — 出力ではなく、ソースをデバッグしてください