mcpField
D
diagnosing-bugs

diagnosing-bugs

github.com/hu3rror開発051 回インストール1 日前に更新
リポジトリ

diagnosing-bugs について

コーディングエージェント向けの体系的なデバッグスキルです。仮説を立てる前に、高速で再現性があり、障害を検出できるフィードバックループを作ります。再現、診断、回帰テスト、後片付けまで扱います。

主な機能

  1. 01障害を検出するループの構築
  2. 02障害の再現と最小化
  3. 03反証可能な仮説の順位付け
  4. 04的を絞った調査プローブ
  5. 05回帰テストと後片付け

活用シーン

  1. 01断続的な障害の調査
  2. 02バージョン間の回帰追跡
  3. 03システム境界をまたぐ不具合の診断

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/hu3rror/my-skills --skill diagnosing-bugs

インストール後、Claude Code で/diagnosing-bugsを入力して読み込まれたか確認します。

同梱ファイルagents/openai.yamlscripts/hitl-loop.template.sh

詳細

バグの診断

難解なバグに取り組むための手法です。明確な理由がある場合に限り、フェーズを省略してください。

コードベースを調査するときは、関連モジュールを把握するためにGLOSSARY.md(存在する場合)を読み、作業対象の領域にあるADRも確認してください。

バグの手がかりが過去のpiセッションにまでさかのぼる場合(以前の実行で発生した場合や、実行間で動作が変わった場合)は、場当たり的なgrep/pythonではなく、pi-session-searchスキルを使って検索してください。このスキルはUTF-8に対応し、JSONLの構造を把握しており、出力にも上限を設けています。

マスキング

このスキルでは、コマンド、出力、取得したアーティファクトを提示します。必ず先にすべてのシークレットをマスキングしてください。該当箇所は<REDACTED>に置き換えます。認証情報が提示内容に含まれず環境内にとどまるよう、環境変数を使ったループを作成してください。取得したアーティファクトには認証ヘッダーが含まれているため、問題の手がかりとなる行だけを引用してください。

マスキング後の出力だけではバグを診断できない場合は、その旨を伝え、ユーザーに確認してください。

フェーズ1:フィードバックループを構築する

これがこのスキルの本質です。 それ以外はすべて機械的な作業です。バグを確実に検出する鋭い合否判定(このバグで確実に失敗するもの)があれば、原因を見つけられます。二分探索、仮説検証、計測はすべて、この判定を利用するだけです。判定がなければ、コードをいくら見つめても解決にはつながりません。

ここには、それに見合うだけの労力をかけてください。積極的に、創造的に取り組み、簡単にあきらめないでください。

おおよそ優先度の高い順に、構築方法を示します
  1. 失敗するテスト:バグに到達できる境界なら、ユニットテスト、統合テスト、E2Eテストのどれでもかまいません。
  2. 稼働中の開発サーバーに対するcurl/HTTPスクリプト。
  3. フィクスチャ入力を使うCLIの呼び出し。標準出力を既知の正常なスナップショットと比較します。
  4. ヘッドレスブラウザースクリプト(Playwright/Puppeteer)。UIを操作し、DOM/コンソール/ネットワークをアサートします。
  5. 取得したトレースを再生する。実際のネットワークリクエスト/ペイロード/イベントログをディスクに保存し、コードパスに単独で再生します。
  6. 使い捨てのハーネス。最小限のシステム構成(1つのサービス、依存関係のモック)を立ち上げ、1回の関数呼び出しでバグのコードパスを実行します。
  7. プロパティテスト/ファジングループ。バグが「ときどき誤った出力になる」場合は、ランダムな入力を1000個実行し、失敗のパターンを探します。
  8. 二分探索用ハーネス。既知の2つの状態(コミット、データセット、バージョン)の間でバグが発生した場合は、「状態Xで起動して確認し、繰り返す」手順を自動化し、git bisect runで実行できるようにします。
  9. 差分比較ループ。同じ入力を旧バージョンと新バージョン(または2つの設定)で実行し、出力を比較します。
  10. HITL Bashスクリプト。最後の手段です。人がクリックする必要がある場合は、scripts/hitl-loop.template.shを使って操作を誘導し、ループを構造化したままにします。取得した出力は次の診断に活用できます。WindowsではGit BashまたはWSLで実行してください。これはBashスクリプトであり、PowerShellから直接実行することはできません(Git Bashからはbash scripts/hitl-loop.template.sh、PowerShellからはwsl bash "$(wsl wslpath -a 'C:/path/to/hitl-loop.template.sh')")。

