Lesson 0015 · Engineering · 人和 AI 都能启动(model-invoked)· 流程课

diagnosing-bugs:先造反馈环,再谈理论

用户报来一个 bug:「点导出按钮偶尔没反应,大概十次里有一次。」 你现在有两条路。第一条:打开导出相关的代码,盯着看,在脑子里推演哪里可能出错—— 这是大多数人(和大多数 agent)的本能,也是这个 skill 明文禁止的动作。 第二条:先造一条命令,跑一下就能让这「十次里的一次」变成「每次都失败」, 然后再开始想原因。diagnosing-bugs 就是把第二条路写成纪律的 skill: 六个相位(phase,阶段),第一相位是本体,其余五个都是机械执行。 它的原话是:没有能变红的命令,就没有资格进入第二相位。 学完这节课,你能说出六个相位各自的完成判据、它会往仓库里留什么、 修完该接哪个 skill,以及行为不对时该改哪个文件的哪一段。

1. 它在整个系统里站在哪

0001 把 22 个已发布 skill 分成三层,diagnosing-bugs纪律层—— 和 tdd、code-review、prototype 这些 model-invoked 的基本功坐在一起, 意思是它不像编排层那样交付一整条流水线,而是一项「随时掉进、修完退出」的纪律。 在 ask-matt 的地图里,它还占了另一个位置:三条 on-ramp(匝道)之一。 所谓 on-ramp,就是「工作不是从『我有个想法』开始,而是从半路并入主流程的入口」: 别人提的 bug 和需求堆积走 /triage;大到看不清的工程走 /wayfinder; 而「东西坏了」这个入口,就是 /diagnosing-bugs

它给人看的文档(docs 页)用了第三种说法:reach-for-it-anytime standalone (随时伸手就用的独立工具)——东西一坏你就进去,修复和回归测试一落地你就出来。 三种说法不矛盾:它不在主流程(想法 → 面试 → spec → 工单 → 实现)的传送带上, 但它是主流程之外最常用的岔路之一,而且它的出口可能指回系统内部—— 复盘时如果发现「这个 bug 锁不住,根子是架构没有好接缝」, 就把具体情况移交给 improve-codebase-architecture(0017 那课讲的那条扫描线)。

一句话定性 diagnosing-bugs 是一条六相位的诊断纪律: 造反馈环 → 复现并最小化 → 列假设 → 插桩 → 修复加回归测试 → 清理复盘。 它的核心赌注是:诊断的速度不取决于你多会读代码,而取决于你多早造出一条 对这个 bug 能变红的命令。所以它把最大的力气花在第一相位, 并且明文规定:连变红命令都没有的时候,禁止读代码建立理论。

分层:0001 系统地图 · 匝道定位:ask-matt/SKILL.md 的 On-ramps 一节 · standalone 说法:docs/engineering/diagnosing-bugs.md 的 Where it fits 一节

2. 调用方式与触发场景

2.1 人和 AI 都能启动

这个 skill 是 model-invoked(模型可调用):它的 SKILL.md frontmatter 里没有 disable-model-invocation 这一行, 目录下的 agents/openai.yaml 也只写了显示名和一句话简介, 没有 policy.allow_implicit_invocation: false 这种「只许人启动」的封印。 所以两条路都通:你直接敲 /diagnosing-bugs; 或者你根本不用点名——只要你对 agent 说「diagnose 一下」「debug this」, 或者报告「什么东西坏了 / 抛错 / 失败 / 变慢」,frontmatter 里 description 字段的触发条件就会命中,agent 自己会把这个 skill 加载进来。 这也解释了为什么它是纪律层里使用频率最高的一档:bug 不需要你想起它,bug 自己会触发它。

2.2 该用它、还是该去隔壁

docs 页给的范围是「难的那类」:第一眼看不穿的 bug、间歇性发作的 flake、 在两个已知良好状态之间悄悄溜进来的回归(regression:本来是对的,某次改动后变错了)。 一眼就能看懂的小毛病(明显的笔误、配置写错)直接修就行, 为一个拼写错误跑六相位是杀鸡用牛刀。真正的边界在它和邻居之间:

