# 纠正记录（corrections）

被推翻或修正过的结论，**原文保留、标注被什么推翻、指向新证据**。评测系统的可信度不来自它从不出错，
而来自它出错时留下的痕迹。按发生顺序。

---

<a name="over-claimed-null"></a>
## 1. "model 效应根本不存在"——过度声称

**当时写的**：2×2 因子设计里模型主效应 Δ=0.000、p=1.000、不一致对 1:1 与 2:2 完美对称，
"不是 harness 效应比 model 大，而是 model 效应根本不存在"。

**错在哪**：16 个配对单元的 MDE 是 0.46——那个 null 只能排除 ≥46% 的效应，几乎什么都排除不了。

**修正**：4 题 × 20 轮 = 80 单元，MDE 0.10：Δ=+0.025，CI95 [0.000, +0.075]，5:3，p=0.727。
可报告结论是**"模型效应 < 10%"**，脚手架效应 +62.5%——相差至少 6 倍，这个倍数有下界支撑。

**机制化**：`interpret()` null 分支强制附可排除范围（[lessons#null-bounds](lessons.md#null-bounds)）。
随后审计仓库全部 4 条已记录 null：2 条为 72 单元的强 null（零不一致对，10% 效应下出现概率 5×10⁻⁴），
2 条为 4 单元的**无信息 null**（忠实性 A/B 的模型间比较），后者被降级为"不能当证据用"。

---

<a name="ceiling-interaction"></a>
## 2. 交互项 −0.667 有天花板成分

**当时写的**：harness × agent 的 2×2 里"两个因子是替代品而非互补品，任一到位即够，两者都做是浪费"。

**错在哪**：strict 侧两格都是 1.000。在 1.000 上无法再加，"strict-check 是否优于 strict-single"
这半个问题在此设计下**不可测**——该格差异恒为 0 是设计所致，不是发现。

**当时的修正**：拆成可测的一半（成立）与不可测的一半（不能声称），只敢说"两者叠加无额外收益**未经证实**，
不要据此拒绝叠加"。补造 6 道更硬的题在严格脚手架下全部 2/2——第五次筛不出余量。

**机制化**：`report()` 自动报出触顶/触底系统并声明交互项不可解释（[lessons#ceiling](lessons.md#ceiling)）。

---

<a name="substitution-refuted"></a>
## 3. "纯替代"结论被推翻

**被推翻的**：上一条里"任一到位即够"的读法（格式轴与忠实性轴都这么测出来，但两次参照格都在 1.000）。

**推翻它的证据**：换到有余量的题族（跨条目推算，参照格 0.64~0.89），严格提示已到位时自检**仍加
Δ=+0.183，CI95 [+0.086, +0.285]，p=0.0046**（18 单元）；独立复现 +0.281，p=0.0002。

**修正后的结论**：两者是**收益递减但可叠加**——宽松提示下自检加 +0.411，严格提示下仍加 +0.183。
上一条那份谨慎（"未经证实"）是对的：一轮后它确实被证伪。见 [findings#interaction](findings.md#interaction)。

**差点发生的第三次过度声称**：首轮 6 单元时增量 +0.122 但 p=0.496、CI 含 0，我已准备写"推翻"。
按 sd 反算补到 18 单元后才拿到显著。教训：点估计的方向不是结论，CI 是否含 0 才是。

---

<a name="floor-basis-mcnemar"></a>
## 4. `interpret` 用单元数算 McNemar 的 p 地板

**错在哪**：McNemar 精确检验可达的最小 p 只由不一致对数决定。10 单元 / 5 不一致对的设计被报成
"未检出差异，只能排除 ≥67%"，真相是"不一致对只有 5 个，最小可能 p 是 0.0625，效应再大也到不了显著"。
前者暗示"效应可能真的小"，后者指向"加轮次凑不一致对，或换更能拉开差距的题"。

**谁抓到的**：`tests/test_pipeline.py` 第一次运行。181 个单测全绿时它红了——各组件单独都对，
错在接口处传了语义不同的同名量。

**修正**：`interpret(floor_n=...)`，`report()` 传入不一致对数。连带发现下游 `check_model_null` 自己重推病因
时把零不一致对误报成"MDE 不可达"——改为复用 `interpret` 的诊断文本。

---

<a name="floor-basis-permutation"></a>
## 5. 置换检验同一 bug 类；上一次实验的诊断也是错的

**错在哪**：符号翻转对零差值对是恒等操作，可达最小 p 是 2/2^(非零差值对数)。
实证：5 对零差 + 1 对满差 → p=1.0000，恰是 p_floor(1)。

**最刺痛的实例**：第 3 条里首轮的数据——差值 `[0, −0.17, 0.4, 0, 0, 0.5]` 只有 3 对非零。
当时报"p=0.496 不显著"，真相是"非零差值对只有 3 个，地板 0.25，检验根本到不了显著"。
当时按 sd 反算补样本恰好治对了病，**但理由是错的**。

**修正**：`paired_compare` 自报 `n_effective`，`interpret` 默认以它为地板基数；三种基数三种措辞
（[lessons#floor-basis](lessons.md#floor-basis)）。推算增量的复现检查连带修正：两侧完全相同（零有效对）
此前被报成"CI 下界 ≤ 0，在足够样本下仍无法确认"——"足够样本"这句是错的，现归为功效警告。

---

<a name="power-formula"></a>
## 6. 样本量手算公式答的是另一个问题

**错在哪**：第 3 条里用 `(1.96·sd/Δ)² = 18` 反算样本量并据此补跑。那个公式答的是"CI 恰好排除 0"，
不是"80% 功效"——后者是 37，大 2.04 倍。实测置换检验在 n=18 的功效只有 **0.45**。

**结论是否受影响**：不受——它经独立复现（+0.281，p=0.0002）站得住。但当时**拿到 p=0.0046 有运气成分**
（补跑后的真实效应 +0.183 大于当初的点估计 +0.122，帮了忙），样本量论证本身是错的。

**修正**：`required_pairs(mean_diff, sd)`——内部真跑置换检验做网格搜索，p 地板做下界，给 42 对。
连带自查出新代码 bug：每次 sim 复用同一重采样种子（[lessons#power-formula](lessons.md#power-formula)）。

---

<a name="sigkill"></a>
## 7. 工具事故：被取消的变异扫描留下损坏的源文件

**发生了什么**：变异扫描被中途取消（SIGKILL），try/finally 来不及执行，`claim_eval.py` 停在
"被 unparse（0 行注释）+ 一个活变异"的状态，快速套件因此变红。唯一的"备份"是 git HEAD，
未提交的一轮改动随 checkout 一起丢，只能凭记忆重放。

**修正**：写源文件前先落盘旁路备份，启动时自动还原残留备份（必须在读取原件之前）；`.gitignore` 加该后缀，
孤儿备份永不进提交。三个测试覆盖，含真实子进程 `os.kill(SIGKILL)`。
见 [lessons#interrupt-safety](lessons.md#interrupt-safety)。

---

## 附：同一条纪律的多次应用

"null 结论要报可排除范围"这条纪律的完整路径，供参考评测系统如何从教训走到机制：

1. 引入 MDE（"任务集看不见多小的差异"）
2. 引入 saturation（"有效样本可能为零"）
3. 补 MDE 最小性（"只测充分性会让虚高值过关"）
4. 抓住自己的过度声称（第 1 条，"Δ=0 不等于已证明为零"）
5. 系统审计全部 null，分级标注
6. 固化为 `interpret()`——从 API 层面不可能重犯
7. 集成测试发现地板基数错误（第 4、5 条）——同一机制的第二层修正
