diagnosing-bugs について
コーディングエージェント向けの難しいバグの診断手順です。仮説やコード調査に進む前に、速く決定的なフィードバックループを作り、再現の最小化と修正の検証を重視します。収集した証拠の秘密情報も伏せます。
主な機能
- 01症状に絞った合否ループの構築
- 02障害の再現と最小化
- 03反証可能な仮説の順位付け
- 04境界を絞った計測
- 05回帰テストの追加と修正確認
- 06証拠内の秘密情報のマスキング
活用シーン
- 01断続的な障害の調査
- 02複数の呼び出し元にまたがるバグの診断
- 03最小限の回帰テストの作成
- 04不足する再現証拠の依頼
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ leandrocfe/ skills --skill diagnosing-bugsインストール後、Claude Code で/diagnosing-bugsを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlscripts/hitl-loop.template.sh
詳細
バグの診断
難しいバグに取り組むための規律です。明確な理由がある場合に限り、フェーズを飛ばしてください。
コードベースを調べるときは、関連するモジュールについて明確なメンタルモデルを持てるよう、CONTEXT.md があれば読み、作業対象の領域にある ADR も確認してください。
表示する情報を編集する
このスキルでは、コマンド、出力、取得したアーティファクトを提示します。最初にすべてのシークレットを編集してください――その箇所には <REDACTED> と記入します。認証情報が表示内容ではなく環境内に留まるよう、環境変数を使ったループを構築してください。取得したアーティファクトには認証ヘッダーが含まれています。シグナルを示す行だけを引用してください。
編集後の出力だけではバグを診断できない場合は、そのことを伝えてユーザーに尋ねてください。
フェーズ 1 — フィードバックループを構築する
これがこのスキルの要です。 残りは機械的な作業です。バグに対する明確な合格/不合格シグナル――この特定のバグで確実に失敗するもの――があれば、原因を見つけられます。二分探索、仮説検証、計測はすべて、そのシグナルを利用するだけです。シグナルがなければ、どれだけコードを眺めても解決にはつながりません。
ここに偏った量の労力を注いでください。積極的に取り組んでください。創意工夫してください。諦めないでください。
構築方法 — おおむね次の順に試してください
- バグに到達するシームでの失敗するテスト — unit、integration、e2e。
- 起動中の開発サーバーに対する Curl/HTTP スクリプト。
- 入力フィクスチャを使った CLI の呼び出し。stdout を正常動作が確認されたスナップショットと差分比較します。
- ヘッドレスブラウザーのスクリプト(Playwright/Puppeteer)— UI を操作し、DOM/console/network を検証します。
- 取得したトレースの再生。 実際のネットワークリクエスト/ペイロード/イベントログをディスクに保存し、コードパスに通して単独で再生します。
- 使い捨てのハーネス。 システムの最小限のサブセット(1 つのサービス、モック化した依存関係)を起動し、1 回の関数呼び出しでバグのコードパスを実行します。
- プロパティ/ファズのループ。 バグが「出力がときどき間違う」なら、ランダムな入力を 1000 件実行し、失敗パターンを探します。
- 二分探索のハーネス。 バグが 2 つの既知の状態(コミット、データセット、バージョン)の間で発生した場合、「状態 X で起動してチェックする」を自動化し、
git bisect runを使えるようにします。 - 差分ループ。 同じ入力を旧バージョンと新バージョン(または 2 つの設定)で実行し、出力を差分比較します。
- Bash の HITL スクリプト。 最後の手段です。人間がクリックする必要がある場合は、
scripts/hitl-loop.template.shでその人を操作し、ループの構造化を保ちます。取得した出力はあなたに戻されます。
適切なフィードバックループを構築すれば、バグは 90% 解決したも同然です。
ループを絞り込む
ループを 1 つのプロダクトとして扱ってください。ループを1 つ作ったら、さらに絞り込みます。
- もっと速くできますか?(セットアップをキャッシュする、無関係な初期化を省く、テスト範囲を絞る。)
- シグナルをもっと明確にできますか?(「クラッシュしなかった」ではなく、特定の症状を検証する。)
- もっと決定論的にできますか?(時刻を固定する、乱数シードを指定する、ファイルシステムを隔離する、ネットワークを固定する。)
不安定な 30 秒のループは、ないのと大差ありません。決定論的な 2 秒のループは強力です――デバッグの超能力になります。
非決定論的なバグ
目標はきれいな再現ではなく、再現率を上げることです。トリガーを 100× 実行し、並列化し、負荷を加え、タイミングの間隔を狭め、sleep を挿入します。50% の確率で発生するバグならデバッグできますが、1% では困難です――デバッグできるようになるまで再現率を上げ続けてください。
本当にループを構築できない場合
作業を止め、明示的にそのことを伝えてください。試したことを列挙してください。ユーザーに次のいずれかを依頼してください。(a)再現する環境へのアクセス、(b)編集済みの取得アーティファクト(HAR file、ログダンプ、コアダンプ、タイムスタンプ付きの画面録画)、または(c)本番環境に一時的な計測を追加する許可。ループなしに仮説を立てて先へ進んではいけません。
完了基準 — 赤くなるタイトなループ
フェーズ 1 は、ループがタイトで失敗を検出できる状態になったら完了です。すでに少なくとも一度実行したコマンドを1 つ挙げられる状態にしてください。たとえば、スクリプトのパス、テストの呼び出し、curl などです(呼び出し方法とその出力を、機密情報を伏せて提示してください)。そのコマンドは、次の条件を満たす必要があります。
- 失敗を検出できる — バグの実際のコードパスを実行し、ユーザーに現れている正確な症状を検証するため、このバグがある状態では失敗し、修正後は成功します。「エラーなく実行できる」だけでは不十分です。_この特定のバグを検出できる_必要があります。
- 決定論的 — 実行するたびに同じ判定になります(不安定なバグの場合は、前述のとおり、高い再現率で固定できていること)。
- 高速 — 数分ではなく、数秒で終わります。
- エージェントが実行できる — 監視なしで実行できます。
scripts/hitl-loop.template.shを介して人間が関与する場合を除きます。
このコマンドが存在する前に、仮説を立てるためコードを読み始めていることに気づいたら、そこで止めてください。仮説に飛びつくことこそ、このスキルが防ぐ失敗です。 失敗を検出できるコマンドがなければ、フェーズ 2 には進めません。
フェーズ 2 — 再現して最小化する
ループを実行します。失敗することを確認してください。バグが現れます。
次の点を確認します。
- ループが、ユーザーの説明した障害モードを再現していること。近くで発生する別の障害ではありません。間違ったバグを修正すれば、修正も間違ったものになります。
- 複数回の実行で障害が再現すること(非決定的なバグの場合は、デバッグに十分な高い頻度で再現すること)。
- 正確な症状(エラーメッセージ、誤った出力、遅い処理時間)を記録していること。これにより、後のフェーズで修正が本当に症状を解消したか検証できます。
最小化する
失敗を確認したら、それでも失敗する最小のシナリオまで再現条件を絞り込みます。入力、呼び出し元、設定、データ、手順を一度に 1 つずつ削り、削るたびにループを再実行してください。障害に不可欠なものだけを残します。
これが有効な理由:最小限の再現条件は、フェーズ 3 で検討する仮説の範囲を狭め(疑うべき変動要素が減り)、フェーズ 5 で使える明確なリグレッションテストになります。
残った要素がすべて不可欠であれば完了です。どれかを取り除くと、ループは成功します。
再現して最小化するまで、先へ進まないでください。
フェーズ 3 — 仮説を立てる
検証に入る前に、優先順位を付けた 3~5 個の仮説を挙げます。仮説を 1 つだけ立てると、最初のもっともらしい案に固執してしまいます。
各仮説は反証可能である必要があります。つまり、仮説から導かれる予測を明示してください。
形式:「 が原因なら、 を変更するとバグが消える/ を変更すると悪化する。」
予測を明示できない場合、その仮説は単なる感覚です。破棄するか、具体化してください。
検証に入る前に、優先順位付きのリストをユーザーに提示してください。 ユーザーは、優先順位をすぐに変更できる専門知識を持っていることがよくあります(「#3 に変更をデプロイしたばかりです」など)。あるいは、すでに除外された仮説を知っている場合もあります。これは手軽なチェックポイントであり、大幅な時間の節約になります。このために作業を止めないでください。ユーザーが AFK なら、自分で付けた優先順位に従って進めます。
フェーズ 4 — 計測する
各プローブは、フェーズ 3 の特定の予測に対応させます。変更する変数は一度に 1 つだけです。
ツールの優先順位:
- 環境が対応している場合は、デバッガー/REPL での調査を使います。ブレークポイント 1 つは、ログ 10 個に勝ります。
- 仮説を区別できる境界に、対象を絞ったログを追加します。
- 「すべてをログに出して grep」は決して行いません。
すべてのデバッグログに、[DEBUG-a4f2] のような一意のプレフィックスを付けてください。最後のクリーンアップは、grep 1 回で済みます。タグのないログは残り、タグ付きログは削除できます。
パフォーマンスの分岐。 パフォーマンスのリグレッションでは、通常、ログは適切ではありません。代わりに、計測のベースライン(タイミング用ハーネス、performance.now()、プロファイラー、クエリプランなど)を確立してから、二分探索します。まず計測し、その後で修正します。
フェーズ 5 — 修正 + 回帰テスト
適切な seam がある場合に限り、修正する前に回帰テストを書きます。
適切な seam とは、呼び出し元で発生する実際のバグのパターンをテストで実行できる場所です。利用できる seam があまりに浅い場合(バグの解決に複数の呼び出し元が必要なのに、呼び出し元が 1 つだけのテストになっている場合や、バグを引き起こした一連の流れを再現できないユニットテストなど)、そこで回帰テストを行っても誤った安心感を与えます。
適切な seam が存在しない場合、それ自体が発見事項です。 その点を記録してください。コードベースのアーキテクチャによって、バグを再発しないように防ぐことができません。そのことを次のフェーズに引き継いでください。
適切な seam が存在する場合:
- 最小化した再現ケースを、その seam で失敗するテストにします。
- 失敗することを確認します。
- 修正を適用します。
- テストが通ることを確認します。
- 元のシナリオ(最小化していないもの)に対して、フェーズ 1 のフィードバックループを再実行します。
フェーズ 6 — クリーンアップ + 事後検証
完了を宣言する前に、以下を必ず実施します。
- 元の再現ケースでバグが再現しない(フェーズ 1 のフィードバックループを再実行)
- 回帰テストが通る(または seam が存在しないことが記録されている)
- すべての
[DEBUG-...]計測コードを削除済み(プレフィックスをgrepで検索) - 使い捨てのプロトタイプを削除済み(または、デバッグ用と明確に示した場所に移動済み)
- 正しいと判明した仮説をコミットメッセージまたは PR に記載済み — 次のデバッガーが学べるように
その後、何があればこのバグを防げたかを考えてください。 テストに適した seam がない、呼び出し元が複雑に絡み合っている、隠れた結合があるなど、アーキテクチャの変更が答えに含まれる場合は、詳細を添えて /improve-codebase-architecture スキルに引き継いでください。推奨は修正を終えてから行ってください。作業を始めた時点よりも、今のほうが多くの情報を得ているためです。