情境 该用 diagnosing-bugs 更该去哪
东西坏了、抛错、失败、变慢,而且一眼看不出原因 (这就是它的触发词覆盖的场景)
想用一段可丢弃的代码回答一个设计问题(状态模型对不对、UI 该长什么样) 不该(那是在探索「该建什么」,不是「建好的为什么坏」) prototype(它的 docs 页明确把「已建好的东西行为不对」推回本 skill)
merge / rebase 正在进行,git 停在冲突上 不该(冲突还没解完,谈不到行为诊断) resolving-merge-conflicts;但如果冲突解完、合并完成后行为不对了,它的 docs 页让你回本 skill
别人往 Issue tracker(工单系统)里提了一堆 bug,还没分拣 不该(先决定哪些值得修、谁该修,再谈诊断) triage;分拣出来标了 ready 的 issue 走 /implement
修好了,但复盘发现是架构没有好接缝才锁不住这个 bug 它的出口就是这里 improve-codebase-architecture(复盘阶段带着具体情况移交)
说不清现在该走哪条流程 不该 ask-matt(路由器)
历史遗迹:/diagnose 已经不存在了 这个 skill 以前叫 diagnose,CHANGELOG 记录了一次改名: 现在只能用 /diagnosing-bugs 调用,旧名字已经作废。 如果你在老笔记、老文章里看到 /diagnose,不要去仓库里找这个目录—— 找 skills/engineering/diagnosing-bugs/

触发词:SKILL.md 的 frontmatter description · 封印缺失:agents/openai.yaml · 改名记录:CHANGELOG.md · 邻居互推:docs/engineering/prototype.mddocs/engineering/resolving-merge-conflicts.md

3. 核心词汇:先把词说对

这个 skill 不搞独立词汇体系(那是 codebase-design 和 domain-modeling 的活), 但它的正文大量使用调试圈的黑话,第一次读容易卡住。 下面这张表把后面要用到的词一次性定义清楚,定义都按 SKILL.md 的用法写。

术语 平实定义 在本 skill 里的精确用法
反馈环(feedback loop) 一条你能反复运行的命令——一个脚本、一次测试调用、一条 curl——运行完会给出「通过 / 失败」的判决 整个 skill 的地基。Phase 1 的全部工作就是造出它并把它调紧;没有它,后面五个相位都不许开始
变红 / 变绿(red / green) 红 = 测试或检查失败,绿 = 通过 反馈环必须「red-capable」:对这个 bug 它有能力变红,修好后才变绿。只会说「没崩」的环不算数
紧(tight)的环 快(秒级)、结果确定(每次判决一致)、信号尖锐(断言的正是用户报的那个症状) 原文:30 秒还抖动的环约等于没有环;2 秒且确定的环是「debugging superpower」(调试超能力)
复现(repro) reproduction 的缩写:让 bug 重新发生的一套操作 Phase 2 先把环跑红确认复现的是用户报的那个病,再把这套操作削减到最小
非确定性 bug / flake 时好时坏、不是每次都出现的 bug(十次里一次那种) 目标不是「干净的复现」,而是把复现率抬高:50% 的 flake 可以调试,1% 的不行
假设(hypothesis)与可证伪(falsifiable) 对病因的猜测;可证伪 = 它能说出一个「如果猜错了就能被观察推翻」的预测 Phase 3 要求 3–5 个排好序的假设,每个都必须能写成「如果 X 是因,那么改 Y 会让 bug 消失」这种格式
插桩(instrumentation) 往代码里临时放观测点——断点、日志、计时——来看清内部实际发生了什么 Phase 4。纪律:每个探针对应 Phase 3 的一个预测,一次只改一个变量,日志必须打标记
接缝(seam) 0005 的词:不用改那处代码就能换行为的位置,也就是模块接口所在的位置 Phase 5 用它判断回归测试该写在哪:接缝太浅,测试给的是假信心;没有正确接缝,本身就是发现
回归测试(regression test) 锁住这个 bug 的一条自动化测试:它在这个 bug 上失败,修复后通过,以后 bug 复活它会再报警 Phase 5 要求在动手修之前先写好(前提是有正确接缝);Phase 2 的最小复现就是它的毛坯
二分(bisect) 在「好的」和「坏的」两个已知状态之间对半查找,定位引入 bug 的那次改动 Phase 1 的第 8 种造环法:把「在某个状态启动、检查、重复」自动化后交给 git bisect run 执行
复盘(post-mortem) 修完之后回头总结:真正的病因是什么、什么能从一开始就防止它 Phase 6。病因要写进 commit / PR 描述;预防方案若涉及架构,移交给 improve-codebase-architecture
HITL(human-in-the-loop) 回路里必须有真人参与——比如必须有人亲手在界面上点那个按钮 造环手段的第 10 种、也是最后手段:用模板脚本驱动真人操作,把结果结构化地喂回给 agent

