关于 diagnose
diagnose 是面向编码 Agent 的疑难调试技能,强调先建立可靠的反馈循环,再分析代码。它依次指导复现问题、提出假设、定向插桩、编写回归测试并清理调试产物。
主要功能
- 01构建确定性故障反馈循环
- 02复现并记录准确故障
- 03排列可证伪的假设
- 04在关键代码边界定向插桩
- 05添加回归测试并清理
适用场景
- 01调试间歇性故障
- 02调查已报告的线上问题
- 03追踪跨提交或版本的回归
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ leandrocfe/ skills --skill diagnose安装后在 Claude Code 中输入/diagnose确认已加载。
附带文件scripts/hitl-loop.template.sh
详细介绍
诊断
用于处理疑难 Bug 的流程。只有给出明确理由时才能跳过阶段。
探索代码库时,请使用项目的领域词汇表,清楚了解相关模块,并检查你正在处理的领域中的 ADR。
阶段 1 — 构建反馈循环
这就是这项技能。 其他一切都是机械操作。如果你有一个快速、确定且可由 Agent 运行的通过/失败信号来检测 Bug,就能找到原因——二分、假设检验和插桩都只是利用这个信号。如果没有,再多花时间盯着代码也无济于事。
在这里投入不成比例的精力。积极尝试。大胆创新。不要放弃。
构建方法 — 尽量按以下顺序尝试
- 在任何能触及 Bug 的接缝处编写失败测试——单元测试、集成测试和端到端测试。
- 对正在运行的开发服务使用 curl/HTTP 脚本。
- 使用 fixture 输入调用 CLI,并将标准输出与已知正常快照进行差异比较。
- 编写无头浏览器脚本(Playwright/Puppeteer)——驱动 UI,并对 DOM/控制台/网络进行断言。
- 回放捕获的 trace。 将真实网络请求/payload/事件日志保存到磁盘;在隔离环境中回放至代码路径。
- 编写一次性测试框架。 启动系统的最小子集(一个服务、模拟依赖),通过一次函数调用触发 Bug 所在的代码路径。
- 属性/模糊测试循环。 如果 Bug 表现为“有时输出错误”,就运行 1000 个随机输入并查找失败模式。
- 二分测试框架。 如果 Bug 出现在两个已知状态(commit、数据集、版本)之间,就自动执行“在状态 X 启动、检查、重复”,以便运行
git bisect run。 - 差分循环。 使用同一输入分别运行旧版本和新版本(或两种配置),并比较输出。
- Bash HITL 脚本。 最后手段。如果必须由人点击,就用
scripts/hitl-loop.template.sh驱动_该用户_,让循环仍然有条理。捕获的输出会反馈给你。
构建好反馈循环,Bug 就有 90% 修好了。
迭代改进循环本身
把循环当作产品。构建出_一个_循环后,问问自己:
- 能否让它更快?(缓存初始化、跳过无关的初始化、缩小测试范围。)
- 能否让信号更清晰?(针对具体症状进行断言,而不是只断言“没有崩溃”。)
- 能否让它更确定?(固定时间、设定随机数种子、隔离文件系统、冻结网络。)
一个运行 30 秒且不稳定的循环,和没有循环相比几乎好不了多少。一个确定且只需 2 秒的循环,则是调试的超能力。
非确定性 Bug
目标不是得到干净的复现,而是提高复现率。将触发条件循环 100 次、并行运行、增加压力、缩窄时序窗口、注入休眠。复现率为 50% 的 Bug 可以调试;1% 的不行——提高复现率,直到可以调试为止。
确实无法构建循环时
停下来并明确说明。列出你尝试过的方法。请用户提供:(a) 可复现问题的环境访问权限;(b) 捕获的产物(HAR 文件、日志转储、core dump、带时间戳的屏幕录像);或 (c) 在生产环境中添加临时插桩的许可。不要在没有循环的情况下继续提出假设。
在确信循环可靠之前,不要进入阶段 2。
阶段 2 — 复现
运行循环。观察 Bug 出现。
确认:
- 循环产生的是用户描述的失败模式,而不是恰好与之相关的其他失败。Bug 搞错了,修复也会错。
- 失败在多次运行中可复现(对于非确定性 Bug,则需要达到足以进行调试的复现率)。
- 你已捕获确切症状(错误消息、错误输出、缓慢的时序),以便后续阶段验证修复是否真正解决了问题。
复现 Bug 之前不要继续。
阶段 3 — 提出假设
在测试任何假设之前,先生成 3-5 个按优先级排序的假设。只生成一个假设会让你锚定在第一个看似合理的想法上。
每个假设都必须可证伪:明确说明它会做出什么预测。
格式:“如果 是原因,那么 <更改 Y> 会让 bug 消失 / <更改 Z> 会让问题恶化。”
如果你无法说明预测,这个假设就只是凭感觉——请舍弃或进一步明确。
在测试之前,先向用户展示排序后的列表。 他们通常掌握能立刻调整排序的领域知识(“我们刚刚部署了 #3 中的更改”),或者知道哪些假设已经被排除。这是成本低、节省时间多的检查点。不要因此停下来——如果用户暂时不在线,就按你的排序继续。
阶段 4 — 埋点
每个探针都必须对应阶段 3 中的一个具体预测。每次只更改一个变量。
工具优先级:
- 如果环境支持,优先通过 debugger / REPL 检查。一个断点胜过十条日志。
- 在能区分不同假设的边界处添加有针对性的日志。
- 绝不要“把所有内容都记录下来再用 grep 搜索”。
为每条调试日志添加唯一前缀,例如 [DEBUG-a4f2]。最后清理时只需 grep 一次。没有标签的日志会残留下来;带标签的日志则会被清除。
性能分支。 对于性能回归,日志通常不是正确的工具。应先建立基准测量(计时测试框架、performance.now()、profiler、query plan),然后再进行二分排查。先测量,再修复。
阶段 5 — 修复 + 回归测试
在修复之前编写回归测试——但前提是存在合适的测试接缝。
合适的测试接缝是指测试能在调用点按实际 bug 模式进行验证。如果唯一可用的接缝过于浅显(bug 需要多个调用方,但测试只覆盖单个调用方;或单元测试无法复现触发 bug 的调用链),在那里编写回归测试会带来虚假的信心。
如果不存在合适的测试接缝,这本身就是一项发现。 请记录下来。这说明代码库的架构阻碍了对该 bug 的测试锁定。请在下一阶段指出这一点。
如果存在合适的测试接缝:
- 将最小化复现转化为一个在该接缝处失败的测试。
- 观察它失败。
- 应用修复。
- 观察它通过。
- 针对原始场景(未最小化)重新运行阶段 1 的反馈循环。
阶段 6 — 清理 + 事后分析
在宣布完成之前,必须完成以下事项:
- 原始复现不再出现(重新运行阶段 1 的循环)
- 回归测试通过(或已记录不存在合适的测试接缝)
- 已移除所有
[DEBUG-...]埋点(grep 该前缀) - 已删除一次性原型(或将其移至明确标记的调试位置)
- 在 commit / PR 消息中说明最终证实的假设——让下一位调试人员能够吸取经验
然后问:怎样才能避免这个 bug? 如果答案涉及架构变更(缺少良好的测试接缝、调用方相互缠绕、隐藏的耦合),请将详细信息交接给 skill /improve-codebase-architecture。请在修复合入之后再提出建议,而不是之前——现在你掌握的信息比刚开始时更多。