返回文章

verification-before-completion:「做完了」这句话,要有刚跑出来的证据

这是 skill 深挖系列的第二十一篇,也是最后的一篇。前面两个系列——五层约束、五个阶段——讲的是系统结构;这个系列逐个拆构成系统的 21 份 skill,每篇只讲一份:它为什么存在,具体怎么干。母篇是《从能跑到可交付:一套前端 AI 协作系统的设计笔记 》。我用的系统是开源的 frontend-harness-cli ,文中所有规则都能在仓库里找到对应实现。

这份 skill 是通用组的最后一份:verification-before-completion。确认阶段 那一篇里提过它一句——把确认的原则缩小到单个任务。这篇把它展开讲完,系列也在它这里收尾。这个安排不是巧合:它是整套系统里最小的一条规则,小到只管一句话怎么说出口。

「做完了,测试都通过了」

先看一个具体的场景。

开发者交活,报告写得很顺:「任务完成,全部测试通过。」数字、语气、格式,样样齐全。

这句话有一个地方不对:测试确实跑过,也是真的全绿——但那是在最后三次修改之前跑的。跑完测试之后,它又改了一个组件的判断条件,顺手调整了两处文案。三次修改都不大,看起来不可能影响测试。报告引用的是那次全绿的结果。

错在哪?它报告的不是假话,是旧话。那次全绿证明的是修改之前的代码能过测试;修改之后的代码,从来没有被任何东西验证过。报告的流畅和自信都是真的,只是它描述的那个代码版本已经不存在了。

而且这种错很难被发现。报告读起来和一份合格的回执一模一样:有结论,有数字。接手的人要识破它,得去翻命令历史、对时间戳——成本全在核查的一方。说这句话的人,成本是零。

这份 skill 管的就是这一瞬间:一句「做完了」在说出口之前,要过一道门。

这份 skill 是什么:一条管「说法」的规则

verification-before-completion 不是给某个角色的,它对所有角色生效:developer、evaluator、planner,谁在开口下结论,谁就受它约束。

它管的范围出乎意料地宽。不只是「做完了」「通过了」这类成功结论——失败结论同样要证据:说「这个测试挂了」,也要有这一次跑出来的输出,而不是「上次跑的时候是挂的」。技能原文的第一句话是整套规则的纲:

没有新鲜证据的结论只是猜测。代码一改,证据就过期。

注意「过期」这个词的选择。证据不是真假问题,是时效问题:它曾经是真的,代码一改,它证明的对象就换了。这条规则不假设谁在撒谎——上一节的场景里没人撒谎——它假设的是:任何结论和它所依据的证据之间,只要隔着一次代码修改,这条结论就自动失效,不需要谁来指控。

技能的适用点也写得很明确:就在 task-done、remediate-done 之前,就在写评判证据之前,就在向编排者汇报之前。四个点位,全是「一句话要变成记录」的时刻。

它怎么干:五步门禁

具体流程只有五步,技能里叫它「门禁」(The gate)。在下任何成功或失败的结论之前:

  1. **说出能证明这个结论的命令。**注意顺序:先有命令,后有结论。「测试通过了」对应的命令是什么,得说得出来;说不出来的结论,本身就是猜测。
  2. 现在完整运行它——在你最后一次修改之后。「完整」是关键词:跑全套,不是跑你觉得相关的那几个;「在最后一次修改之后」是另一把锁,上一节那种引用旧结果的路从这里断掉。
  3. **读完整输出和退出码。**跑完不看,等于没跑。测试框架的汇总行、失败计数、退出码,都在输出里,不在你的印象里。
  4. **确认输出确实支持你要说的话。**比如你打算说「42 个测试全过」,输出就得真的写着 42 个通过、0 个失败——不是「我跑的那个命令通常输出这个」。结论和输出对不上,改结论,不改说法。
  5. 然后才下结论,并引用数字。

第五步的「引用数字」是这道门的落点:合格的回执长这样——「npm test:42 通过,0 失败,退出码 0;HEAD 在 a1b2c3d」。它把核查的成本从读者身上拿走了:数字摆在那里,谁想复核,按图索骥即可。

不同角色的结论,要配的证据不一样,技能里列了表:

角色结论所需证据
developer任务/修复完成任务检查 + 其测试,新鲜、带计数;git rev-parse --short HEAD
evaluatorAPI 行为已验证本次运行中采集的真实请求与响应
evaluator旅程可用 / 页面正确本会话的真实浏览器运行:Playwright 输出和 harness capture 截图(manifest 状态 pass)
planner契约完整harness clarify-done / replan-done 的输出

反过来,技能还明确列了不算证据的东西,这份清单比流程本身更能说明它在防什么:

  • **最后一次修改之前的运行。**旧证据,第一节的主角。
  • **「应该能用」「大概没问题」、读源码得出的判断。**读代码得出的结论是推理,不是运行结果;生成的代码最擅长的就是读起来合理。
  • **用 API 响应证明页面显示了什么。**证据要和结论同类:页面长什么样,只能由页面自己的证据(截图、浏览器运行)证明,接口返回正常推不出渲染正常。
  • **状态为 fallback 或 fail 的截图运行。**截图管线自己都没跑通的产物,当然不能拿来证明页面没问题。
  • **别的 agent 报告的、你没亲眼看到的结果。**转述不是证据。链条上每一环都说「上一个环节验过了」,合起来可能没有任何一环真的验过。

