root-cause-tracing について
呼び出しチェーンをさかのぼり、症状ではなくエラーの発端を特定する開発者向けデバッグスキルです。テスト汚染の調査やスタックトレースの記録方法も扱います。
主な機能
- 01呼び出しチェーンをたどるエラー調査
- 02発端特定のためのスタックトレース記録
- 03テストを個別実行して汚染源を特定
- 04根本原因の修正と防御策の追加
活用シーン
- 01実行深部で発生するエラーの調査
- 02無効なデータの発生元の特定
- 03テスト汚染を起こすテストの発見
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill root-cause-tracingインストール後、Claude Code で/root-cause-tracingを入力して読み込まれたか確認します。
詳細
根本原因の追跡
概要
バグはコールスタックの深い場所で表面化することがよくあります(間違ったディレクトリでの git init、間違った場所へのファイル作成、間違ったパスでのデータベースのオープンなど)。エラーが現れた場所を修正したくなりますが、それでは症状に対処しているだけです。
基本原則: 呼び出しチェーンをさかのぼり、最初のトリガーを見つけてから、発生源で修正します。
使用する場面
次の場合に使用します:
- 実行の深い段階でエラーが発生する(エントリーポイントではない)
- スタックトレースに長い呼び出しチェーンが表示される
- 無効なデータがどこで発生したのか不明である
- 問題を引き起こすテスト/コードを特定する必要がある
追跡プロセス
1. 症状を観察する
Error: git init failed in ~/project/packages/core
2. 直接の原因を見つける
この問題を直接引き起こしているコードは何ですか?
await execFileAsync('git', ['init'], { cwd: projectDir });
3. 何がこれを呼び出したかを確認する
WorktreeManager.createSessionWorktree(projectDir, sessionId)
→ called by Session.initializeWorkspace()
→ called by Session.create()
→ called by test at Project.create()
4. さらにさかのぼって追跡する
どの値が渡されましたか?
projectDir = ''(空文字列!)cwdが空文字列の場合、process.cwd()に解決される- それがソースコードのディレクトリです!
5. 最初のトリガーを見つける
空文字列はどこから来ましたか?
const context = setupCoreTest(); // Returns { tempDir: '' }
Project.create('name', context.tempDir); // Accessed before beforeEach!
スタックトレースの追加
手動で追跡できない場合は、計測コードを追加します:
// Before the problematic operation
async function gitInit(directory: string) {
const stack = new Error().stack;
console.error('DEBUG git init:', {
directory,
cwd: process.cwd(),
nodeEnv: process.env.NODE_ENV,
stack,
});
await execFileAsync('git', ['init'], { cwd: directory });
}
重要: テストでは console.error() を使用してください(logger は表示されない場合があります)
実行して記録する:
bun test 2>&1 | grep 'DEBUG git init'
スタックトレースを分析する:
- テストファイル名を探す
- 呼び出しを引き起こした行番号を見つける
- パターンを特定する(同じテストか?同じパラメーターか?)
汚染を引き起こすテストの特定
テスト中に何かが現れるものの、どのテストが原因かわからない場合:
二分探索スクリプトを使って、テストを1つずつ実行します:
# Example: find which test creates .git in wrong place
bun test --run --bail 2>&1 | tee test-output.log
テストを1つずつ実行し、最初に汚染を引き起こした時点で停止します。
実例:空の projectDir
症状: .git が packages/core/(ソースコード)に作成される
追跡チェーン:
git initがprocess.cwd()で実行される ← cwd パラメーターが空- WorktreeManager が空の projectDir で呼び出される
- Session.create() に空文字列が渡される
- beforeEach の前にテストが
context.tempDirにアクセスする - setupCoreTest() が最初に
{ tempDir: '' }を返す
根本原因: 空の値にアクセスするトップレベル変数の初期化
修正: beforeEach の前にアクセスされた場合に例外をスローする getter に tempDir を変更
さらに多層防御を追加:
- レイヤー 1:Project.create() がディレクトリを検証する
- レイヤー 2:WorkspaceManager が空でないことを検証する
- レイヤー 3:NODE_ENV ガードが tmpdir 外での git init を拒否する
- レイヤー 4:git init の前にスタックトレースを記録する
重要な原則
エラーが現れた場所だけを修正してはいけません。 最初のトリガーを見つけるために、さかのぼって追跡します。
スタックトレースのヒント
テストでは: logger ではなく console.error() を使用します。logger は抑制される場合があります
操作の前に: 危険な操作が失敗した後ではなく、その前に記録します
コンテキストを含める: ディレクトリ、cwd、環境変数、タイムスタンプ
スタックを記録する: new Error().stack で完全な呼び出しチェーンを確認できます
実際の効果
デバッグセッションの結果:
- 5段階の追跡で根本原因を発見
- 発生源で修正(getter による検証)
- 4層の防御を追加
- 1847 件のテストに合格し、汚染はゼロ