一次 spec → plan → workflow 一次性构建的根因排查:18 个单元全部通过对抗验收,可交付给用户的价值只剩四成。没有一个环节作弊。问题出在方法论的结构里——以及我们怎么把它从根上补上。
一个流程严谨的构建(先写测试、独立对抗验收、逐片过闸)产出了 18/18 全绿的结果,却漏掉了约 61% 的用户可见价值。我们一开始以为是"验收标准没写全",查下来发现验收标准写得很全、连端到端旅程都有——真正的病根是:把工作切成小片、并逐片定义"什么算做完"的,是同一份 plan。切片的盲区会自动变成验收的盲区。
解法不是"把标准写得更好",而是一个结构性倒转:让验收权威回到 spec、凌驾于 plan 之上;让分片纵切;让"做完"等于整条用户旅程能走通,而不是各片各自绿。再用一个 hook 保证这套东西下次真的被触发,而不是又变成一条没人执行的规矩。
"一键开店"是一个帮商户在新加坡从零开一家餐厅的产品——从公开测算、注册、问卷,到选址报告、证照预审、持牌撮合,一直到开业交接,一条 23 步的旅程。
我们用一套自认为很硬的流程来造它:先把需求写成 spec,再拆成一片片小任务写成 plan,最后交给一个多智能体的 workflow 一次性跑完——每一片都"先写会失败的测试、再实现到绿、再由一个全新的智能体对抗验收",闸门全绿才进下一片。
结果 workflow 报告:18 个单元,18 个通过。然后一个人照着真实的开店流程,从注册开始一屏一屏点下去做走查——发现大约 六成的用户可见价值根本没交付:后端引擎一个个都造好了、都有测试,但真正的界面要么是"敬请期待"的占位,要么根本没接进任务板,用户点不到。
最让人不安的是第三个数字。这不是"谁偷懒了"能解释的事故。每个实现智能体都写了真测试、跑了真绿;每个验收智能体都独立复核、甚至看见了缺口(比如"通知铃点不动、跳不到材料页"),但都判了"非阻塞、放行",理由高度一致:"这不在本片闸门的断言范围内。"
当一个严谨的流程稳定地产出"全过但没用"的结果,而且人人合规——那问题就不在人,在结构。这份文档记录我们怎么把这个结构性病根挖出来,以及怎么从根上补上它。
要看懂后面的分析,得先把这三个阶段讲清楚。它们各自回答一个不同的问题。
为什么这套流程被认为很硬?因为它把三条业界公认的防错手段叠在了一起:
这三条单独看都对。但它们组合起来时,藏着一个谁都没注意到的接缝。要看清它,得先看一次失败的排查——从一个看起来最合理、其实是错的假设开始。
最自然的猜测是:spec 里的验收标准(EARS 条款),在拆进 plan 的一片片时,漏掉了一些——有些条款没落到任何一片上,于是没人做。
这个假设很有说服力。如果 42 条验收标准里有几条没被分配,那漏掉一部分功能就顺理成章了。于是我们做了最直接的核对:把 spec 的验收标准全集,和 plan 里那张"覆盖矩阵"逐条比对。
spec 的第 9 节写了 21 条主验收标准,加上四个深功能附录各自的验收,共 42 条。plan 的覆盖矩阵——标题就叫"21 条 EARS + F1–F4 全部验收 → 切片;一行不漏"——把这 42 条每一条都点了名、分给了具体的切片。连最敏感的"受控取件"红线都挂在了某一片上。
换句话说:没有一条验收标准掉在缝里。覆盖矩阵是满的,"一行不漏"是真的。
假设被推翻了。而且推翻得很彻底:一个覆盖率 100% 的 plan,照样漏了六成价值。这反而是整件事最关键的线索——它排除了"标准没写全",把我们逼向一个更深的问题:
如果每条验收标准都被分配了、也都被"通过"了,可用户还是拿不到东西——那"通过"这个词,到底在验什么? — 排查转折点
更进一步:我们甚至发现 spec 里本来就有一条完整的端到端旅程验收——从"未注册访客扫码进来算账、留资注册"一路写到"开业、门店激活、渠道报表归因",一步不落。它完全符合我们自己定的标准("至少一条端到端场景")。spec 该有的都有。问题一定在别处。
答案藏在"每一片怎么验"这行字里。我们拿一个具体例子来拆——算钱功能(开店成本测算器)。
它被切成一片,代号 S4。翻开 plan 里 S4 的"闸门"那一行,原文开头就是四个字:pytest——,整行讲的全是后端算出来的数字对不对:覆写行项要实时重算、重油烟要按工期上限、台账过期要警示……一个字都没提"用户能不能打开这个算钱的页面、看不看得到这些数字"。
然后 workflow 严格照着这行字执行。给实现智能体的指令里有一句关键的话:
# workflow 给实现智能体的原文(build #1) 跑全量回归:backend pytest 全套 + 涉前端时 pnpm build(+ playwright 若本片有浏览器闸)
盯住那半句:"playwright 若本片有浏览器闸"——意思是,只有当这一片的 plan 里明确写了要开浏览器,workflow 才会真去浏览器里点一遍。S4 那行没写,于是 workflow 只跑了后端,全绿,判过。算钱的那个页面,从头到尾没有任何一道闸看过它一眼。
所以要回答"'纯 pytest'到底在哪儿定的"——答案是两层:选择在 plan(写 plan 的人一片一片定),执行在 workflow("不写就不开浏览器"的默认,把这个疏忽变成了既成事实)。没有一个总开关,也没人作弊。
是的。spec 那条完整的端到端旅程验收,plan 确实分配了——排给最后一片当"收官大考",而且明明白白写着要"浏览器级 e2e"。听起来这道闸能兜住一切。问题出在时机:它只在 18 片全做完之后跑一次。可跑到那会儿,前面那些页面(算钱、选址报告……)根本没接上,"从头点到尾"物理上跑不起来——于是它被悄悄缩水成"跑跑后端数据链 + 点一个毕业页面"糊弄了过去。真正的端到端,从来没真跑过。
把"整体能不能用"的检查排在最后跑一次,等于没有早期信号。等它跑的时候,一切都已经晚了——它甚至没有能力真跑起来,只能降级放行。整体验收必须每片都跑,才能在坏掉的那一片当场拦住。
把算钱这一类看清后,六成的缺口其实来自三种不同的漏法:
三种漏法长得不一样,但都指向同一个源头。
用一句话概括病根:把工作切成小片的人,同时也是写"这一片怎么验"的人——而且是同一笔写下去的。
写 S4 这片时,同一个动作既画了切片的边界("这片是算钱引擎"),也定了这片的验收("测后端")。这两件事本该互相制约,现在却由同一个念头一次性产生。于是——如果切错了(把"引擎"和"页面"切成两件事、而页面那件没人认领),或者验收写弱了(只写后端),后面没有任何一道关能纠正。因为在这套流程里,你写的那行"怎么验",本身就是"什么算做完"的定义。定义它的人漏了,就没有别人能拦了。
这就是为什么覆盖矩阵补到 100% 也没用:那张表只回答"每条标准归了哪片",不回答"那片的验收,验的是后端还是用户能看到的东西"。盲区随着分片,自动复制到了验收里。
我们最初盯上的"前端可达性",其实只是这次恰好撞上的那一个整体属性。病根一天不动,换一种整体属性,同一类"全绿却带病"就会换个症状复发。
这一步值得单独说清楚,因为它决定了解法往哪走。一个很自然的结论是"那就把 spec / 验收标准写得更好、更全"。但这个结论是错的,证据就在 spec 自己身上:
如果规则是"必须写一条完整的端到端验收",那 build #1 通过了这条规则——然后照样漏了 61%。所以 spec 不是那个弱点。你没法靠"把标准写得更好"来防这个 bug,因为它本来就写得够好。
这是工作流的问题,不是 spec 质量的问题。同一条写得很好的端到端验收,被工作流用错了三件事:
① 只在最后跑一次(太晚) ② 前面每片用后端测试就放行 ③ 活按层切,让"接线"没人认领。
补救全在改工作流的机制,没有一条是"把标准写得更高质量"。
一句话解法:把验收权威从 plan 上移到 spec;把分片从"横"改"纵";把"做完"从"各片各自绿"改成"整条旅程绿"。
前者切断"分片者即验收定义者"这个机制;后者让"每片绿"和"集成有价值"收敛成同一件事。这张图取代了那种拿等宽字符拼出来的流程图——它要说的是谁说了算变了。
build #1 是横切——按技术层切:算钱引擎一片、选址引擎一片,页面是"另一层",顺带做、没人专门验。横切必然留下"接线无人负责"的缝。纵切反过来:每一片都是"从用户入口到一个用户能看见的产出"的一条薄竖条。这样每片天生就包含页面,而且页面没做出来,这片就没法判做完。
切一片 = 横着切一层。界面层没人切到 → 页面成孤儿。
每片贯穿所有层、落在一个用户可见产出上。没页面 = 没做完。
把上面的原则落到三个阶段,就是这套新方法论的骨架:
交付了实质价值(一个能打开、但里面是空的页面 ≠ 绿);③ 后端片不能"豁免"用户可见义务,只能"转移"给点名的下游片;④ 至少一名验收智能体直接锚 spec、有权因"plan 合规但 spec 承诺没兑现"而判红;⑤ 终局走查用多只独立的眼睛,不许一个人自证。这些不是我们拍脑袋想的。它们各自对应软件工程里成熟的做法:可执行验收凌驾于计划(Specification by Example)、外向内的双循环测试与 walking skeleton(《GOOS》)、纵切用户故事(INVEST 的 V=Valuable)、"整体可用"是塔尖属性不能下放到零件(测试金字塔)、Definition of Done 独立于任务分解(Scrum)。我们做的是把它们拼成一套能防住这个具体病根的组合拳。
这里有一个必须诚实面对的陷阱:build #1 之所以漏,不是因为缺规矩。
我们的 spec 标准里早就白纸黑字写着"端到端验收测试要真跑通整条闭环、它就是闸门"。规矩在,build #1 还是栽了。所以如果这次的补救只是"往标准里再多写几条规矩",下一次照样能被软化——这正是要避免的失败模式。
真正的强制力,只能来自一个不看执行者心情的机制。在 Claude Code 里,那就是 hook(由外壳执行,不由智能体自觉)。所以我们把这套东西分成三层,每层的"保证程度"不同:
这套已经落地并验证:标准升到 v1.1(补上第 5 个病、纵切规约、棘轮与机械字段);可复用模板把机械闸编码进代码;hook 用真实输入测了 7 种情形(达标放行、调研类 workflow 不误伤、缺闸构建被拦)。hook 先跑"只提醒不拦"的观察模式,确认无误伤后一个词翻成"硬拦"——这样硬闸的误伤风险基本清零。
下次跑 feature 构建:用模板 → 三道机械闸自动在场,build #1 那种漏法结构上做不到;没用模板裸手搓 → hook 把你推回模板;跑调研/审查类 workflow → 不受影响。"每片绿但整体不交付"从一个方法论漏洞,变成了一个过不去的闸。
如果这次复盘只能留下一句话,就是这句:
验收权威在 spec、凌驾于 plan;
分解工作的人,不得同时定义"什么算验收"。
这条铁律不只属于"一键开店"。任何"把一个目标拆成 N 块、每块独立过闸"的流程——不管块是代码切片、微服务、还是工序——都有同一个结构性风险:拆的那一刀,会把整体属性切进块与块的缝里;而如果拆的人同时定义了验收,缝里的东西就永远没人验。
防它的办法是恒定的:让一道照着整体目标、独立于分解的检查凌驾在所有块之上,并且每完成一块就重跑它。部分的正确永远推不出整体的正确——这道跨越所有部分的检查,才是"整体真的交付了"的唯一证据。