mcpField
D
diagnosing-bugs

diagnosing-bugs

github.com/mattpocock测试与质量305.7k856.9k 次安装1 天前更新
代码仓库

关于 diagnosing-bugs

diagnosing-bugs 是一个用于排查疑难 Bug 的 Agent 技能。它要求先构建一个能稳定复现问题的反馈循环,再进行假设、插桩和修复,适合间歇性故障和回归问题。

主要功能

  1. 01先建立红灯可复现的反馈循环
  2. 02复现后将案例缩减到最小
  3. 03生成并排序可证伪的假设
  4. 04使用带前缀的调试日志插桩
  5. 05修复前先编写回归测试
  6. 06收尾时检查调试代码是否已清理

适用场景

  1. 01排查间歇性失败或不稳定的测试
  2. 02定位两个已知正常版本之间引入的回归
  3. 03在修复前确认问题真实复现

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/diagnosing-bugs确认已加载。

附带文件agents/openai.yamlscripts/hitl-loop.template.sh

详细介绍

诊断 Bug

针对疑难 Bug 的一套方法。只有在明确说明理由时才可跳过某个阶段。

探索代码库时,若存在 GLOSSARY.md,请先阅读它,以便清晰理解相关模块;同时检查你所修改区域中的 ADR。

脱敏

本技能会要求你展示命令、输出和捕获的产物。先对所有机密信息进行脱敏:用 <REDACTED> 替换它们。基于环境变量构建循环,使凭据保留在环境中,而不出现在你展示的内容里。捕获的产物中包含认证请求头:只引用携带关键信号的行。

如果脱敏后的输出不足以诊断该 Bug,请明确说明并询问用户。

阶段 1:构建反馈循环

这是本技能的核心。 其余步骤都是机械性的。如果你拥有针对该 Bug 的严格通过/失败信号(只在_这个_ Bug 出现时才变红),就能找到根因;二分法、假设验证和插桩都只是消费这一信号。如果没有这样的信号,再怎么盯着代码看也无济于事。

在这里投入远超其他步骤的精力。要大胆,要有创造力,绝不放弃。

构建方式,大致按以下顺序排列
  1. 失败测试:在能触达该 Bug 的任意切入点编写:单元测试、集成测试或端到端测试。
  2. Curl / HTTP 脚本:针对正在运行的开发服务器。
  3. 命令行调用:使用测试夹具输入,将标准输出与已知正确的快照进行比对。
  4. 无头浏览器脚本(Playwright / Puppeteer):驱动 UI,并对 DOM、控制台或网络请求进行断言。
  5. 重放捕获的追踪记录。 将真实的网络请求、负载或事件日志保存到磁盘;在隔离环境中通过相应代码路径重放。
  6. 一次性测试框架。 启动系统的最小子集(单个服务,模拟依赖),通过一次函数调用执行该 Bug 的代码路径。
  7. 属性测试 / 模糊测试循环。 如果 Bug 表现为「偶尔输出错误」,运行 1000 组随机输入,寻找失败模式。
  8. 二分测试框架。 如果 Bug 出现在两个已知状态(提交、数据集、版本)之间,将「在状态 X 启动、检查、重复」自动化,以便使用 git bisect run 执行。
  9. 差分循环。 将同一输入分别通过旧版本与新版本(或两种配置)运行,并比对输出。
  10. 人工介入(HITL)Bash 脚本。 最后手段。如果必须由人点击,则使用 scripts/hitl-loop.template.sh 引导其操作,使循环仍然有结构。捕获的输出会反馈给你。

构建正确的反馈循环,Bug 就已经修复了 90%。

收紧循环

把循环当作一个产品来对待。一旦有了_一个_循环,就要收紧它:

  • 能否让它更快?(缓存初始化、跳过无关的初始化、缩小测试范围。)
  • 能否让信号更精准?(断言具体症状,而不是「没有崩溃」。)
  • 能否让它更具确定性?(固定时间、设定随机种子、隔离文件系统、冻结网络。)

一个耗时 30 秒且不稳定的循环,几乎不比没有循环好;而一个耗时 2 秒且确定性的循环,才是严密的、强大的调试利器。

非确定性 Bug

目标不是获得干净的复现,而是提高复现率。将触发操作循环 100 次,并行化,增加压力,缩小时间窗口,注入 sleep。复现率为 50% 的 Bug 可以调试,1% 则不行,因此应持续提高复现率,直到可以调试为止。

确实无法构建循环时

请明确停下并说明情况。列出你已尝试的方法。向用户请求:(a) 访问能够复现该问题的环境;(b) 经过脱敏的捕获产物(HAR 文件、日志转储、核心转储、带时间戳的屏幕录像);或 (c) 添加临时生产环境插桩的许可。在没有循环的情况下,不要继续提出假设。

完成标准:一个能稳定触发失败的紧凑循环