词汇出处:SKILL.md 各相位正文 · seam 的精确定义:0005 codebase-design

4. Phase 1 — 造反馈环(这就是本体)

SKILL.md 用加粗字体写着:「This is the skill. Everything else is mechanical.」 ——这就是本体,其余全是机械执行。它的推理是:只要你有一条对这个 bug 能变红的紧反馈环, 找到病因只是时间问题,因为二分、假设检验、插桩全都只是在「消费」这个信号; 反过来,没有这个信号,盯多久代码都救不了你。 所以它要求你在这一相位投入不成比例的力气,原文连用了三个祈使句: Be aggressive. Be creative. Refuse to give up.(要凶、要巧、别放弃。)

4.1 造环的十种方法,大致按这个顺序试

# 方法 什么时候合适
1 失败测试:在够得着 bug 的任意接缝上写一条测试——单元、集成、端到端都行 首选。接缝够得着 bug 路径时,这是最便宜也最耐久的环(修好后就地变成回归测试)
2 curl / HTTP 脚本:对着跑起来的开发服务器打请求,断言响应 bug 在 HTTP 接口层,起个 dev server 比配测试框架还快
3 CLI 调用:给命令行工具一个固定输入(fixture,样本数据),把标准输出和已知正确的快照做 diff bug 在命令行工具的输出里,差异肉眼可见但手工比对太累
4 无头浏览器脚本(Playwright / Puppeteer):程序驱动真实界面,断言 DOM、控制台报错、网络请求 bug 只在真实浏览器交互里出现,比如「点导出按钮没反应」
5 重放抓取的轨迹:把真实的网络请求、负载(payload)、事件日志存到磁盘,隔离地重放进代码路径 bug 依赖一份线上才有的真实数据,本地编不出来
6 一次性 harness(临时台架):启动系统的最小子集——一个服务、依赖用 mock(仿冒实现)——一次函数调用就走到 bug 路径 系统太大起不来整体,但 bug 路径其实只穿过一小块
7 属性 / fuzz 循环:跑 1000 个随机输入,找失败模式 bug 的形状是「有时输出不对」,但你不知道什么样的输入会触发
8 二分 harness:把「在状态 X 启动、检查、重复」自动化,交给 git bisect run bug 出现在两个已知状态之间(两个 commit、两版数据、两个版本)
9 差分循环:同一份输入分别跑旧版和新版(或两套配置),diff 两份输出 你怀疑「升级之后行为变了」,想让差异自己现身
10 HITL bash 脚本(最后手段):必须真人点击时,用模板脚本驱动真人,把他的观察结构化地打印出来喂回给 agent 前面九种都够不着——比如必须在登录后的真实界面里手动操作

原文给这一节的评语是:「Build the right feedback loop, and the bug is 90% fixed.」 ——环造对了,bug 就修好了九成。第十种方法(HITL)有现成模板: scripts/hitl-loop.template.sh,复制一份改改步骤就能用, 它提供两个辅助函数——step(给真人看一句指令,等他按回车)和 capture(问真人一个问题,把回答存进变量), 最后把所有答案以 KEY=VALUE 的形式打印出来,方便 agent 解析。

4.2 有了环之后,把它调紧

原文要求把环「当产品对待」,从三个方向收紧:

判据很直白:30 秒还抖动的环只比没有环好一点点;2 秒且确定的环才叫紧——那是调试超能力。 环每快一倍,你每一轮假设检验的成本就降一半,这就是为什么「 tightening 」值得花掉整段诊断里最大块的预算。

4.3 非确定性 bug:目标不是干净复现,是抬高复现率

对十次里发作一次的 flake,别追求「一次必现」——原文给的目标是 higher reproduction rate(更高的复现率): 把触发动作循环 100 次、并行跑、加压力、收窄时间窗口、注入 sleep。 判据同样直白:50% 的 flake 可以调试,1% 的不行—— 没达到可调试的复现率之前,就继续抬。

