修复缺陷 是做什么的?
找到根本原因,不要修复症状。无论是应用还是库,此规则不变。
该技能仅在修复变绿后且用户选择或之前已要求证明时加载 prove-it。它不会覆盖 CopilotKit 调试技能。
何时使用
当失败发生时加载:测试失败、CI 变红、意外结果、堆栈跟踪,或用户表示“出了故障”/“修复这个”。
跳过没有失败的绿地功能。跳过文档和纯类型编辑。
硬性规则
- 在从日志中定位原因之前,不得进行修复。 不允许“试几样东西”。
- 在热路径上至少提出三个假设,且分布在不同的位置(我们的函数、调用者、依赖)。
- 在每个假设路径的前后添加调试日志。如果涉及的第三方包有调试模式,则在本轮排查中启用它。
- 当原因确定后:删除本技能添加的所有日志,并关闭第三方调试。 然后编写失败的测试。最后进行修复。日志绝不允许进入修复代码。
- 不得跳过、删除或禁用失败的测试来使测试变绿。 不得吞没错误(
|| true、空的 catch)。不得提高超时时间以隐藏竞态条件。 - 修复根本原因,同时修复该文件/附近模块中的相同模式。不得进行额外的重构。
- 除非用户选择或之前已要求证明此修复,否则不得加载
prove-it。 绿色测试套件不是证据。默认跳过。
热路径
从失败的测试(或用户操作)沿着调用栈向下直到错误行,以及调用和被调用的函数。
从那里开始。逐个排除。 缩小范围。只有当该路径上的所有假设都不成立时才扩大范围。
流程
流程 1:阅读
- 阅读完整的错误信息、堆栈信息或 CI 日志。每一行。
- 如果有失败测试,阅读其主体。
- 如果存在可能引入问题的差异,阅读该差异。
- 暂不进行编辑。
流程 2:提出假设
- 绘制热路径(文件和函数)。
- 编写至少三个假设:“错误是由 X 引起的,因为 Y。”它们必须分布在热路径上(不能针对同一行代码猜三次)。
- 如果不能写出三个假设,说明阅读不够。回到流程 1。
流程 3:添加工具
- 对于每个假设中提到的第三方包:查阅其文档,看是否有调试标志或环境变量。如果有,启用它。
- 在每个假设分支的前后添加临时日志(输入进来,输出出去)。使用仓库现有的日志记录器或
console。不要添加日志包。 - 复现一次。读取日志。划掉日志中排除的假设。
- 如果三个假设都不成立,将热路径向外扩展一步(下一个调用者或下一个依赖)。提出新假设。添加新日志。再次排除。
- 当确定了一个原因时:删除本技能添加的所有日志。 关闭第三方调试。确认工作目录中没有残留此类日志。
流程 4:编写失败测试,然后修复
- 编写一个因该缺陷而失败的测试(红色)。使用独立的期望值。不要跳过此步骤。
- 运行它。它必须因你命名的原因而失败。
- 只修复根本原因。同时修复该文件/附近模块中相同错误的副本。
- 运行该测试(绿色)。然后运行此包/此文件套件的现有测试。
- 如果出现新的失败,将其视为一个新缺陷。从流程 1 开始。不要在另一个新故障之上推送第一个修复。
流程 5:可选的 prove-it
- 在此包的测试套件变绿且日志已删除后,询问一次。选项:加载
prove-it,或跳过。默认跳过。 - 等待。在用户选择之前不要加载
prove-it。 - 如果用户已在此对话中要求证明此修复,则跳过询问。直接加载
prove-it。 - 如果跳过: 说明此次修复未经过用户路径的验证。不要将绿色套件视为证据。进入流程 6。
- 如果用户选择
prove-it:加载prove-it并遵循其指示。然后进入流程 6。 - 不要在该技能内部点击应用、调用 curl 或编写验证报告。此项工作属于
prove-it。
流程 6:停止
跳过或 prove-it 返回后,停止。
决策树
- 发现失败 → 流程 1 → 2 → 3 → 4 → 5 → 6。
- 无法复现 → 停止。说明未能复现。请求步骤。不要猜测性修复。
- 日志排除所有热路径假设 → 扩大范围,再次从流程 2 开始。
- 修复后测试仍为红色 → 原因错误。回退修复。回到流程 2。
- 下一个函数中存在相同模式 → 在此次更改中一并修复(硬性规则 6)。
- 套件变绿,尚未询问证明 → 流程 5(询问,默认跳过)。
- 用户已要求证明此修复 → 加载
prove-it,然后进入流程 6。
危险信号
| 信号 | 含义 | 应做措施 |
|---|---|---|
| 一个假设,然后打补丁 | 猜测 | 流程 2。在热路径上提出三个假设。 |
| 没有日志的修复 | 未排除 | 流程 3。 |
差异中仍有调试用的 console.log | 日志未清除 | 流程 3 步骤 5。然后写测试。 |
第三方 DEBUG=* 被遗留 | 排查标志被发布 | 与日志一起关闭。 |
| 跳过/删除失败的测试 | 处理症状 | 修复代码。 |
| ` | true` / 空的 catch | |
| 更长的超时时间 | 隐藏的竞态条件 | 修复竞态条件。 |
| 修复的同时进行重构 | 附带修改 | 仅限根本原因 + 相同模式。 |
| 整个 monorepo 的测试运行 | 未经要求 | 仅此包/此套件。 |
| “在我机器上能跑” | 环境不同 | 使用与失败相同的命令/相同条件。 |
未经询问加载 prove-it | 未经要求的验证 | 流程 5。默认跳过。 |
| 在该技能内部进行迷你浏览器通过验证 | 重复 prove-it | 在用户选择后加载 prove-it。 |
| “测试通过了,所以算验证了” | 将绿色套件视为证据 | 流程 5。跳过意味着未验证。 |
错误处理
- 未能复现: 说明情况。列出运行了哪些操作。不要凭空编造修复方案。
- 依赖没有调试模式: 跳过该步骤。仍然在热路径上为我们的代码添加日志。
- 日志量很大: 只记录假设的分支,而不是文件中的每一行。
- 尚无法编写失败测试(没有测试框架):使用一个包含断言的单文件脚本作为临时方案。仍然要让它变红,然后修复,再变绿。
- 修复导致另一个测试失败: 对该失败执行流程 1。在级联问题解决之前不要推送第一个修复。
prove-it在此代理上缺失: 说明情况。提供跳过的选项。不要在此技能内发明第二个验证流程。- 用户选择
prove-it,然后在其中选择跳过: 修复未经验证。进入流程 6。
该技能不放置辅助工具或编写文档。验证仅在流程 5 中执行。