还有一条容易被忽略的分工:检查或评测失败时,往 failure-log.md 里追加记录的是内核,不是任何角色;没有角色往里面写成功,也没有角色能编辑它。失败的记录权和结论的举证责任,分开放。

设计一:同一条规则,四种尺寸

这份 skill 看起来只是一条工作习惯,但把它放回整套系统里看,会发现它是其余所有机制的最小单元——同一条规则,在不同尺度上反复出现:

  • 任务尺度(这一篇):一句「做完了」,要有最后一次修改之后跑出的输出。
  • 提交尺度:developer 声称任务完成时报一个 commit SHA,内核要用 git rev-parse 把它验出来——不存在的编号直接驳回。「我提交了」也是结论,也要有能指着看的东西。
  • 评测尺度:评判者的 judge 结论必须引用本轮新采集的证据(截图、真实请求响应),旧证据不作数。评的是整个功能,规则是同一条:证据的出生时间必须晚于最后一次修改。
  • 功能尺度(确认阶段):评测通过还不算,证据要全部重新生成、换一个评判者、连续稳定通过若干次。这是同条规则的最大尺寸:一次新鲜证据能证明「此刻」,连续多次才能压住「运气」。

四个尺寸,一句话:**结论必须能指出产生它的那次检查,而那次检查必须发生在被检查的东西定型之后。**层是这个逻辑在代码结构上的展开,阶段是它在流程上的展开,21 份 skill 是它在每个具体动作上的展开。

设计二:核查的成本,由开口的人付

为什么这条规则要设计成「声称者举证」,而不是「怀疑者核查」?

因为在 AI 协作里,这两种陈述的成本极不对称。生成一句「全部测试通过」几乎免费——它甚至不需要恶意,只需要模型把上一次运行的结果顺手写进这一次的报告,流畅度毫无损失。而核查一句结论的成本是实打实的:找到命令、重跑、对时间戳、读输出。如果规则不干预,系统的自然状态是:结论越来越多,核查越来越少,因为便宜的那件事总会赢。

这道门禁做的就是把成本挪个位置:开口的人先付——跑一遍命令、读一遍输出、把数字抄进回执。付完之后,这句结论就变得廉价可查了:读者只需要扫一眼数字和命令是否对得上。整套系统的其他机制也是同一个朝向:任务包不让 developer 看到 rubric,是不让它省掉「读懂需求」的成本;评测报告只许追加不许改,是不让任何人省掉「面对旧结论」的成本。这份 skill 管的尺度最小,朝向没变。

还有一个细节:门禁不区分成功和失败结论。为什么失败也要举证?因为一句没有证据的「这个测试挂了」同样昂贵——它会把接手的人送去修一个可能并不存在的 bug,或者让一个其实通过了的功能被反复返工。错报失败和错报成功,烧的是同一种东西:后面所有人对报告的信任。

它管不了什么

边界说清。

第一,它保证的是结论有当场证据,不保证证据背后的检查本身问对了问题。你完全可以诚实地跑完一套问错了方向的测试,引用真实数字,报告一个真实但不相干的结论——测试套件该测什么,是写验收标准那一环的事,这份 skill 不越界。

第二,它防的是过期和转述,防不了采集环节本身的造假或失灵:截图管线在 fallback 状态下产出的图,按规则不算证据,但如果某个检查工具自己报假数,这条规则发现不了——那要靠检查工具本身的校验和确认阶段的独立复跑去兜。

第三,它有成本。每个结论之前多跑一遍命令、多读一遍输出,在一天几百次交接的流程里累积起来不算小。它的取舍是写死的:只卡四个下结论的点位,不要求每句过程汇报都附证据。贵的是结论,不是说话。

收尾:三十一篇,同一件事

这篇是第 31 篇,五层约束、五个阶段、21 份 skill,到此全部拆完。

回头看这 31 篇,会发现它们讲的是同一个动作,只是在不同尺度上重复:语言层把「类型对不对」交给编译器当场判,状态层把「数据住哪」钉成一张先写好的表,评测阶段把「做没做对」交给一个没参与过的人用新证据重判,确认阶段再把「是不是运气」用连续通过压掉。这一篇讲的这份 skill,是这个动作的最小形态——一句「做完了」,也要有刚跑出来的输出撑着。

一句话概括整套系统:**把信任换成一次指着看的检查。**凡是原先靠「它说做完了,那就做完了」的地方,换成「它说做完了,证据在这一行命令的输出里,数字是这几个」。检查的形态随尺度变——编译器、扫描器、评判者、新上下文、连续多轮——不变的是结论和证据的绑定关系,以及证据必须比结论年轻这一条。

这也是为什么这套东西里没有一处写着「请认真负责」。对 AI 说「请认真」是把希望当机制。把每一次下结论都变成一道要付成本的门,把每一次通过都钉上时间戳和数字,认真就不再是一种态度,而是流程的副产品。

31 篇的完整脉络在母篇《从能跑到可交付:一套前端 AI 协作系统的设计笔记 》里,系统本身在 frontend-harness-cli 开源。系列到此结束。