Red-First, Tamper-Proof
Effort: free — 纯顺序纪律:你反正要写的测试先写、先证明是红的、再用一次 commit 封存;防篡改检查就是一条 git diff。消除:照着修复长出来只为能过的测试,以及构建者靠改测试掰弯的绿色裁决。
修完之后再写的测试什么都证明不了——它是照着"能过"长出来的。 构建者能改的测试证明得更少——它可以被掰弯到通过。 所以测试先行、上锁,评分时原封未动。
什么时候跑
派出任何构建或修复之前——只要想要的行为能用一条测试说清。这是 bug 修复和新能力共同的默认。
步骤
- 写那条失败的契约测试。 用能捕捉"该行为缺失"的最小形式,写出你想要的行为。它现在必须是失败的。
- 证明它是红的。 跑这条测试,亲眼看它失败——而且是因为对的理由。import 就报错的测试、悄悄通过的测试,都不算红。没人跑过的红测试是猜测,不是基线。
- 派出构建者之前,先把红测试 commit 掉。 记下 commit id。这个 commit 就是红色基线——防篡改的封条。
- 给构建者只派一个活:让它变绿。 构建者禁止碰测试文件。派活时明说。
- 独立评分。 一位没写这次改动的评分者查两件事:
- 测试现在通过;
- 测试文件与红色基线逐字节一致——
git diff <red-sha> HEAD -- tests/test_contract.py输出为空。 测试文件上有任何 diff 都判评分失败。没有例外,"只是修了个错别字"也不行。
- 一个结构性守卫,胜过散落的点测试。 结构性守卫是一种能拦住"下一个"违规者的检查(grep 扫描、AST 扫描、lint 规则),不是只钉住这一例。一个守卫顶十个各钉一例的点测试。
硬性规则
- 红必须被证明是红。 跑它、看它失败,才算数。
- 构建者从不编辑测试。 自红色基线以来测试文件 diff 为空,是落地关卡的一部分,不是顺手一查。
- 构建者绝不当评分者。 用不同的人、不同的 agent,或与构建者不同家族的模型。
- 只有绿不算证明。 绿 + 测试原封未动 + 独立评分,才是证明。
- 牵涉一整类缺陷时,守住这一类。 点测试挡得住这个 bug;结构性守卫挡得住下一个。
搭配使用
- sniper-testing — 迭代时只跑改动触及的测试;落地时一次全量。
- seam-engineering — 结构性守卫所属的"修一类"纪律。
- blind-tribunal — 从没见过作者的独立评分者。
- repair-loop — 把红 → 绿 → 已证明端到端跑完的循环。