適切なフィードバックループを構築すれば、バグは90%解決したも同然です。

ループを磨き上げる

ループを1つのプロダクトとして扱います。ループを構築したら、さらに磨き上げてください。

  • もっと速くできますか?(セットアップをキャッシュし、無関係な初期化を省き、テスト範囲を絞る。)
  • 判定をもっと明確にできますか?(「クラッシュしなかったか」ではなく、具体的な症状をアサートする。)
  • もっと決定的にできますか?(時刻を固定し、乱数生成器のシードを設定し、ファイルシステムを分離し、ネットワークを固定する。)

不安定な30秒のループは、ループがないのと大差ありません。決定的な2秒のループなら、無駄がなく、デバッグの強力な武器になります。

非決定的なバグ

目指すのは完全な再現ではなく、再現率を高めることです。トリガーを100回繰り返し、並列実行し、負荷をかけ、タイミングの許容幅を狭め、スリープを挿入します。再現率が50%のバグならデバッグできますが、1%では困難です。デバッグできるようになるまで、再現率を上げ続けてください。

どうしてもループを構築できない場合

いったん止めて、その旨を明確に伝えてください。試したことを列挙してください。ユーザーには、次のいずれかを依頼してください。(a)問題を再現できる環境へのアクセス、(b)秘匿情報を伏せた取得済みアーティファクト(HAR ファイル、ログダンプ、コアダンプ、タイムスタンプ付きの画面録画)、または(c)一時的な本番環境の計測を追加する許可です。ループなしに仮説を立てて先へ進んではいけません。

完了条件:失敗を検出できる、緊密なループ

フェーズ 1 の完了条件は、ループが緊密で、かつ失敗を検出できることです。つまり、すでに少なくとも一度実行済みのコマンドを1つ(スクリプトのパス、テストの実行コマンド、curl など)示せること(実行したコマンドと、秘匿情報を伏せた出力を提示してください)。さらに、そのコマンドは次の条件を満たす必要があります。

  • 失敗を検出できる:実際のバグのコードパスを実行し、ユーザーに現れている症状そのものを検証します。このバグがあると失敗し、修正後は成功するものでなければなりません。「エラーなく実行できる」だけでは不十分です。_この特定のバグを検出できる_必要があります。
  • 決定的:毎回同じ判定になります(不安定なバグの場合は、前述のように再現率を固定して高くします)。
  • 高速:数秒で完了し、数分もかからないこと。
  • エージェントが実行可能:人が介在するのは scripts/hitl-loop.template.sh を使う場合に限り、人手を介さずに実行できること。

このコマンドができる前に、理論を組み立てるためにコードを読み始めていることに気づいたら、そこで止めてください。仮説へ直行することこそ、このスキルが防ぐ失敗です。 失敗を検出できるコマンドがなければ、フェーズ 2 に進んではいけません。

フェーズ 2:再現して最小化する

ループを実行します。バグが現れて失敗するのを確認してください。

次のことを確認します。

  • ループが、近くで発生した別の障害ではなく、ユーザーが説明した障害モードを引き起こすこと。別のバグを直しても、正しい修正にはなりません。
  • 複数回の実行で障害を再現できること(非決定的なバグの場合は、デバッグに十分な頻度で再現できること)。
  • 症状そのもの(エラーメッセージ、誤った出力、遅い処理時間)を記録し、後のフェーズで修正が実際に問題へ対処したか検証できること。
最小化

失敗を確認したら、再現ケースを失敗を維持できる最小のシナリオに縮小します。入力、呼び出し元、設定、データ、手順を一度に1つずつ削り、削るたびにループを再実行して、障害の発生に不可欠なものだけを残してください。