4.4 真的造不出环怎么办:停下来,说真话

如果环真的造不出来,原文的要求是明确说出来,而不是硬往下走。 列出你试过的方法,然后向用户要三样东西之一:

  1. 能复现 bug 的那个环境的访问权限;
  2. 一份抓取到的现场物证——HAR 文件(浏览器导出的网络请求记录)、日志转储、core dump(程序崩溃时操作系统保存的内存快照)、带时间戳的屏幕录像;
  3. 许可你在生产环境加临时插桩。
硬闸门:没有变红命令,就没有 Phase 2 原文:如果你发现自己在变红命令存在之前就读代码建理论, 停下来——直接跳到假设正是这个 skill 要防的那个失败模式。 造不出环时唯一合法的动作是上面那张「要资源」清单, 不允许在环缺位的情况下进入假设阶段。这一条是整个 skill 的牙齿。

4.5 完成判据:一条你能点名的、已经跑过一次的命令

Phase 1 不是「我觉得差不多了」就能过的。完成时你必须能说出一条命令—— 脚本路径、测试调用或 curl——而且至少真的跑过一次,把调用方式和输出贴出来。 这条命令要通过四项检查:

检查项 含义(写完整)
Red-capable
(能变红)
它驱动的是真实的 bug 代码路径,断言的是用户报的那个确切症状——所以它对这个 bug 能变红、修好后能变绿。「运行没报错」不算数,它必须能抓住这只特定的虫
Deterministic
(结果确定)
每次运行判决一致;对 flake,则是按上一节把复现率钉在一个足够高的固定水平上
Fast
(够快)
以秒计,不以分钟计
Agent-runnable
(agent 能自己跑)
不需要人盯着就能运行;回路里唯一的真人只能是通过 hitl 模板脚本被驱动的那个

本节全部出自 SKILL.md 的 Phase 1 各小节 · HITL 模板:scripts/hitl-loop.template.sh

5. Phase 2 — 复现,然后最小化

相位二做两件事:把环跑红,再把复现削到最小。跑红之后先确认三件事, 原文是三条勾选项,每一条都对应一类常见翻车:

5.1 最小化:一次只砍一刀

确认变红之后,把复现削减到仍然会变红的最小场景: 输入、调用方、配置、数据、操作步骤,一次砍一个,每砍一刀重跑一次环, 只留对失败「承重的」部分——承重(load-bearing)的意思是:砍掉它,环就不红了。 完成判据:剩下的每个元素都是承重的,再砍任何一个环都会变绿。

为什么值得砍 原文给了两个理由,都连着后面的相位: 其一,最小复现缩小 Phase 3 的假设空间——剩下的活动部件越少,可怀疑的对象越少; 其二,最小复现就是 Phase 5 回归测试的毛坯——砍干净的场景直接改写成测试,几乎不用返工。 所以相位二不是洁癖,是在给相位三和相位五预付成本。 闸门:没复现且没最小化,不许往下走。

出处:SKILL.md 的 Phase 2 一节

6. Phase 3 — 先列假设,再动手验证

相位三的纪律只有三条,但每条都在反本能:

  1. 先产出 3–5 个排好序的假设,再验证其中任何一个。 只产一个假设的写法叫 single-hypothesis generation,它会把你锚定(anchor)在 第一个看似合理的念头上——后面所有观察都会被你无意识地解释成支持它。
  2. 每个假设必须可证伪,用原文的格式: 「If <X> is the cause, then <changing Y> will make the bug disappear / <changing Z> will make it worse.」 (如果 X 是病因,那么改 Y 会让 bug 消失 / 改 Z 会让它更严重。) 说不出预测的假设是一个 vibe(感觉),要么丢掉,要么磨尖到能说出预测为止。
  3. 把排好序的清单先给用户看,再开始验证。 用户手里常有能瞬间重排顺序的领域知识——原文的例子是「我们昨天刚部署了一个改动,正好碰到你列的第 3 条」, 也可能直接告诉你哪几条早就排除过了。这是便宜的检查点,省的是大块时间。 但不要阻塞在这:用户 AFK(away from keyboard,人不在)时,按你自己的排序继续。

出处:SKILL.md 的 Phase 3 一节