当循环足够紧凑且能够触发失败时,Phase 1 就完成了:你可以指出一条命令(脚本路径、测试调用或 curl),并且至少运行过一次(展示调用和输出,注意脱敏),且该命令满足以下条件:

  • 能够触发失败:它会驱动实际的 Bug 代码路径,并断言用户遇到的确切症状,因此遇到这个 Bug 时能失败,修复后能通过。不是“运行时没有报错”就够了;它必须能_捕获这个特定 Bug_。
  • 确定性:每次运行结果都相同(对于不稳定的 Bug,按照上述说明,需固定并达到较高的复现率)。
  • 快速:耗时以秒计,而不是分钟。
  • 可由 Agent 运行:可以无人值守地运行;只有通过 scripts/hitl-loop.template.sh 才需要人工介入。

如果你发现自己在这条命令还不存在时,就开始读代码构建理论,请停下来:直接跳到假设阶段,正是这项技能要避免的问题。 没有能触发失败的命令,就不能进入 Phase 2。

Phase 2:复现并缩小范围

运行循环。观察它在 Bug 出现时失败。

确认:

  • 循环产生的是用户描述的故障模式,而不是碰巧相关的另一种故障。Bug 找错了,修复也会错。
  • 多次运行都能复现故障(对于非确定性 Bug,复现率必须足够高,才能据此调试)。
  • 已捕获确切症状(错误消息、错误输出、耗时过长),以便后续阶段验证修复是否确实解决了问题。
缩小范围

确认循环会失败后,将复现缩减为仍能触发失败的最小场景。每次只删减一项输入、调用方、配置、数据或步骤,并在每次删减后重新运行循环,只保留对故障起关键作用的内容。

这样做的原因:最小复现能在 Phase 3 缩小假设范围(可疑因素更少),并在 Phase 5 成为干净的回归测试。

当剩下的每个元素都起关键作用时,即可完成:移除其中任意一个,循环都会通过。

完成复现并缩小范围之前,不要继续。

Phase 3:提出假设

在测试任何假设之前,先生成 3–5 个按优先级排序的假设。只生成一个假设会让人锚定在第一个看似合理的想法上。

每个假设都必须可证伪:说明它会给出什么预测。

格式:“如果 是原因,那么 <改变 Y> 将使 Bug 消失/<改变 Z> 将使问题恶化。”

如果无法说明预测,这个假设就只是凭感觉:舍弃它或进一步明确。

测试假设之前,先向用户展示排序后的列表。 用户通常掌握领域知识,可以立即重新排序(“我们刚部署了与 #3 相关的改动”),或者知道哪些假设已经被排除。这是成本低、收益大的检查点。不要因此停滞;如果用户暂时不在线,就按自己的排序继续。

Phase 4:加设探针

每个探针都必须对应 Phase 3 中的具体预测。每次只改变一个变量。

工具优先顺序:

  1. 如果环境支持,优先使用调试器/REPL 检查。一个断点胜过十条日志。
  2. 在能够区分不同假设的边界处添加定向日志。
  3. 绝不要“记录所有内容再用 grep 筛选”。

为每条调试日志添加唯一前缀,例如 [DEBUG-a4f2]。最后只需一次 grep 就能清理干净。未加标签的日志会残留;加了标签的日志则会被清除。

性能分支。 对于性能回退,日志通常不是正确的办法。相反,应先建立基准测量(计时测试框架、performance.now()、性能分析器、查询计划),然后进行二分排查。先测量,再修复。

阶段 5:修复 + 回归测试

仅当存在合适的 seam 时,才在修复之前编写回归测试。

合适的 seam 是指测试能够在调用点上复现真实的 bug 模式。如果唯一可用的 seam 过于浅层(例如只有单一调用方的测试,而 bug 需要多个调用方才能触发;或者单元测试无法复现触发 bug 的调用链),那么在该处编写回归测试会带来虚假的信心。

如果不存在合适的 seam,这本身就是一项发现。 请记录下来。这说明代码架构阻碍了对该 bug 的固化验证。将其标记,交由下一阶段处理。

如果存在合适的 seam:

  1. 将最小化复现转换为该 seam 处的失败测试。
  2. 确认测试确实失败。如果你是通过修改代码或测试夹具来强制得到红色结果,请与未修改的原始副本执行 diff,以证明修改已生效,之后再信任该结果。
  3. 应用修复。
  4. 确认测试通过。
  5. 针对原始(未最小化)场景,重新运行阶段 1 的反馈循环。

阶段 6:清理

宣告完成前必须满足以下条件:

  • 原始复现不再出现(重新运行阶段 1 的循环)
  • 回归测试通过(或已记录缺少 seam 的情况)
  • 所有 [DEBUG-...] 调试代码已移除(用 grep 搜索该前缀)
  • 一次性原型已删除(或已移至明确标注的调试目录)
  • 在提交信息或 PR 描述中写明最终被证实正确的假设,以便后续调试者从中学习

评论

支持 Markdown