最小化する理由:再現ケースが小さくなると、フェーズ 3 で検討すべき仮説の範囲が狭まります(疑うべき要素が減ります)。また、フェーズ 5 で明確な回帰テストとして使えます。

残ったすべての要素が障害の発生に不可欠になったら完了です。要素を1つでも取り除くと、ループが成功する状態になります。

再現および最小化が完了するまで先へ進んではいけません。

フェーズ 3:仮説を立てる

どれも検証する前に、優先順位を付けた3~5個の仮説を立ててください。仮説を1つだけ立てると、最初のもっともらしい考えに引きずられます。

各仮説は反証可能である必要があります。仮説から導かれる予測を示してください。

形式:「原因が なら、 を変更するとバグが解消し、 を変更すると悪化する。」

予測を示せない場合、その仮説は単なる勘です。破棄するか、具体化してください。

検証する前に、優先順位を付けた一覧をユーザーに提示してください。 ユーザーはドメイン知識を持っており、優先順位をすぐに変えることがあります(「#3 に関係する変更を今デプロイしたところです」)。また、すでに除外した仮説を知っている場合もあります。わずかな確認で、大幅な時間を節約できます。ユーザーの返答を待って作業を止める必要はありません。ユーザーが不在なら、自分で付けた優先順位に従って進めてください。

フェーズ 4:計測する

各プローブは、フェーズ 3 の特定の予測に対応させてください。変更する変数は一度に1つだけにします。

ツールの優先順位:

  1. デバッガー/REPL による調査:環境が対応している場合に使用します。ログを10個出すより、ブレークポイントを1つ使うほうが効果的です。
  2. 対象を絞ったログ:仮説を見分けられる境界に出力します。
  3. 「すべてをログに出して grep」は決して行わないでください。

すべてのデバッグログに、[DEBUG-a4f2] のような一意のプレフィックスを付けてください。最後のクリーンアップは grep 1回で済みます。タグのないログは残り、タグ付きログは削除できます。

パフォーマンスの分岐。 パフォーマンスのリグレッションでは、通常ログは適切ではありません。その代わり、基準値(計測ハーネス、performance.now()、プロファイラー、クエリプラン)を確立してから、二分探索してください。まず計測し、修正はその後です。

フェーズ 5:修正+回帰テスト

修正を加える前に回帰テストを書きます。ただし、適切なテストの接点がある場合に限ります。

適切なテストの接点とは、呼び出し箇所で実際に発生する本物のバグパターンをテストで再現できる箇所です。利用できる接点が浅すぎる場合(複数の呼び出し元が必要なバグなのに単一の呼び出し元だけをテストする場合や、バグを引き起こした呼び出しの連鎖を再現できない単体テストなど)、そこで回帰テストを行うと誤った安心感を招きます。

**適切なテストの接点がないなら、それ自体が発見事項です。**記録してください。コードベースのアーキテクチャによって、バグを再発しないように防ぐことができません。次のフェーズでこの点を取り上げてください。

適切なテストの接点がある場合:

  1. 最小化した再現例を、その接点で失敗するテストにします。
  2. テストが失敗することを確認します。コードやフィクスチャを変更して意図的に失敗させた場合は、その変更が加わったと確信する前に、クリーンなコピーとの差分を diff で確認します。
  3. 修正を適用します。
  4. テストが成功することを確認します。
  5. 元の(最小化していない)シナリオに対して、フェーズ 1 のフィードバックループを再実行します。

フェーズ 6:クリーンアップ

完了とする前に、以下が必要です。

  • 元の再現例で問題が再現しなくなった(フェーズ 1 のループを再実行)
  • 回帰テストが成功する(または適切なテストの接点がないことを記録済み)
  • [DEBUG-...] の計測コードをすべて削除した(プレフィックスを grep で検索)
  • 一時的なプロトタイプを削除した(または、デバッグ用と明確に示した場所に移動した)
  • 正しかった仮説をコミットメッセージまたは PR メッセージに記載し、次にデバッグする人が学べるようにした

コメント