7. Phase 4 — 插桩:每个探针对应一个预测

相位四回答「现在往代码里放什么观测点」。总原则:每个探针必须对应 Phase 3 清单里的一个具体预测,不是「到处看看」。执行时一次只改一个变量—— 同时改两处,你就不知道是哪处让症状变了。

7.1 工具的优先顺序

  1. 调试器 / REPL 检查(环境支持就用它)。原文:One breakpoint beats ten logs.(一个断点胜过十条日志。)
  2. 定向日志:打在能区分几个假设的那些边界上——假设 A 说数据在边界前就是错的,假设 B 说在边界后才错,那就在边界两侧各打一条。
  3. 永远不要「到处打日志然后 grep」——那不叫插桩,叫捞针。

7.2 日志必须打标记

每条调试日志都带一个唯一前缀,比如 [DEBUG-a4f2]。 收尾时清理就是一条 grep:搜这个前缀,全删。 原文的对仗句值得背:「Untagged logs survive; tagged logs die.」 ——没标记的日志会活下来(混进代码库成为永久居民),打了标记的才会死。

7.3 性能分支:先测量,再动手

对性能回归(变慢,而不是算错),日志通常是错的工具。 正确的动作是先建立基线测量(baseline):计时 harness、performance.now()、 profiler(性能分析器,按函数统计耗时)、query plan(数据库查询计划), 然后在测量值上二分。原文八个字:Measure first, fix second.

出处:SKILL.md 的 Phase 4 一节

8. Phase 5 — 修复与回归测试

相位五的第一句话就是纪律:回归测试写在修复之前——但有一个前提, 存在正确的接缝。这里用的是 0005 那套词:接缝是模块接口所在的位置, 测试该站在接缝上,不该捅进内部。

8.1 什么叫「正确的接缝」

正确接缝的标准:测试在那里能按 bug 在调用现场的真实发生方式,复现出真正的 bug 模式。 反例是太浅的接缝:这个 bug 需要多个调用方按特定顺序调用才出现, 你却只写了一个单调用方的单元测试;或者触发 bug 的是一整条调用链, 单元测试根本拼不出那条链。在太浅的接缝上写「回归测试」, 给的是假信心——测试绿了,bug 换个路径照样回来。

没有正确接缝?那本身就是发现 如果整个代码库找不到一个能锁住这个 bug 的接缝, 原文说得很清楚:this itself is the finding—— 是代码库的架构在阻止这个 bug 被锁定,不是你不努力。 把它记下来,标记给相位六:复盘时这就是移交给 improve-codebase-architecture 的具体材料(那边的「加深候选」正好处理这种病)。

8.2 有正确接缝时的五步

  1. 把 Phase 2 砍干净的最小复现,改写成这个接缝上的一条失败测试。
  2. 看着它失败(红)。
  3. 动手修复。
  4. 看着它通过(绿)。
  5. 拿 Phase 1 的反馈环,对着原始的、没砍过的场景再跑一遍——最小复现只是为了分析方便,最终要证明的是用户报的原始场景真的好了。

出处:SKILL.md 的 Phase 5 一节 · 接缝词源:0005 codebase-design 的「接口就是测试面」

9. Phase 6 — 清理与复盘

宣布「修完了」之前,相位六有五条强制勾选项,少一条都不算完:

然后问复盘的核心问题:「什么能从一开始就防止这个 bug?」 如果答案涉及架构改动——没有好的测试接缝、调用方缠成一团、藏着看不见的耦合—— 就带着具体情况移交给 /improve-codebase-architecture。 原文特意规定了时点:在修复落地之后提这个建议,而不是之前—— 因为修完之后你掌握的病因信息,比刚动手时完整得多。

六相位总览(每个闸门都是硬性的)

Phase 1  造反馈环        闸门:能点名一条已跑过、red-capable 的命令
Phase 2  复现+最小化     闸门:复现的是用户的病;每个剩余元素都承重
Phase 3  列假设          纪律:3–5 个、排序、可证伪、先给用户看
Phase 4  插桩            纪律:探针对应预测、一次一变量、日志打标记
Phase 5  修复+回归测试   纪律:测试先于修复;无正确接缝=发现本身
Phase 6  清理+复盘       闸门:五条勾选;病因进 commit;架构问题移交

出处:SKILL.md 的 Phase 6 一节

