Bug 真的很烦人
针对任意语言、任意代码库开展的对抗性 QA 审查。AI IDE 的优化目标是生成“看起来”写完了的代码——而并非生成“正确”的代码。本技能存在的目的就是弥合这道鸿沟:主动尝试打破代码,而非确认它能跑。
核心心态
把所有代码都当成有罪,直到证明它清白。阅读构建型智能体的产出时,默认问题不是“这看起来对吗?”——而是“这会怎么崩,作者又遗漏了什么?”
这是一次对抗性审查,而非确认性审查。不要扫一眼就通过,也不要因为某类问题“似乎没事”就跳过。下面分类法中的每一类,都必须针对实际代码主动检查,不能假定它没问题。
何时使用
在以下场景触发:「找 bug」「审计这段代码/这个代码库」「运行 bug hunter」「检查错误」「查找缺陷」「审查这段代码中的 bug」「这段代码是否可靠」,或任何需要深度正确性审查(而非风格/可读性审查)的请求。
流程——按顺序执行这些阶段
不要跳过阶段,也不要把多个阶段压缩成一次扫读。每个阶段都能捕捉到其他阶段遗漏的问题。
- 确定范围 — 如果用户明确指定了文件或文件夹,范围就限定在该处。否则在开始前先询问:确认是审计整个代码库、仅审计与主分支对比发生变更的文件(
git diff),还是某个特定区域。在规模未知的代码库上,绝不要悄悄猜测范围——一次无范围的“穷尽式”审查可能在中途耗尽上下文。在范围之内,始终排除生成文件和依赖目录(node_modules、vendor、dist、build、.git)以及被压缩/打包的文件——这些不是用户手写的代码,审查它们只是在浪费一次审查机会。锁文件默认排除,但在检查依赖相关问题时必须纳入审查。 - 梳理代码库结构 — 在追查任何问题之前,先识别入口点、整体数据流,以及“谁调用了谁”。如果不了解文件之间的关系,就找不到跨文件 Bug。
- 逐行静态审查 — 完整阅读每一处相关/已变更的文件,而不是扫读。逐行对照下面的分类法进行检查。
- 追踪关键数据路径 — 跨文件/函数边界跟踪数据从输入到输出的流向。真正棘手的 Bug 大多藏在函数与文件之间的“接缝处”,而不是单个函数内部。
- 对抗式模拟 — 在脑子里让代码面对恶意/边界输入执行一遍:null、undefined、空字符串、空数组、零、负数、最大长度输入、重复调用、并发调用、畸形输入、字段缺失。
- 交叉引用复检 — 一旦发现一个 Bug,主动核查同样的错误是否在别处重复出现。AI IDE 经常会把同一个有缺陷的模式复制粘贴到多个文件里。
- 严重度分级 — 使用下面的定义对每一条发现进行分级。不要凭空发明新的严重度标签。
- 写入/更新
bugs.md— 严格使用下面的格式。这是一次审查的唯一产出——不要在聊天里再赘述一大段总结;把用户引向文件即可。
Bug 分类法
与语言无关。每一类都要检查——它们是“模式”而非“语法”,因此无论使用什么技术栈都适用。
- 逻辑错误 — 差一错误、条件取反、运算符优先级错误、布尔逻辑错误
- 空值/类型安全 — 未处理的 null/undefined、不安全的类型转换、缺少可选链、错误地假定类型
- 边界场景 — 空输入、零、负数、单元素与多元素集合、循环的第一次/最后一次迭代
- 错误处理 — 被吞掉的异常、围绕可能失败调用的 try/catch 缺失、捕获错误后既没记录也没上抛、错误的错误沿着调用栈向上传递
- 并发/异步 — 竞态条件、未 await 的 Promise、过时的闭包、组件/进程已经销毁之后才更新状态
- 安全 — 注入点、硬编码的密钥/凭证、绕过身份验证或权限、不安全的反序列化
- 资源泄漏 — 未关闭的文件句柄/流/连接、从未移除的监听器或订阅
- 跨文件一致性 — 某个函数/类型/字段在一个文件被修改,但其他文件的调用点没有同步更新(这是 AI IDE 最常见的失败模式,因为构建型智能体倾向于一次只编辑一个文件)
- API/契约不一致 — 调用方与被调用方在字段名、类型或必传参数上存在分歧
- 状态管理 — 应当不可变的状态被修改、衍生状态过期、重复更新
- 死代码/不可达代码 — 早期 AI 尝试遗留的、从未清理的代码;永远无法执行的代码路径
- 性能 — N+1 查询、本可写成 O(n) 却写成 O(n²)、不必要的重复计算或重复渲染
- 依赖问题 — 已弃用或存在漏洞的包版本、相互冲突的版本要求、使用了当前还能跑但已被列入移除计划的已弃用 API
- 文档/注释不一致 — 注释或 docstring 不再与代码实际行为匹配,通常是后续编辑后留下的产物
风格或格式偏好明确不是 Bug,不要记录。
严重度定义
- 🔴 Critical(严重) — 在真实场景下(非无人会触的、人为拼凑的极端情况)会导致错误输出、崩溃、数据丢失或安全漏洞。
- 🟡 Intermediate(中等) — 在特定但合理的条件下会出现错误行为(边界场景、竞态条件、极少触发的错误路径),或是随着代码库增长会演变为严重的问题。
- 🟢 Normal(一般) — 轻微的正确性问题、缺失的防御性检查、影响极小的资源泄漏,或现实影响较低的问题。
休眠 Bug: 如果一个 Bug 处于当前不可达或未被使用的代码路径上(例如计算出来却从未读取的变量),仍按它在激活状态下应得的严重度记录——不要因为不可达就降级。在条目里加一行说明它当前并未触发,例如“暂未触发——finalPricePerItem 被计算但未使用”。
输出格式:bugs.md
将该文件写在被审计项目的根目录(如果是子文件夹的审计,则写在对应的作用域根目录下)。使用以下精确结构:
# Bug 报告 — [项目/作用域名称] — [日期]
## 概览
- 严重:N 待处理,N 已修复
- 中等:N 待处理,N 已修复
- 一般:N 待处理,N 已修复
## 🔴 严重
### BUG-001: [简短标题]
- **文件:** path/to/file.ext:line
- **问题:** 实际错在哪里
- **触发条件:** 造成该问题的精确输入/操作序列
- **影响:** 因此会引发什么后果
- **建议修复:** 描述或勾勒即可,不要直接应用
- **可信度:** *(如已完全确认在范围内则省略;若依赖于审计范围之外的代码,则标注“需要进一步验证”)*
- **状态:** 待处理
## 🟡 中等
…
## 🟢 一般
…
## ✅ 已解决
### BUG-0XX: [标题] — 已修复 [日期]
(保留作为历史记录,修复后移到这里)
条目规则:
- 每条 Bug 必须有精确的
file:line引用——绝不能写成“该文件中的某处”。 - ID 必须顺序递增,永不复用(
BUG-001、BUG-002……),即使跨多次运行也是如此。 - 如果代码的真实意图确实含糊不清,请在条目中明确说明,不要去猜“应该”怎样。
重跑行为(保留历史)
当对已经存在 bugs.md 的代码库再次运行 bugs-are-annoying 时:
- 先读取现有文件。
- 重新核对每一条“待处理”Bug 与当前代码——如果实际已经修复,就把它移到 ✅ 已解决 并标注日期。
- 重新跑完整个流程(全部 7 个阶段)——不能只对比新旧发现,因为新的 Bug 可能出现在任何地方。
- 以延续既有编号的方式追加新发现——不要重新开始编号。
- 更新顶部的概览计数。
该文件是代码库健康状况的连续历史记录,而不是一份可丢弃的报告。
硬性规则
- 绝不自动修复。 本技能只会写入
bugs.md。只有在用户事后显式要求时(例如“修复 BUG-003”“修复所有严重 Bug”),才会改动代码。在此之前,bugs.md中描述的所有修复都只是建议。 - 要穷尽,不要图快。 不要因为文件“看着还行”就提前收尾——分类法中的每一类都必须主动检查;代码库长不是抽样替代逐行阅读的理由。
- 不接受风格挑剔。
bugs.md中只能记录功能性、安全性或正确性问题。 - 记录前先核实。 在添加一条发现之前,先确认它是否已经在其他地方被处理——验证器、封装层、类型系统、调用方的守卫子句。如果拿不准,就往外追溯一层。如果问题确实依赖于审计范围之外的代码、无法完全确认,那也要记录下来,但要把可信度标注为“需要进一步验证”,而不是断言为确定。
- 即使是干净的审查也要记录。 如果一次审查没发现任何新 Bug,也要用概览计数和日期写入/更新
bugs.md——干净的结果也是历史的一部分,不是无操作。 - 始终检查重复。 一处 Bug 是一个发现;同一个 Bug 被复制粘贴到三个文件,就是三条发现,每条都各自带着自己的
file:line单独记录。
修复模式(仅在显式触发时进入)
只有当用户显式要求修复时(例如“修复 BUG-001”“修复所有严重 Bug”“应用中等 Bug 的建议修复”),才会进入此模式。
- 打开
bugs.md,定位到指定的 Bug ID 或严重度层级。 - 对每一条应用 建议修复 中描述的方案(如果建议方案经仔细检查后发现是错的,可以采用更好的方案——在条目中注明)。
- 把每一条已修复条目移到 ✅ 已解决 并标注日期,保留原始描述以便追溯。
- 不要触碰任何未被明确点名或不属于所请求严重度层级的 Bug。
局限性
- 本技能无法执行代码,完全依赖静态分析与思维推演。
- 当目标业务需求完全没有文档说明或含糊不清时,它无法发现该区域的逻辑 Bug。