verification-before-completion 的本质是:AI 代理在声明“测试通过”、“Bug 修复”或“配置生效”前,必须运行并审阅当次的验证命令输出。这条“铁律”根除了“应该没问题了”这类基于猜测的完成声明,将其转变为基于证据的客观流程。无论是在 finishing-a-development-branch 收尾工作,还是在 test-driven-development 的红绿循环中,它都是非可选的质量门禁。
verification-before-completion:AI 代理声明完成前必须拿出新鲜证据
在 AI 编码代理的辅助下,一句轻率的“应该能工作了”可能比一个明确的 bug 更具破坏性。它用一种未经证实的乐观情绪冒充了事实,而接收方——无论是人类开发者还是下游的代理——可能会基于这个“声明”继续推进,直到错误暴露,导致时间浪费、信任破裂和严重返工。SuperPowers 的 verification-before-completion 技能旨在彻底解决这个问题,它将“完成”重新定义为一个客观的、可验证的事件。
核心铁律:没有新鲜证据,禁止声明
该技能的核心原则凝结为一条不可违背的“铁律”:
NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE
这句话的字面意义就是其全部精神:如果你在当前这条消息里没有运行验证命令并查看其输出,你就不能声称验证通过了。即使上一次运行是成功的,或者你基于代码逻辑“有十足把握”,这些都不能替代当次的、可观察的证据。任何试图绕过这条规则的行为,本质上都是在用猜测替代事实。
五步门禁函数:一个非可选的验证流程
在作出任何关于工作状态的正面声明(如“测试通过”、“构建成功”、“需求已满足”)或表达满意(如“搞定”、“完美”)之前,必须严格执行以下五步“门禁函数”:
- 识别:找出能直接证明你声明的具体命令。例如,声明“测试通过”对应项目的测试命令;声明“Bug 修好了”对应能复现原始症状的命令。
- 运行:立即、完整地执行该命令。必须是“新鲜”的运行,不能依赖缓存或历史结果。
- 阅读:查看完整的命令输出。检查退出码(如
exit 0),统计失败用例数量,而不仅仅是扫描输出中有没有“ERROR”字样。 - 验证:将命令输出与你的声明进行比对。
- 如果输出不确认你的声明:你必须基于证据如实说明当前状态。例如:“当前有 2 个测试失败,原因是
assertEqual期望值不符。” - 如果输出确认你的声明:你获得了支持声明的证据。
- 如果输出不确认你的声明:你必须基于证据如实说明当前状态。例如:“当前有 2 个测试失败,原因是
- 声明:只有到这一步,你才能做出“完成”或“通过”的陈述,并且必须附上证据摘要(例如“运行
npm test,输出显示 34/34 测试通过”)。
跳过其中任何一步,都意味着你不是在“验证”,而是在“撒谎”。
常见失败模式与所需证据
技能源文件提供了一个清晰的“常见失败”表,指出了不同声明所对应的充分证据和常见的不充分证据:
| 你的声明 | 需要的证据 | 以下证据 不充分 |
|---|---|---|
| 测试通过 | 测试命令输出显示 0 失败 | 之前的运行结果,“应该能过” |
| Linter 干净 | Linter 输出显示 0 错误 | 部分检查,外推结论 |
| 构建成功 | 构建命令退出码为 0 | “Linter 通过了,日志看起来正常” |
| Bug 修复 | 测试原始症状的命令通过了 | 代码改了,假设已修复 |
| 回归测试有效 | 经历了完整的“红-绿”验证周期 | 测试只通过了一次 |
| 代理完成任务 | 版本控制系统(VCS)的 diff 显示了预期的变更 | 代理报告“成功” |
| 需求已满足 | 逐项核对计划清单 | “测试通过,阶段完成” |
这个表格揭示了一个关键点:操作状态(如 200 OK、exit 0)不等于操作意图达成。验证必须精确对应声明的核心。
红旗信号:看到这些词,必须立即停止并验证
当 AI 代理或开发者在沟通中使用以下措辞时,这些是危险信号,必须触发强制验证流程,而不是继续表达乐观情绪:
- 使用“应该”、“可能”、“看起来”等不确定性词语。
- 在验证前就表达满意(如“太好了!”、“完美!”、“搞定了!”)。
- 准备提交代码、创建 PR 或宣布任务完成,但没有提及验证。
- 直接信任子代理(subagent)返回的“成功”报告。
- 依赖部分验证进行外推(如“Linter 过了,所以编译应该没问题”)。
- 认为“就这一次例外”。
- 因为疲劳而想尽快结束工作。
- 任何暗示成功但未运行当次验证的表述。
防止合理化:借口与事实
技能源文件列出了常见的合理化借口,并给出了直接的现实反驳,帮助开发者和代理建立正确的肌肉记忆:
| 借口 | 现实 |
|---|---|
| “应该能工作了” | 去 运行 验证命令。 |
| “我很有把握” | 把握 ≠ 证据。 |
| “就这一次例外” | 没有例外。 |
| “Linter 通过了” | Linter ≠ 编译器/测试套件。 |
| “代理说成功了” | 必须独立验证。 |
| “我太累了” | 疲劳不是豁免理由。 |
| “部分检查足够了” | 部分证明不了什么。 |
| “用了不同的词,所以规则不适用” | 精神优先于字面。 |
验证在 SuperPowers 工作流中的闭环位置
verification-before-completion 并非孤立的技能,它是 SuperPowers 质量门禁闭环中承上启下的关键一环。
- 在 finishing-a-development-branch 技能中,流程的第一步就是“验证测试”。在代理向你展示合并或创建 PR 的选项之前,它必须先运行项目的测试套件并确认通过。如果测试失败,整个收尾流程将立即停止。这确保了只有已验证的代码才被集成。
- 在 test-driven-development 技能中,“验证 RED”和“验证 GREEN”是红-绿-重构(RED-GREEN-REFACTOR)循环中强制性的两个环节。开发者必须亲眼看到测试因为功能缺失而失败(RED),并亲眼看到测试因为实现了最小代码而通过(GREEN)。没有这两个亲眼见证的验证,整个 TDD 流程就不成立。
真实案例:为什么“状态 200”不足以证明配置切换成功
一份来自 2025-11-28 的用户反馈改进计划文档,揭示了一个深刻的验证缺口:配置变更验证缺口。
具体场景是:一个子代理在测试“OpenAI 集成”。它设置了 OPENAI_API_KEY 环境变量,向 OpenAI API 发起请求并收到了 status 200 的响应。于是它报告“OpenAI 集成正常工作”。然而,问题在于响应体中包含 "model": "claude-sonnet-4-20250514",表明它实际上仍在使用 Anthropic 的模型,根本没有切换成功。
- 失败原因:代理只验证了“操作成功”(收到 200 状态码),但没有验证“操作实现了预期意图”(使用正确的模型)。
- 根本问题:原来的检查点关注操作是否成功,但并未强制要求验证变更的结果是否符合意图。
- 解决方案:对于配置变更、功能开关、环境切换等操作,必须验证操作的输出(如响应体中的模型名称、日志中的环境标识、功能行为是否激活)而不仅仅是操作的状态(如 200 OK、无报错)。
这个案例完美诠释了“铁律”:status 200 是证据,但它证明的是“请求成功”,而不是“请求使用了正确的模型配置”。你需要的是能直接证明后者的新鲜证据。
子代理场景:验证必须独立
当你将任务委派给子代理时,verification-before-completion 的原则变得尤为重要。
- 子代理返回的“成功”报告,不能直接转述为你自己的“完成”声明。
- 子代理可能在部分失败的情况下仍报告成功,也可能做出了与预期不符的改动。
- 正确流程:子代理完成任务 → 你检查版本控制系统的 diff,查看实际变动 → 亲自验证变动符合预期 → 你才能基于自己核实的证据,对上游说“已完成”。为子代理的成功背书,意味着你要为没有亲自核实的事情负责。
总结:将验证变成习惯
verification-before-completion 将“完成”从一个主观判断转变为一个客观的、可验证的事件。它要求代理和开发者培养证据优先的习惯。虽然这可能会增加几秒或几分钟的验证时间,但相比于因错误声明而导致的返工、信任丧失和生产事故,这是微不足道的投入。记住:证据在前,声明在后。这是非谈判的底线。
FAQ
Q: 每次声明前都验证,会不会大大拖慢开发速度? A: 大部分验证命令(如运行相关测试、检查构建)只需几秒到几分钟。与因跳过验证导致的返工成本相比,这个时间投入非常划算。更重要的是,当“验证”成为习惯,它本身就是“完成”定义的一部分,而非额外步骤。
Q: 如果验证命令耗时很长(比如完整的端到端测试套件),也必须每次都跑吗? A: 你必须运行能证明你具体声明的那个命令。如果你声明的是“单元测试通过”,运行单元测试套件即可,无需运行完整的 E2E 测试。关键在于你的声明与你的验证必须精确对应,不能用更弱的验证为更强的声明背书。
Q: 如果验证显示没有完全通过,应该怎么办? A: 如实报告当前状态。例如:“当前有 3 个测试失败,错误是[具体信息]。问题原因是[X],下一步计划[Y]。” 这远比假装完成、等待他人发现问题要诚实和高效得多。