10. 副作用、下一步、和谁相连

10.1 它会读什么、写/留什么

动作 落在哪 说明
探索代码库时读 CONTEXT.md(如果存在)和相关区域的 ADR 只读 SKILL.md 开篇的要求:先建立相关模块的心智模型再动手。这两个文件是 grill-with-docs / domain-modeling 那条线维护的,本 skill 只消费
Phase 1 的反馈环(脚本、curl、临时测试) 多数是临时的 若是失败测试形态,Phase 5 会把它转正为回归测试;其余形态在 Phase 6 删除或挪到调试专用位置
[DEBUG-...] 标记的日志 临时,必须死 Phase 6 用前缀 grep 全删;漏网的就是 bug
回归测试 留在仓库的测试套件里 Phase 2 的最小复现改写而来,写在正确接缝上
修复本身 改业务代码 0001 的副作用矩阵把「业务代码 + 测试 + commit」归给 implement 和 diagnosing-bugs 的修复路径
病因结论 commit / PR 描述 Phase 6 勾选清单的最后一项
Issue tracker、CONTEXT.md / ADR 不写 它不碰工单系统,也不直接维护领域词汇——复盘发现架构问题时是「移交」,由 improve-codebase-architecture 那条线决定要不要落 ADR

10.2 修完之后,下一步去哪

  1. 常规出口:修复和回归测试落地、commit 里写明病因 → 回到你被打断前的工作。如果 bug 来自一张 issue,按你们仓库的常规流程关掉它。
  2. 架构出口:复盘的「什么能防止它」指向架构(没有好接缝、调用方纠缠、隐藏耦合)→ 带具体情况进 /improve-codebase-architecture(0017)。那边扫描出的加深候选可以生成新想法,并入主流程的 /grill-with-docs
  3. 预防出口:想把「防住这类 bug」变成一项正式工作(而不是一次修复)→ 当新想法处理,走主流程。

10.3 依赖谁、被谁依赖

            ┌──────────────────────────┐
            │   CONTEXT.md / ADR       │  grill-with-docs + domain-modeling 维护
            └────────────┬─────────────┘
                         │ 读(建立模块心智模型)
                         ▼
   触发词命中 ──► ┌──────────────────┐      复盘移交(无好接缝 / 纠缠 / 隐藏耦合)
   或 /diagnosing-bugs │ diagnosing-bugs │ ──────────────────────────► improve-codebase-architecture
                  └──────┬───────────┘
                         │ 用词与纪律
            ┌────────────┼────────────────┐
            ▼            ▼                ▼
      codebase-design   tdd           邻居互推:
      (seam 词汇,     (回归测试就是   prototype(设计问题 vs 东西坏了)
       Phase 5 判据)    红绿纪律)     resolving-merge-conflicts(合完行为不对 → 来这里)

读 CONTEXT/ADR 与移交:SKILL.md 开篇与 Phase 6 · 邻居互推:prototype / resolving-merge-conflicts 的 docs 页 · 矩阵:0001 系统地图

11. 行为不对时,去改哪个文件的哪一段

这个 skill 的全部行为几乎都在一个文件里:skills/engineering/diagnosing-bugs/SKILL.md, 134 行,六个相位各一节。改行为之前先想清楚你要改的是「触发」「纪律」还是「工具」:

