Agents with Receipts · 有据可查:agentic coding 实践手册
RECEIPT #13 · /practices/13-checkable-claims.html 在 GitHub 查看源文件

13 — 可核对的宣称:把每个对外数字接上一条命令

适用工具:所有写 README / 文档 / 看板的仓库 · 验证于 2026-09

09 章讲「让 agent 验得动这个仓库」,11 章讲「让证据进 PR」。本章补最后一环:仓库对外说出去的每个数字,也要能自己验。README 里的「283 家餐馆」「6,395 条记录」「167 个版本」「召回率 100%」——它们是别人引用你的依据,却常常是全仓库唯一没有任何东西在检查的部分。

13.1 一个数字没有命令,就不是证据

场景:README 写着「tRPC 950 文件,变异测试 28/28」。三个月后文件数变成 957,测试批次换了,README 没人动——它已经错了,而所有 CI 都是绿的。因为 CI 检查的是代码,不是你对外说过的数字。

做法:给每条宣称配一条能重新算出它的命令,并把命令与断言写进仓库里的 claims.json:

{
  "id": "trpc-mutation-impact-recall",
  "claim": "在 tRPC 上注入的 28 个变异全部被影响集覆盖",
  "value": "28/28",
  "metric": "eval/ 里 recall_hit 为真的条目数",
  "method": "注入变异 → 跑全量测试 → 对比预测集",
  "repro": "python3 eval/mutation_check.py",          // 人读的复现方式
  "evidence": "eval/mutation-2026-08-28-trpc-n30.json",
  "as_of": "2026-09-13",                              // 这个值什么时候为真
  "check": {                                          // 机器跑的那一份
    "cmd": "python3 -c \"import json;K=[e for e in json.load(open('eval/mutation-2026-08-28-trpc-n30.json')) if 'precision' in e];print('%d/%d'%(sum(1 for e in e if e.get('recall_hit')),len(K)))\"",
    "expect": { "equals": "28/28" }
  }
}

依据:verify-claims 是这一模式的实现(零依赖,Python 3.9+),退出码 0 全过 / 1 某个数字不再复现 / 2 形状非法——形状非法时拒绝运行,而不是给一个绿勾。--strict 会把「没有机器检查的宣称」也算失败,便于逐步收紧。

边界:只解决「可计算但没人算」的那一半。厂商数字、人工评分、别人的页面,仍然要人读(见 13.3)。

反模式:把断言写成「输出非空」。那不是检查,是把 exit 0 换个写法。

13.2 六种失效模式,都不是”手滑”

一次对 16 个仓库、553 条宣称的复核里约 100 条不成立(18%),集中在六类——每一类都有对应的检查写法(完整案例见 verify-claims/docs/case-studies.md):

失效模式 真实例子 用哪种检查
手写数字,数据走了它没走 卡片里写死的 60k★(当天是 74,442★),而这张卡片每天被重新生成,日更永远纠正不了它 equals:重数一遍
归因给了错的上游 某 agent 的「32 个工具 / 13 个 LSP 操作」,上游 README 是 31 / 14 equals + 数字旁写上上游链接
文档里的功能不存在 「每天定时同步」的 cron 根本不存在;文档让你跑 ./search-bench.sh,而仓库里只有 edit-bench.sh cmd 就写 README 里那条指令
门禁不门禁 --check 因为时间戳取自 date.today() 而每天都红;链接检查 --exclude-path 恰好排除了唯一有坏链的目录;task 集合检查只 warn 不 fail 让门禁红一次:故意改坏一个数字,要求它失败
把估算写成实测 README 的 ~20 s,实测 71 s;「命中率约 18%」的输入文件是 gitignored,无法复算 区分「实测 / 推导 / 估算」,实测进 check,其余进 check.manual
两处自己打架 同一项目在两页写着 28K 与 237K 星;徽章写「Secondary TypeScript」,而页面自己生成的语言表把 JavaScript 排在前面 用生成替代手写;或一处的输出断言另一处

13.3 诚实地说「这个算不了」

做法:算不了的宣称不要删了事,也不要编一个命令——写 check.manual 加一句理由,让它每次都出现在报告里:

"check": { "manual": "25% / 75% / 57% / 20% 是人对评论质量的评分,没有任何命令能产出评审分" }

依据:verify-claims 把 manual 条目单独计数并逐条打印(6 machine-checked · 6 ok · 3 manual),所以「还差多少」是可见的;--strict 可以随时把它变成失败——这就是收紧的路径:先把数字接上命令,再决定哪些值得补。

边界:manual 不是免责声明。它是一条待办,写在每次 CI 的输出里。

反模式:为了让门禁变绿而把真实数字换成模糊措辞(「数百家」「约百个版本」)。那是把问题从「错」改成「空」。

13.4 落地顺序(约一小时)

  1. 挑对外可见的数字:README 首屏、徽章、看板、站点 meta、图表脚注里出现的那些。
  2. 每条写一条能重算它的命令(通常已有的脚本就够,例如 validate.py 打印的那行计数)。
  3. 接进门禁:CI 里 uses: alloevil/verify-claims@v0.1.0,同时加每周定时——数据会在没人提交的日子里变;这次的 18% 里有一半是这样烂掉的。
  4. 加一步「故意改坏一个数字、要求这次运行失败」,证明门禁真的会红。
  5. 把 manual 清单当待办,定期清。

对应检查:verify-doctor 的 verify-command(本仓库里「一条命令能验完」的前置条件)· 跨仓库的那一半由姊妹工具 verify-claims 承担,本仓库不依赖它


位置:轴二 · 验得动 — 上一章 11 验证技能 · 下一章 09 可验证的仓库(轴二 · 验得动)

相关:09 可验证的仓库 · 04 验证闭环 · 11 验证技能