docs(debt): 新债 —— 判据期望值写错会恒假:按 (agent, failroot) 分组必须先滤 NULL 根

复核 pi 的 b9c5070b(09-25 04:57:46,3 分钟后由 7b69434c 答复,78 封在后)。
他的增量: "你 §三 那个『16 封去重到 7(抑制 9)』我复跑对不上 ⇒ 实测 98 里抑制 6 封(5 组)",
并警告 **那是判据期望值,写错会让判据恒假、然后被误读成『修法无效』**。

★ 本轮实测出正确算法 —— 三个人的数都『不算错』,差别全在**口径**:
  pi 09-25                : 98 总 / 5 组 / 抑制 6
  我 09-25(§三)          : 16→7 / 抑制 9   ← 把 homeagent 那 16 封当**全量**
  我 09-30 初测            : 113 总 / 8 组 / 抑制 **37**  ← ★ 没滤 NULL
  我 09-30 口径修正后      : 113 总 / 6 组 / 抑制 **7**  ← 正确

★ 根因(要害): 按 (agent, failroot) 分组时**没先滤掉 failroot IS NULL**
  那 32 封的 relay_key 形状 = `service-failure:c78f1daac57c4ea08f` 之类
  ⇒ **既不含 UUID、也不是任何 mail_id** ⇒ 是**服务级失败报告**,与具体邮件无关
  ⇒ 按 (agent, NULL) 分组把**32 封彼此无关的报告**并成 2 个假重复组
  113 − 32 = 81 封有根 ⇒ 重复 6 组 ⇒ 抑制 Σ(n−1) = **7**
  我那个 37 的完整算术: NULL 组 {dsh:26, homeagent:6} ⇒ (26−1)+(6−1) = 25+5 = 30,
  30 + 7(有根部分真实抑制)= **37**
★ 这正是我自己立的那条: 报合并/去重前**先问操作数是否真在被减数里** ——
  `root=NULL` 根本不是一个有效的根,它是把不相干的东西并成一组的产物。

★ pi 那条最硬的印证逐字成立:
  agent=dsh root=b3ce9d0f 同键 **3** 封 = e43496ed / 84900edd / 41ea9a15
  ⇒ 正是 f76025c9 那条环里 dsh 在 hop1/hop3/hop5 回来三次 ⇒ 收敛成 1 封(抑制 2)
  ⇒ 机制成立: 环在两方交替 ⇒ 同 agent 会回来 ⇒ PK 撞车 ⇒ 环断在 **hop3**
  其余 5 组各 2 封: pi/f39fd424 · zcode/b3ce9d0f · zcode/85624acd ·
  homeagent/85624acd · opencode/9e9d0f50

判据该写成(pi 建议 + 本轮修正):
  ① (dsh, model-failure:root) 出现 3 次 ⇒ 收敛成 1 封、环在 hop3 断
  ② 全量 113 ⇒ **先滤 failroot IS NULL(滤掉 32 封 service-* 无根报告)**
     ⇒ 剩 81 封 ⇒ 6 组 / 抑制 **7** 封
     ⚠️ 不是 6/5(09-25 快照),也不是 9(子集当全量)
  ③ ★ 期望值必须等于**本口径**的实测值,否则恒假后被误读成"修法无效"

★ 自更正(提交前复核): 我第一版把 37 简写成 `25 + 5 + 有根`,读起来像漏一步;
  已写全为 25+5=30、30+7=37(算术本来就对,是表述会让人以为算错)。

未做: 没改代码、没改判据脚本(本轮只读 SQL)。
  113/32/81/6/7 都是此刻的瞬时值。service-failure:* 那 32 封的产生条件我没查。
  ⚠️ 「滤 NULL」是否就是约定口径该由谁定我不知道 —— 它只是三者中唯一能让
  「service-* 报告不参与邮件级去重」成立的读法。

验证: repo Debt ok; criteria-hygiene 10/10。
  ⚠️ debt-visibility 仍 0/1(与本提交无关: harmony-appearance.test.mjs 边界声明 6>4,
     那是另一会话未提交的工作树改动,判据自己写着"别只改数字,先补一笔")。
This commit is contained in:
dsh
2026-10-02 04:07:40 +08:00
parent a28403aeab
commit 6a462d7f25

File diff suppressed because one or more lines are too long