症状 去改哪 不要误改
agent 没造环就开始讲病因理论 SKILL.md Phase 1 的「Completion criterion」一节,特别是「No red-capable command, no Phase 2」那句闸门 Phase 3 的假设格式(它是下一道闸门,管不了没环就起跑)
环选得不合适,或顺序总乱(上来就写 HITL 脚本) Phase 1「Ways to construct one」那张十项清单及其「roughly this order」的顺序 hitl 模板脚本本身(它只是第 10 项的载体)
对间歇 bug 轻易宣布「无法复现」然后放弃 Phase 1「Non-deterministic bugs」一节(目标是抬高复现率:50% 可调,1% 不行) 「When you genuinely cannot build a loop」一节(那是真的造不出环时的协议,不是 flake 的退路)
跳过最小化,带着几十行噪音场景进假设 Phase 2 的「Minimise」小节:一次砍一个、每砍重跑、承重判据 Phase 5 的五步(测试毛坯来自 Phase 2,不在那里补砍)
只抛一个假设就开测,或者假设说不出来怎么验证 Phase 3:3–5 个排序、可证伪格式、先给用户看但不阻塞 Phase 4 的工具顺序(工具没问题,是没东西可测)
满屏无标记日志、修完还留在代码里 Phase 4 的 [DEBUG-...] 打标规则 + Phase 6 清理勾选项 「Never log everything and grep」一句(那是方法问题;这是卫生问题)
性能问题靠加日志猜 Phase 4 的「Perf branch」:先基线测量再二分 Phase 1 的环清单(性能 bug 同样需要环,但插桩工具不同)
在太浅的接缝上写「回归测试」,bug 换条路又回来 Phase 5 的「correct seam」定义和两个反例 tdd 的测试风格细则(问题不在风格,在接缝位置)
修完 commit 里没写病因,下一个调试者重新猜 Phase 6 勾选清单最后一项 仓库的 commit message 风格约定(这是内容缺失,不是格式问题)
触发太灵(小笔误也启动六相位)或太钝(报告了错误没反应) SKILL.md frontmatter 的 description 字段:「Use when…」那段触发词 plugin.json(它只是发布清单,不管触发灵敏度)
HITL 脚本想改交互方式 scripts/hitl-loop.template.sh 的 step / capture 两个辅助函数 SKILL.md 的 Phase 1 正文(它只规定「最后手段用模板」,不规定模板长什么样)
Codex 侧的显示名或一句话简介不对 agents/openai.yaml 的 display_name / short_description SKILL.md 的 H1 标题(两边各管各的展示)

全部指向 SKILL.mdhitl-loop.template.shagents/openai.yaml 三处原文

12. 检索练习

先别往回翻,凭记忆答。选项的长度刻意对齐,不会从版式泄题。判据以本课和 SKILL.md 的原文为准。

自测(立即反馈)

1. Phase 1 的「red-capable」要求反馈环做到什么?
2. 真的造不出反馈环时,skill 规定的动作是?
3. 对非确定性 bug(flake),Phase 1 给的目标是?
4. Phase 2「最小化」的两条收益是?
5. 一个合格的 Phase 3 假设必须满足什么?
6. Phase 4 对调试日志的卫生纪律是?
7. 对性能回归,Phase 4 的「perf branch」要求?
8. Phase 5 找不到能锁住 bug 的正确接缝时,应当?
额外提取练习(无选项) 合上本页,默写六个相位的名字和每个相位的「闸门」(什么条件不满足就不许往下走)。 然后挑一个你最近真实遇到的 bug,回答三个问题: 当时你的第一条 red-capable 命令是什么、花多久造出来的; 如果当时造不出来,按 4.4 节你该向谁要什么; 修完后 commit 里写了病因假设吗。不要求真的重跑诊断,只要求会问问题。

13. 下一课与一手材料

本课主一手材料(请打开原文读,不要只背本页摘要):

速查页(本课同步): reference/diagnosing-bugs.html

导航: 上一课 0014 triage (另一条 on-ramp:别人的 bug 先分拣;分拣排期之外、眼下就烧起来的那个,才进本课的诊断循环)。 相邻课程: 0012 tdd(回归测试的红绿纪律)、 0005 codebase-design(接缝词汇)、 0008 prototype(设计问题 vs 东西坏了)、 0017 improve-codebase-architecture(复盘移交的去处)、 0018 resolving-merge-conflicts(合完代码行为不对时的下一站是本课)。

建议下一课(0016): 0016 wayfinder——三条 on-ramp 里最重的一条: 工程大到或模糊到一个会话装不下时,不交付代码,先在 Issue tracker 上画一张决策地图。 和本课的交接点很自然:diagnosing-bugs 处理「目的地清楚、路断了」, wayfinder 处理「连路在哪都看不见」。

老师就在会话里。 对本课任何一个判据的边界有疑问——比如环到底多快才算「fast」、 什么算「正确接缝」的灰色地带、Phase 3 的清单给用户看过之后要不要等回复、 flake 的复现率抬到什么程度才够——直接在对话里问。 回答会回到 SKILL.md / hitl-loop.template.sh / docs 页的原文,不会临场编造。 做完检索练习后,回复「练习结果 / 哪里卡住 / 开 0016 或先补 0014」,我们安排下一课。