关于 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确认已加载。
详细介绍
根因追踪
概述
Bug 往往会在调用栈深处显现(在错误的目录中运行 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'
分析堆栈跟踪:
- 查找测试文件名
- 找出触发调用的行号
- 识别模式(同一个测试?同一个参数?)
找出导致污染的测试
如果某个问题在测试期间出现,但你不知道是哪个测试导致的:
使用二分脚本逐个运行测试:
# Example: find which test creates .git in wrong place
bun test --run --bail 2>&1 | tee test-output.log
脚本会逐个运行测试,并在发现第一个造成污染的测试时停止。
实例:空的 projectDir
**症状:**在 packages/core/(源代码目录)中创建了 .git
追踪链:
git init在process.cwd()中运行 ←cwd参数为空- WorktreeManager 接收到空的 projectDir
- Session.create() 接收到空字符串
- 测试在 beforeEach 之前访问了
context.tempDir - setupCoreTest() 最初返回
{ tempDir: '' }
**根因:**顶层变量初始化时访问了空值
**修复:**将 tempDir 改为 getter,在 beforeEach 执行前访问时抛出错误
还添加了纵深防御:
- 第 1 层:Project.create() 验证目录
- 第 2 层:WorkspaceManager 验证目录不为空
- 第 3 层:NODE_ENV 守卫拒绝在 tmpdir 之外运行 git init
- 第 4 层:在运行 git init 前记录堆栈跟踪
核心原则
**绝不要只修复错误出现的位置。**追溯问题,找出最初的触发点。
堆栈跟踪提示
**在测试中:**使用 console.error(),不要使用 logger——logger 可能被抑制
**操作之前:**在执行危险操作之前记录日志,不要等操作失败后再记录
**包含上下文:**目录、cwd、环境变量、时间戳
捕获堆栈:new Error().stack 会显示完整调用链
实际效果
来自一次调试会话:
- 通过 5 层追踪找到了根因
- 从源头修复(getter 验证)
- 增加了 4 层防御
- 1847 项测试通过,没有造成任何污染
评论
支持 Markdown