导语:代码是 AI 写的、测试也是 AI 写的,问它通过了吗,它说通过了——这套流程的漏洞不在某个工具,而在举证角色重合。本文给出三道零成本验收闸:强制交代已知问题、用 git worktree 拿旧测试跑新代码、效果声明必须带测量方式,附实测可用的 crossrun.sh 脚本。
先让它交代没做完什么
最省事的一道,改一句 prompt 就行。我们现在所有 AI 编码任务的收尾都带这段话。
提交之前,除了代码本身,另外输出两部分内容。
【我声称的效果】
一句话说明这次改动带来了什么变化,禁止出现形容词。
【已知问题清单】
列出你知道但还没解决的部分,至少 3 条。写不出 3 条说明你看得不够深,
可以写「性能未在多线程场景下验证」这类具体项,不许写「可能存在未知问题」这类废话。
你做完这一步会看到什么,取决于 AI 之前有多爱吹牛。第一次加上去的时候,我这边有个任务的输出从「已完成全部优化」变成了「已完成主路径优化,异常处理未覆盖,压测未进行」。
这里有个关键点,说实话,「至少 3 条」这个数字别调。 试过改成「尽量列」,输出立刻退回一句「暂无」。
Linus 那份补丁说明里最有价值的段落,恰恰就是他那串已知问题清单,包括「这是整个补丁系列里处理得最不到位的部分」这句原话。小动作,零成本,改的是举证的方向。
再拿旧测试跑新代码
这道稍微要点配置,我把在用的脚本原样贴出来。它做的事是,用 git worktree 把改动前的测试取出来,覆盖到当前工作目录,对着新代码跑一遍,跑完再还原你自己的测试。
#!/usr/bin/env bash
# crossrun.sh 旧测试 × 新代码,AI 提交前的最后一道闸
# 用法 bash crossrun.sh [base 分支名,默认 main] [测试命令,默认 pytest -q]
set -euo pipefail
BASE="${1:-main}"
CMD="${2:-pytest -q}"
WT=/tmp/crossrun-base
OLD=/tmp/crossrun-oldtests
cleanup() { rm -rf "$OLD"; git worktree remove --force "$WT" 2>/dev/null || true; }
trap cleanup EXIT
# 0. 保存当前改动,后面要换测试文件,别把你的代码改动搞丢
git stash push --include-untracked -m "crossrun-wip"
# 1. 开一个干净的 base 工作树
git worktree add "$WT" "$BASE"
# 2. 恢复你的改动,此时工作区是「新代码 + 新测试」
git stash pop
# 3. 取出 base 版本的测试目录
cp -R "$WT/tests" "$OLD"
cp -R "$OLD/." ./tests
# 4. 旧测试 × 新代码
$CMD || echo "有原本通过的测试在新代码上挂了,见上面输出"
# 5. 还原你自己的测试
git checkout -- tests
跑成功的标志很明确。正常情况第 4 步全绿;如果 AI 改了测试去迁就它的代码,第 4 步会报红,报错位置指向的通常正是它偷偷动过的那个用例。
我在一个临时仓库里实跑了一遍这套流程。改动前 main 分支上有一条测试,断言 is_weekend(0) 返回 False(0 代表周一)。改完之后 AI 把代码和测试一起动了,代码改成 return d >= 0,测试跟着改成 assertTrue。这时候 head 测试跑 head 代码,输出是「Ran 1 test, OK」,全绿。同一个改动跑一遍这个脚本,结果变成下面这样。
FAIL: test_monday (test_dates.T.test_monday)
File "tests/test_dates.py", line 6, in test_monday
self.assertFalse(is_weekend(0))
AssertionError: True is not false
Ran 1 test in 0.022s
FAILED (failures=1)
有原本通过的测试在新代码上挂了,见上面输出
同一个改动,head 对 head 是绿的,旧测试对新代码是红的。这个红就是你要找的东西。
😅 我跟你说,坑有三个,都是我自己踩过的。第一个,第 0 步的 stash 绝对不能省,我第一次图省事直接覆盖 tests 目录,把自己的测试改动冲掉了,好在改动不大重写了一遍。第二个更隐蔽,如果你把 crossrun.sh 放在仓库根目录又没提交过,stash push --include-untracked 会把脚本自己也卷进 stash 里,这次只是因为 bash 已经把文件读进内存才没中断,正确做法是放进 scripts/ 并提交,或者索性放在仓库外。第三个,脚本假设测试都在 tests/ 下,是 src/**/test_*.py 那种散在源码里的布局,自己改第 3 步的路径,另外 cp -R 只覆盖不清理,追求严谨就先删后拷。
坦白讲,这套命令我在本机临时仓库跑通了,用的是 git 2.x 的标准 worktree 命令。你的目录结构不一样的话路径要自己调,第一次建议先拿临时分支试。
最后是效果声明必须带测量方式
这条最简单,也最容易被绕过。规矩是这么写的。
涉及性能的改动,效果说明必须包含三项 测试数据怎么来的、在什么机器上跑的、对照组是什么。
不接受「明显更快」「性能大幅提升」这类描述。
Linus 那组数据之所以让人服气,是因为每一项都带着测量场景,「我自己的 Core i7-8565U 笔记本,关掉部分安全缓解措施,8 线程 16 字节读同一个文件」。他还明写了坏消息,单线程读一个还没建立映射的页,从约 4150 个时钟周期涨到约 6550 个。有对照、有场景、有坏消息,这才叫测量,光给结论的那叫转述。
这三道闸的边界,我把话说全
第一,crossrun 只在有存量测试的时候成立。一个连基本测试都没有的项目,压根没有旧测试可跑,那得先补测试,没捷径。
第二,旧测试跑新代码天生有一批误报。接口调整、改名、拆模块这些正常重构都会让旧测试挂掉,那不是回归,是预期变化。一直乱报警的工具最后会被所有人关掉,所以你得先分得清哪些是正常的。
第三,也是最难的一条, Linus 这套能成立的前提是他本人看得懂内存管理的改动。换成看不懂的领域,AI 交上来的东西你也验不明白。AI 没有省掉学习成本,它把学习成本搬到了验收环节。这条我到现在也没解决好,只能挑我熟悉的模块先上,不熟的先老老实实人工看。
总结
三道闸里真正起作用的不是技术手段,是它把举证责任挪回去了。AI 从「证明自己干成了」变成「交代自己没干完什么」。最省事的是第一道,改一句 prompt,今天就能加。
素材来源与出处
| 内容 | 来源 | 链接 |
|---|---|---|
| 「已知问题清单」「测量是我自己跑的」出处 | Phoronix(2026-10-12) | www.phoronix.com/news/Linus-… |
| 性能数据与已知问题原文 | Hardware Busters | hwbusters.com/news/linus-… |
| 「CI 只跑新测试对新代码」的结构性漏洞 | pr-witness 作者长文 | dev.to/talhayme911… |
| Transluce 8600 份会话分析、ImpossibleBench 作弊率 | 同上引述 | 同上 |
| crossrun.sh 实测环境 | 作者本机临时仓库(git 2.x + Python 3.13 unittest) | 本篇实测 |