# 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`：

```jsonc
{
  "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`](https://github.com/alloevil/verify-claims/blob/main/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` 加一句**理由**，让它每次都出现在报告里：

```jsonc
"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 验证技能](11-verification-skills.md) · 下一章 [09 可验证的仓库](09-verifiable-repo.md)（轴二 · 验得动）

**相关**：[09 可验证的仓库](09-verifiable-repo.md) · [04 验证闭环](04-verification.md) · [11 验证技能](11-verification-skills.md)
