mcpField
D
diagnose

diagnose

github.com/polya20开发043 次安装24 周前更新
代码仓库

关于 diagnose

diagnose 是用于排查复杂缺陷的开发技能,通过可重复、可由 Agent 运行的反馈循环开展调查。它指导复现问题、排列假设、定向插桩、回归测试和清理。

主要功能

  1. 01构建可重复的反馈循环
  2. 02复现并记录问题表现
  3. 03排列可证伪的假设
  4. 04进行定向插桩探查
  5. 05添加回归测试并清理

适用场景

  1. 01调查难以复现的缺陷
  2. 02排查非确定性故障
  3. 03追踪性能回退
  4. 04针对原始场景验证修复

安装

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

$npx skills add https://github.com/polya20/grill-me --skill diagnose

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

附带文件scripts/hitl-loop.template.sh

详细介绍

诊断

专门用于处理棘手 bug 的方法。只有在明确说明理由时,才能跳过阶段。

探索代码库时,使用项目的领域术语表,清楚了解相关模块,并查看你要修改的部分的 ADR。

阶段 1 — 建立反馈循环

这就是关键技能。 其他一切都是机械操作。如果你有一个快速、确定、可由 Agent 运行的通过/失败信号,就能找到原因——二分查找、假设检验和插桩都只是利用这个信号。如果没有,再怎么盯着代码看也无济于事。

在这里投入不成比例的精力。积极尝试。发挥创意。绝不轻易放弃。

构建反馈循环的方法 — 大致按以下顺序尝试
  1. 失败测试:在能够触及 bug 的任意边界上进行测试——单元测试、集成测试、端到端测试。
  2. Curl / HTTP 脚本:针对正在运行的开发服务器。
  3. CLI 调用:使用固定输入样例,并将标准输出与已知正常的快照进行差异比对。
  4. 无头浏览器脚本(Playwright / Puppeteer):驱动 UI,并对 DOM/控制台/网络进行断言。
  5. 重放捕获的跟踪记录。 将真实的网络请求/负载/事件日志保存到磁盘,然后通过隔离环境中的代码路径重放。
  6. 一次性测试框架。 启动系统的最小子集(一个服务、模拟依赖),通过一次函数调用触发有问题的代码路径。
  7. 属性/模糊测试循环。 如果 bug 表现为“输出有时错误”,就运行 1000 个随机输入,寻找故障模式。
  8. 二分测试框架。 如果 bug 出现在两个已知状态(提交、数据集、版本)之间,就自动执行“在状态 X 启动、检查、重复”,以便运行 git bisect run。
  9. 差分循环。 将相同输入分别交给旧版本和新版本(或两种配置),并比较输出。
  10. HITL bash 脚本。 最后的手段。如果必须由人点击,就用 scripts/hitl-loop.template.sh 引导_他们_,让循环仍然有条理。捕获的输出会反馈给你。

构建合适的反馈循环,bug 就解决了 90%。

不断改进循环本身

把循环当作产品。建立_一个_循环后,思考:

  • 能否让它更快?(缓存设置、跳过无关初始化、缩小测试范围。)
  • 能否让信号更明确?(针对具体症状断言,而不是只检查“没有崩溃”。)
  • 能否让它更确定?(固定时间、设置随机数种子、隔离文件系统、冻结网络。)

一个耗时 30 秒且不稳定的循环,几乎不比没有循环强。一个耗时 2 秒且确定的循环,是调试的超能力。

非确定性 bug

目标不是构造出干净的复现步骤,而是提高复现率。重复触发 100 次、并行运行、增加压力、缩小时间窗口、注入休眠。复现率为 50% 的 bug 可以调试;1% 的不行——持续提高复现率,直到能够调试。

确实无法构建循环时

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

在建立你确信可靠的循环之前,不要进入阶段 2。

阶段 2 — 复现

运行循环。观察 bug 出现。

确认:

  • 循环产生了用户描述的故障模式——而不是附近碰巧发生的另一种故障。找错 bug,就会修错问题。
  • 多次运行都能复现故障(对于非确定性 bug,则需要以足够高的频率复现,才能据此调试)。
  • 已捕获确切症状(错误消息、错误输出、耗时过长),以便后续阶段确认修复确实解决了问题。

在复现 bug 之前不要继续。

阶段 3 — 提出假设

在测试其中任何一个假设之前,生成 3–5 个按可能性排序的假设。只提出一个假设会让你锚定于第一个看似合理的想法。

每个假设都必须可证伪:明确写出它所预测的结果。

格式:“如果 是原因,那么 会使缺陷消失/ 会使缺陷恶化。”

如果无法给出预测,这个假设就只是一种直觉——应当舍弃或完善。

测试前先向用户展示排好序的假设列表。 用户通常掌握领域知识,往往能立刻重新排序(“我们刚部署了针对 #3 的更改”),或者知道哪些假设已经被排除。这只是成本极低的检查点,却能节省大量时间。不要等待回应——如果用户不在(AFK),就按你的排序继续。

阶段 4 — 埋点

每项探测都必须对应阶段 3 中的一项具体预测。每次只更改一个变量。

工具优先级:

  1. 调试器 / REPL 检查,前提是环境支持。一个断点胜过十条日志。
  2. 在能够区分各项假设的边界处添加有针对性的日志。
  3. 绝不要“记录所有内容再 grep”。

为每条调试日志添加标签,使用唯一前缀,例如 [DEBUG-a4f2]。这样,结束时只需执行一次 grep 即可完成清理。未加标签的日志留存;加了标签的日志淘汰。

性能分支。 对于性能回归,日志通常不是正确选择。应改为先建立基线测量(计时工具、performance.now()、性能分析器、查询计划),再进行二分排查。先测量,后修复。

阶段 5 — 修复 + 回归测试

先编写回归测试,然后再修复——但前提是存在一个正确的测试切入点。

正确的测试切入点,是指测试能够还原调用点处实际出现的缺陷模式。如果唯一可用的切入点过浅(例如,缺陷需要多个调用方,但测试只涉及单个调用方;或者单元测试无法复现触发缺陷的调用链),那么在该处进行回归测试只会带来虚假信心。

如果不存在正确的测试切入点,这本身就是调查结论。 记录这一点。当前代码库架构使该缺陷无法通过测试锁定。在下一阶段将这一点标出。

如果存在正确的测试切入点:

  1. 在该切入点将最小化后的复现转化为失败的测试。
  2. 确认测试失败。
  3. 应用修复。
  4. 确认测试通过。
  5. 针对原始的(未经最小化的)场景重新运行阶段 1 反馈循环。

阶段 6 — 清理 + 复盘

宣布完成前必须做到:

  • 原始复现问题不再出现(重新运行阶段 1 循环)
  • 回归测试通过(或已记录不存在测试切入点)
  • 所有 [DEBUG-...] 埋点均已移除(对该前缀执行 grep)
  • 一次性原型已删除(或已移至明确标记的调试位置)
  • 提交 / PR 消息中写明了最终证实正确的假设,让下一位调试者可以借鉴

然后问:怎样才能防止这个缺陷? 如果答案涉及架构变更(没有合适的测试切入点、调用关系混乱或存在隐蔽耦合),则将具体情况移交给 /improve-codebase-architecture 技能。应在修复完成后再提出建议,而不是提前提出——因为现在掌握的信息比开始时更多。

评论

支持 Markdown