root-cause-tracing について
コールスタックの深部で起きるエラーを調査する開発者向けスキルです。呼び出し元と入力をたどって元の原因を特定し、関連する層に検証を加える手順を示します。
主な機能
- 01コールチェーンをさかのぼる
- 02無効な値の発生源を特定
- 03スタックトレースと状況を記録
- 04テストを二分して汚染源を特定
- 05複数の層に検証を追加
活用シーン
- 01誤ったファイルパスの発生源を特定
- 02テスト汚染の原因を調査
- 03実行の深部で起きる障害を診断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ neolabhq/ context-engineering-kit --skill root-cause-tracingインストール後、Claude Code で/root-cause-tracingを入力して読み込まれたか確認します。
詳細
根本原因の追跡
概要
バグは、コールスタックの深い場所で表面化することがよくあります(間違ったディレクトリでの git init、間違った場所へのファイル作成、間違ったパスでのデータベースのオープンなど)。エラーが現れた場所を修正したくなりますが、それでは症状に対処しているにすぎません。
基本原則: 呼び出しチェーンをさかのぼり、最初のトリガーを見つけてから、発生源で修正します。
使用するタイミング
digraph when_to_use {
"Bug appears deep in stack?" [shape=diamond];
"Can trace backwards?" [shape=diamond];
"Fix at symptom point" [shape=box];
"Trace to original trigger" [shape=box];
"BETTER: Also add defense-in-depth" [shape=box];
"Bug appears deep in stack?" -> "Can trace backwards?" [label="yes"];
"Can trace backwards?" -> "Trace to original trigger" [label="yes"];
"Can trace backwards?" -> "Fix at symptom point" [label="no - dead end"];
"Trace to original trigger" -> "BETTER: Also add defense-in-depth";
}
次の場合に使用します:
- 実行の深い場所でエラーが発生する(エントリーポイントではない)
- スタックトレースに長い呼び出しチェーンが表示される
- 無効なデータがどこで発生したのかが不明
- 問題を引き起こすテストやコードを特定する必要がある
追跡の手順
1. 症状を確認する
Error: git init failed in /Users/jesse/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 は表示されない場合があります)
実行して記録します:
npm test 2>&1 | grep 'DEBUG git init'
スタックトレースを分析します:
- テストファイル名を探す
- 呼び出しを引き起こした行番号を見つける
- パターンを特定する(同じテストか?同じパラメーターか?)
汚染を引き起こすテストを見つける
テスト中に何かが現れたものの、どのテストが原因かわからない場合:
二分探索スクリプトを使用します:@find-polluter.sh
./find-polluter.sh '.git' 'src/**/*.test.ts'
テストを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 の前にスタックトレースをログに記録する
重要な原則
digraph principle {
"Found immediate cause" [shape=ellipse];
"Can trace one level up?" [shape=diamond];
"Trace backwards" [shape=box];
"Is this the source?" [shape=diamond];
"Fix at source" [shape=box];
"Add validation at each layer" [shape=box];
"Bug impossible" [shape=doublecircle];
"NEVER fix just the symptom" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
"Found immediate cause" -> "Can trace one level up?";
"Can trace one level up?" -> "Trace backwards" [label="yes"];
"Can trace one level up?" -> "NEVER fix just the symptom" [label="no"];
"Trace backwards" -> "Is this the source?";
"Is this the source?" -> "Trace backwards" [label="no - keeps going"];
"Is this the source?" -> "Fix at source" [label="yes"];
"Fix at source" -> "Add validation at each layer";
"Add validation at each layer" -> "Bug impossible";
}
エラーが現れた場所だけを修正してはいけません。 元のトリガーを見つけるために、さかのぼって追跡してください。
スタックトレースのヒント
テストでは: logger ではなく console.error() を使用してください。logger は抑制される場合があります
操作の前に: 危険な操作が失敗した後ではなく、実行する前にログを記録してください
コンテキストを含める: ディレクトリ、cwd、環境変数、タイムスタンプ
スタックを取得する: new Error().stack で完全な呼び出しチェーンを確認できます
実環境での効果
デバッグセッション(2025-10-03)より:
- 5段階の追跡で根本原因を発見
- 発生源で修正(getter による検証)
- 4層の防御を追加
- 1847件のテストが成功し、汚染はゼロ