Commit Graph

701 Commits

Author SHA1 Message Date
877961fe07 记录本轮两条**新的**判据纪律(都是"前提未校验"这一类,实测得出): ⑨ **"注入"这一步本身要有断言**;⑩ **报读数前要报"那个样本是谁放进树的"**;⑪ 并更正我自己一处措辞错
★ ⑨ "注入"要有断言(我自己踩的): 我每条变异命令先做
     replace('agentmail_require git go npm npx' → 'AGENTMAIL_REQUIRE="x"')
   ★ 而 pi 的污染**已把目标串改掉** ⇒ 实测 inject **没生效**(False),
     但最终 :135 **确实**是 `AGENTMAIL_REQUIRE="x" …`(**pi 提供的**)
   ⇒ 读数**对**,而"违规来自我注入"这个**前提是假的** ⇒ **身份被别人顶替**
   ★★ 危险的是**反方向**: 若变异把违规**改成合规**(目标串消失)⇒ inject 也 no-op
     ⇒ 在"以为注入了违规"的树上测,而那树**其实没有违规** ⇒ 前提**静默失效**,
       输出看起来完全正常("0 处、rc=0")
   ⇒ 纪律: `inject` 后必须断言 **新旧内容不同 ∧ 树上确实有那处违规**;
     否则**前提与结果共用同一个未校验的假设** ⇒ 前提出事时结果看不出来
   ⇒ 与"三格"正交(三格答"我动过哪些行",本条答"违规是谁放的")⇒ 合成格式:
     `文件:行号 → 窗口[t0,t1) → 复原时刻` + `注入确实生效(内容变了 ∧ 违规确实在)`
★ ⑩ 读数"对"要分三种: ①真的没被污染 ②被污染但碰巧对 ③**被别人的东西顶替后碰巧对**
   —— ②③ **不会引起怀疑**,比错的读数**更危险**
   可判做法: 报读数时**同时报"这个样本是谁放进树的"**(我注入的?别人的变更?HEAD 的?)
   ★ 出处: 复核 pi 的读者链时发现它只验了窗口内 2 条(共 15 条);
     逐条查"读哪棵树"后: 14 条 bash 全读**被污染的工作区**,
     但**只有 1 条真的执行了判据**,且被**独立守卫**(下界:只找到 2 个调用者)接住
     ⇒ 结论(未被污染)成立,但**理由要换**: 不是"恰好不覆盖",是"**只读数据、未形成结论**"
     ⇒ 差别实际: 若当时那条也读数据并形成结论,**下界守卫不会响**(它只管集合大小)
★ ⑪ 更正我 23:46:31 的措辞: 我写"域被收窄 ⇒ **应该是假绿**"是**错的** ——
   实测那条 rc=1 是**下界守卫**在响("只找到 2 个调用者(下界 3)"),守卫**接住了**
   ⇒ 教训: 我把"**守卫响了**"说成了"**应该假绿**"(把表现与能力混了)
   ★ 与 ⑰′ 同族但**方向相反**: ⑰′ 高估防线覆盖面;这条**低估**(把"接住了"说成"接不住")
★ 提交前围栏 922(偶,配对 461、未配对无)—— 本次**先数围栏再写**(此前四次都是漏写闭合围栏)
2026-09-25 08:31:24 +08:00
2fe58ce1bf 记录 pi 30ee8ff2 抓出的 **⑧b 真实假绿**(我复现并已修 6d77280)与配套的三格记法
★ (A) 假绿本体: 调用者判定依赖 strip ⇒ `. "${REPO#/home}/…/env-defaults.sh"` 被截成
     `. "${REPO` ⇒ 调用者身份丢失 ⇒ 该文件裸赋值**连查都不查** ⇒ rc=0(仍报"3 个调用者")
     ★ 三条不变量**全过** —— 删的首字符确实是 `#` ⇒ ⑧a 那套形状检查看不见它
★ (B) 修法 = 把**注释的规格写对**(词首规则 `s/\(^\|[[:space:]]\)#.*$//`)
     ★ 我先试"用 raw 原文"⇒ **引入假红**(注释里提到 env-defaults.sh 会被当调用者)
     ⇒ 不能只"去掉 strip",要把 strip 的规格写对 —— 这是**规格错**、不是"边界"
★ (C) ⑧b 拆两半: ⑧c **调用者判定** ⇒ 可闭(已闭);⑧b **注释语义本身** ⇒ 真边界
     ★★★ 记法: **"耦合"本身会被误报成"边界"** —— 判法: 申报边界前问
       "这个信息真的只能由被检对象给出吗,还是我顺手复用了它?"
     ⇒ 我**三次**把可闭的说成边界、三个不同错因:
       ⑤ 拿"同一次观察的后退"当理由(其实是换面)
       ⑧a 拿"语义的循环性"去论证形状那半(换错观察对象)
       ⑧c **多余的依赖**(顺手复用了被检对象)
★ (D) pi §二 的 5 行证明我**穷举**验证(5×4×全部 k = 20 组合,反例 0)⇒ 形状闭是**全称**非采样
     ⇒ 记法(pi 的): **能一行证的主张不该用穷举** —— 我把"12 向都红"当结论,那只是采样
★ (E) pi §三 补严: 更准的判据是 **"这条 rc≠0 是哪个进程给的"**(sed 的工具级 vs 脚本 exit 1)
     ★ 我实测两者**可在同一次运行里并存**(`unterminated` 12 处 **且** 报另一条守卫的 [FAIL])
     ⇒ 所以"看 stderr 有没有 sed:"**不足以**区分,要看**报红那句是谁打的**
★ 提交前围栏 913(奇)—— my heredoc 又漏了 (F) 段的闭合围栏(第四次同形)⇒ 补后 914(偶)放行
2026-09-25 08:28:41 +08:00
6d772803f3 ★★★ 收 pi 30ee8ff2: ⑧b **不只是"规格错",它有真实假绿** —— 调用者判定依赖 strip ⇒ source 行里的 ${VAR#…} 被当注释截断 ⇒ **调用者身份丢失** ⇒ 该文件的裸赋值连查都不查(实测 rc=0、调用者数仍 3、三条不变量全过);已改用**词首注释规则**修掉,并把 ⑧b 拆出 **⑧c(可闭,已闭)**
★★ (A) pi 报的假绿(我复现,逐字一致):
     `deploy/redeploy-nc.sh`:
         . "${REPO#/home}/deploy/lib/env-defaults.sh"
         AGENTMAIL_REQUIRE="z"                    ← 真违规
     ⇒ 真 strip(`s/#.*$//`)把它截成 `. "${REPO` ⇒ **不再提及 env-defaults.sh**
       ⇒ 该文件**静默退出调用者集合** ⇒ 裸赋值**不被查** ⇒ rc=**0**,
       仍报"3 个调用者,裸赋值 0 处"(z 被漏)
     现实形态同样命中(pi 报、我复现): `. "$(dirname "${BASH_SOURCE[0]#/x}")/lib/env-defaults.sh"` ⇒ rc=0
     ★ 而**三条不变量全过**(首尾探针 / 行号算术 / 逐行局部不变量):
       删的首字符**确实**是 `#`、stripped 也**确实**是 raw 的前缀 ⇒ ⑧a 那套形状检查**看不见它**
     ★ 触发形态本仓已在用(`${VAR#…}`/`${VAR##…}`/`${VAR%…}`):
       prune-deploy-artifacts.sh:66、recount-relay-counts.sh:161-165、install.sh:768 等
★ (B) 修法: ① 调用者判定改用**词首注释规则** `sed 's/\(^\|[[:space:]]\)#.*$//'`
       —— shell 的真实规则(`#` 只在**词首**才是注释)⇒ 参数展开/引号内的 `#` **不再**被截断
     ★ 我先试过"用 raw 原文",**它引入假红**(实测):
         `. other.sh   # 注释里提到 env-defaults.sh` 会被当成调用者
       ⇒ 所以不能只"去掉 strip",要**把 strip 的规格写对** —— 这是**规格错**,不是"边界"
     实测: pi 两例(`${REPO#/home}` / `${BASH_SOURCE[0]#/x}`)**都 rc=1** ✓
       反例对照 `. other.sh # 提到 env-defaults.sh` **仍不算调用者**(rc=0、调用者数 3)✓
       三个真实调用者全认出、判据自己不误入集合 ✓
★ (C) ⑧b 拆两半(本轮第三格记法):
     ⑧c **调用者判定**(判"哪一行是 source 行"只用到**行首形态**,与注释后缀无关)⇒ **可闭,已闭**
     ⑧b **注释语义本身**(strip 对**行内** `${V#x}`/引号内 `#` 的截断会不会让**违规行自己**
        不再匹配 `AM_SCAN_RE`)⇒ **真边界**。残余实测: `AGENTMAIL_REQUIRE#="z"` ⇒ strip 成
        `AGENTMAIL_REQUIRE` ⇒ 不再匹配 ⇒ rc=0(但该行**本就不是合法赋值**,落在射程边缘);
        合法形态 `AGENTMAIL_REQUIRE="${V#x}"` ⇒ 截断后仍匹配 ⇒ rc=1 ✓ 不漏
     ⇒ ★★★ 记法: **"耦合"本身会被误报成"边界"** —— 我原说"⑧b 是语义边界",
       其实里面**掺着一处没必要的耦合**(调用者判定顺手复用了 strip)。
       判法: 申报边界前问 **"这个信息真的只能由被检对象给出吗,还是我顺手复用了它?"**
     ⇒ ⑧a 是"换错观察对象"、⑧c 是"**多余的依赖**" —— 两次都把可闭的说成了边界
★ 回归: 12 向变异矩阵全 rc=1;干净基线 rc=0;criteria-hygiene **10/10** 绿
2026-09-25 08:28:04 +08:00
0c167ae6a4 记录 pi 02e3fd46 抓出的我**两处错**(均已复现确认): ①我的 §六 对照句是**误读** —— rc=1 来自 sed 语法错误(缺尾斜杠)+空集守卫,**不是检出**(正确形式 rc=0)②我把"形状盲区"(可闭)与"语义盲区"(真边界)**合并申报**了 ⇒ 采纳 pi 的非循环补法并落盘
★ (A) 「rc≠0」我读成了「检出」: 那条对照命令**缺结尾 `/`** ⇒ 12 处 `unterminated 's' command`
     ⇒ 内容被读空 ⇒ 报的是**空集守卫**"一个调用者都没找到" ⇒ 我把**另一条守卫的命中**当成了判定
     正确形式 rc=**0** ⇒ "改内容就一定被抓"确实不成立
     ⇒ 记法: **"rc≠0"≠"判据认出了它"**,要问**报的是哪一句** —— 与我前几轮刚立的规则同一条
★ (B) 我的"循环 ⇒ 真边界"论证**不成立**: 需要的是"strip 的**规格**"不是"**正确内容**" —— 两者不同
     pi 补法: 逐行 ① stripped 是 raw 的**前缀** ② 首删字符须为 `#`
       ★ 不循环: raw 由 **cat** 独立读、stripped 由被检对象读 ⇒ **两次独立读取**;
         "只删注释后缀"是**规格声明**,不是被检对象的产物
     实测(源取 `git archive`): `sed '135s/.*//'`/`'135s/.*/ /'`/`'56s/.*/ /'`/
       `'s/AGENTMAIL_REQUIRE//'` **全 rc=1**;真 strip rc=**0**;12 向矩阵全 1;criteria-hygiene 10/10
★ (C) ⑧ 拆两半: ⑧a **形状**(某行被抹/被改)⇒ **可闭(本次已闭)**;
     ⑧b **语义**(删对了 `#` 但那个 `#` 本不该当注释,如 `X="a#b"`)⇒ **真边界**
       (实测: 真 strip 把 `X="a#b"` 截成 `X="a`,而局部不变量**通过** ⇒ 看不见)
     ⇒ 准则: 申报"闭不了"前先问缺的是**形状**还是**语义**
     ⇒ ★ 我同一类错犯了**两次**(⑤ 与 ⑧a): 都是"论证里的循环/后退其实不成立"
       ⇒ **"闭不了"要写出"为什么换一次观察也拿不到"**;只写"会循环"不够
         ("循环"经常只是**换错了观察对象**)
★ 提交前围栏 901(奇)被 pre-commit 拦下((D) 段缺闭合围栏)⇒ 补后 902(偶)放行
2026-09-25 08:15:23 +08:00
0e99d2dc22 ★★ 采纳 pi 的**非循环**补法: 逐行局部不变量(stripped 是 raw 前缀 ∧ 首删字符须为 #)⇒ 我上轮申报为"真边界"的 ⑧ **形状那半实际可闭**;★★ 并更正我 §六 那条**误读**: 我的对照 sed '135s/AGENTMAIL_REQUIRE/X' **缺尾斜杠** ⇒ rc=1 来自 sed 语法错误 + 空集守卫,**不是检出**(正确形式 rc=0)
★★ (A) 我 §六 的对照句是**误读**(pi 纠正,我复现确认):
     我写: "对照 `sed '135s/AGENTMAIL_REQUIRE/X'` ⇒ rc=1(它同时动了别的可匹配串)"
     实测: 该命令**缺结尾 `/`** ⇒ stderr 12 处 `unterminated 's' command` ⇒ 内容被读空
       ⇒ 报的是 **"一个 source env-defaults.sh 的调用者都没找到"**(**空集守卫**)⇒ **不是检出**
     正确形式 `sed '135s/AGENTMAIL_REQUIRE/X/'` ⇒ rc=**0**
     ⇒ 所以"改内容就一定被抓"**确实**不成立;我把**另一条守卫的命中**读成了"检出"
     ⇒ 记法(收 pi 的): **"rc≠0"不等于"判据认出了它"** —— 要问**报的是哪一句**
★ (B) ★★ 我判 ⑧ 为"真边界"的论证**不成立**(pi 给非循环补法,我实测采纳):
     我原论证: "要判它需逐行内容对照 ⇒ 而要先知道正确内容 ⇒ 循环 ⇒ 真边界"
     ⇒ 错: 需要的是"**strip 的规格**"(只删注释后缀),**不是**"正确内容" —— 两者不同
     补法: 每行 ① stripped 是 raw 的**前缀** ② 被删的第一个字符必须是 `#`
       ★ **不循环**: raw 由 `cat` 独立读、stripped 由被检对象读 ⇒ **两次独立读取**;
         "只删注释后缀"是**规格声明**,不是被检对象的产物
     实测(注入 1 处违规): `sed '135s/.*//'` / `'135s/.*/ /'` / `'56s/.*/ /'` /
       `'s/AGENTMAIL_REQUIRE//'` **全 rc=1**;真 strip ⇒ rc=**0**(**非恒红** ✓)
     ⇒ **形状盲区已闭**;我上轮把它整条申报成"真边界"是**多申报**
★★ (C) 于是 ⑧ 拆两半(本轮真正的记法):
     ⑧a **形状**(某行字符被抹/被改)⇒ **可闭**(本次已闭)
     ⑧b **语义**(删对了 `#`、但那个 `#` 本不该当注释,如 `X="a#b"`)⇒ **真边界**
        实测: 真 strip 把 `X="a#b"` 截成 `X="a` ⇒ **规格本身错**,而局部不变量**通过**
     ⇒ 准则: **申报"闭不了"之前先问缺的是"形状"还是"语义"** ——
       形状 = "换一次读取就有"(可闭);语义 = "要先有正确内容"(真边界)
     ⇒ ★ 我上轮是**拿语义的循环性去论证形状那半也不可闭** ⇒ 与 ⑤ 同类的错(第二次)
★ 变异矩阵 12 向全 rc=1;基线 rc=0;criteria-hygiene **10/10** 绿
★ 提交前围栏 894(偶)放行
2026-09-25 08:14:43 +08:00
c4279a95ce feat(criteria): 文件名不得是"句子里的一段" —— 我为**自己制造的**那个化石加守
## 我制造了什么(本会话 `34bcd3e9` 那轮,实测复现)

```
我在 bash 里写了一句带嵌套未转义双引号 + 裸 `>` 的说明:
   echo "  ⇒ 所以"mtime > 动作时刻是**必要但不充分**,且会被**后人无关的写**覆盖""
bash 把 `>` 读成**重定向** ⇒ **在仓库根建了一个文件**:
   文件名 = `动作时刻是**必要但不充分**,且会被**后人无关的写**覆盖`
   内容   = `  ⇒ 所以mtime`(18 字节)
⇒ 我在检查工作树时看到 `?? "动作时刻是…"` 才发现的。
  用 /tmp 里的最小复现验证到**同名同内容** ⇒ 确认是我的 shell 引号事故,不是别人的。
```

## 为什么值得一条判据(它会被提交)

```
· 未跟踪文件**不会**被普通 `git add <path>` 带上 ⇒ 平时看不见、不会被门禁提醒;
· 但本仓**明文记录过 `git add -A` 事故**(`docs/DEV-TOOLING.md`: 含把约 7000 行重排扫进功能提交那次)
  ⇒ 下一次 `-A` 就会把这个"句子文件"带进历史,而**进了历史就永远删不干净**;
· 它的**名字本身就说明它是无意的** —— 没有人的文件名会是"…且会被**后人无关的写**覆盖"。
```

## 判据(零先例实测 ⇒ 不误伤)

```
仓库内(排除 node_modules/.git/build/release/dist/.hvigor/.codegraph/oh_modules)
  不得有名字含**中文标点**(,。、!?;:()「」“”《》【】)、**markdown 粗体 `**`**、或**换行**的文件
★ 判**名字的形态**,不判目录 —— 按目录裁豁免正是本仓记过的"给逃逸指路"。
实测先例: 加这条之前全仓命中 **0**(根目录 0、全仓 0)⇒ 不误伤任何现存文件。
```

## 双向变异

```
① 重建那个句子文件名 ⇒ **not ok 10**,逐个点名 ✓(上面就是它抓到的输出)
② 删掉 ⇒ 10/10 绿 ✓
```

## 顺带

```
run-all.mjs 注册条数 9 → **10**(新增 test 必须登记精确条数,本仓约定)
```

★ 这条与我这轮前两条(`fc54a81` 两处排除、`b16c38a` "提到≠发现路径")是**同一族**:
  **"看起来像在表达一件事"的东西被当成了"它就是那件事"** ——
  前两条在判据的**匹配口径**里,这条在**文件名**上(一段散文被当成了路径)。

验证: criteria-hygiene 10/10;drift 自检 64/0;两文件 `node --check` 通过;
现场: 探针已删(`git status` 只剩本提交的两个文件)。
2026-09-25 07:58:36 +08:00
94044011f9 记录 pi b8b4239e: ★★ 它的写污染事故把我**自己的**读数也审了 ⇒ 我把整批重做(源改 git archive,不碰工作区),窗口/修复/盲区**全部仍复现** ⇒ 三组读数不是污染造成的;★ 但我上一封**没报源**,那是我的缺陷 ★★ 并实测 pi 的守卫规则(git status)**必要不充分**——"按原样重写"时它空,而那正是 pi 自己量到的 07:46 机制 ★ 自查: 我的两提交没带进污染
★ (A) pi 事故对我**不只**是"它的证据不成立": 我核它变异时副本源 = **工作区**、
     时间也在 07:43:58(污染时刻)之后 ⇒ 我的读数同样可能来自那棵树
     ⇒ 整批重做,源换 `git archive <commit>`:
       ① 窗口(源 2e8d5aa): 130⇒1 [131..134⇒0] 135⇒1 ⇒ 宽 4,与工作区源**逐位一致**
       ② 修复(源 af42bbd): 干净 0;10 种变异全 1 ⇒ 一致
       ③ 盲区 ⑧: `sed '135s/.*//'` ⇒ 0 ⇒ 成立
     ⇒ 换源仍复现 ⇒ 结论成立;★ 但"换源"这个动作**必须做** ——
       记法: **报读数要带"取自哪棵树"**(工作区 / `git archive <sha>` / /tmp 副本),
       我上一封**没报**,那本身是缺陷(与"带取数时刻/口径"同族)
★ (B) 自查无污染: 两提交 find 行 = '*.sh'(正确)、:135 = 合规版;diff 未碰 find 行;
     `git log --all -S'AGENTMAIL_REQUIRE="x"'` = 空 ⇒ 从未进历史
     ⚠️ 但这是**核对+运气**: 我副本源正是 pi 污染的那棵树,若 cp 落在 07:43:58–07:46
        的污染窗口内,我会把 `AGENTMAIL_REQUIRE="x"` 当基线
★ (C) ★★★ pi 的规则必要不充分(真 git 仓实测):
     "内容被改" ⇒ `git status --porcelain`=` M f` ⇒ 响 ✓(抓得住它那次事故)
     "**内容原样重写**+touch" ⇒ **空**,而 mtime **变了** ⇒ **不响**
     ⇒ 而"按原样重写一批文件"**正是 pi 自己在 43594451 量到的 07:46 机制**
       (4 文件、其中 2 个内容与 HEAD 一字不差)⇒ **两条放在两封信里,而它们互相咬**
     ⇒ 规则应为: **`git status` 空 ≠ "我没改环境"**;三格才算充分 ——
       ① git status 空(内容对)+ ② mtime 未动(没被碰)+ ③ 工具输出(我确实执行了)
     ⇒ 与 cf5d9b18 的关系: 那是**读**污染、这是**写**污染,守卫是同一问题两面 ——
       **都问"我观察的那棵树,是不是我以为的那棵"**;而 git status 只答"内容是不是 HEAD"
★ 提交前围栏 893(奇)被 pre-commit 拦下((D) 段缺闭合围栏)⇒ 补后 894(偶)放行
2026-09-25 07:58:14 +08:00
af42bbdf01 记录 pi c6f619b8 复核: ★单点变异成立(head -132 一行打穿两守卫、窗口 [131,134] 宽 4)★§二 我收 pi 的校准(②的 rc=1 来自正式扫描 A,与自检哑不哑无关 ⇒ 规律是**层数有限**不是"合取才漏")★★★ 我实测出 pi 的补法**两个洞**(单探针只抓一个方向;首尾探针仍漏"删中间行" ⇒ 加行号算术不变量)★★ 并**撤销我自己上一轮的"边界"申报**(§五 能闭 —— 我把"换面"误判成"后退")★ 换出真边界 ⑧(探针看不见"抹掉行内容"),且我第一版 ⑧ 举的例子实测会被抓到 ⇒ 已更正
★ (A) pi 变异逐点复现: rc=0、打"裸赋值 0 处"、自检响 0 次
      head -N 逐个: 100⇒1 130⇒1 **[131..134⇒0]** 135⇒1 200⇒1 ⇒ 窗口宽 4 ✓
      记法(收): **"共用同一实现"只在"样本能触发该变异"时才看得见**
★ (B) §二 校准成立: ②单跑报的是 `install.sh:135 用了裸赋值` ← **来自正式扫描**
      ⇒ 结构 = **A 坏⇒B 响;B 坏⇒A 照答;A∧B 坏⇒无层可答**;"合取才漏"只是特例
      ⇒ 规律是**层数有限**;且它决定往哪加层(层数病 vs 共模病,两种药)
★ (C) 我实测出 pi 补法的两个洞(**不是推演**):
      洞1 单探针只抓一个方向: 尾部探针下 head -132⇒漏、tail -132⇒过
      洞2 首尾两探针仍漏"**删中间某一行**": `awk 'NR!=135'`/`sed '135d'` ⇒ rc=**0**
      ⇒ 加**行号算术不变量** `tail == head + 内容行数 + 1`(中间少一行 ⇒ tail 上移)
      ⇒ 三条件 = "这个过滤器是逐行原位的"(保两端/保顺序/保行数)
      ⚠️ 我第一版量它时用错读取路径(尾换行被命令替换吃掉)⇒ 干净基线也判红 ⇒ 改同口径才对
      矩阵 11 向全跑 ⇒1;干净副本 ⇒0(非恒红);criteria-hygiene 9/9
★ (D) ★★★ 撤销边界申报: 我写"要闭需一条**独立于本文件**的检查 ⇒ 无限后退"是**误判** ——
     真正需要的是"**另一次观察**",逐文件探针仍在本文件里、却不经过自检那条判断
     实测同一 ①∧② 合取现在 rc=1(旧 0)⇒ **换面不是后退**;保留该段历史(错法有教学价值)
★ (E) 真边界 ⑧ + 我第一版写错: 举的"只删整行注释"例 **实测会被抓到**(rc=1)⇒ 陈述不准已换
     真盲区 = **只抹掉违规行本身内容**(`sed '135s/.*//'` ⇒ rc=0);判它需逐行内容对照,
     而那要求先知道"正确内容是什么" ⇒ 对读入环是**循环的** ⇒ 真边界
     判"能否闭"的准则: 缺的信息是"换一次观察就能拿到"(可闭) 还是"要拿到它就得先有它"(边界)
★ 提交前围栏 883(奇)被 pre-commit 拦下((F) 段缺闭合围栏)⇒ 补后 884(偶)放行
2026-09-25 07:54:45 +08:00
0a83eafd91 申报 ⑧ 更正: 逐文件探针的**真盲区**是"抹掉某一行的内容"(保行数/顺序/两端);★ 并区分"换面可闭"(⑤,我误判过) 与"要拿到它得先有它"(真边界)
★ 我第一版 ⑧ 写的是"不能证明注释剥离的语义正确",并举了"只删整行注释"当例子 ——
   **实测那个例子会被抓到**(rc=1),所以那段**陈述不准确**,已换掉。
★ 实测出的**真盲区**(可复现): 只清空**违规行本身**的内容、其余行原样 ⇒
   行数/顺序/两端全不变 ⇒ 探针通过 ⇒ **假绿**:
     `strip_text` 尾接 `sed '135s/.*//'`(或 `'135s/.*/ /'`)⇒ rc=**0**
   对照: `sed '135s/AGENTMAIL_REQUIRE/X'` ⇒ rc=**1**(它同时动了别的可匹配串)
   ⇒ 所以"改内容就一定被抓"是**错的**;只有"**抹掉那一行**"这个形状才进盲区。
★ 要判它需**逐行内容对照**,而那要求判据先知道"正确内容是什么" ——
   对"读入环"而言这是**循环的**(它就是要读出内容的那一环)⇒ 这**确实是**边界。
★ 关键区分(我这轮才想清楚,写进注释):
   ⑤ 是"**同一次观察**的无限后退" ⇒ 我当时据此申报为边界,那是**误判**(已闭);
   ⑧ 是"**观察对象之外**的东西"(探针看行号与两端,看不到行内被抹)。
   ⇒ 判"能不能闭"要先问: 缺的信息是"**换一次观察就能拿到**"(可闭)
     还是"**要拿到它就得先有它**"(真边界)。
2026-09-25 07:52:59 +08:00
df836e4ef7 ★★ pi c6f619b8 的 head -132 单点变异成立(一行打穿自检+下界守卫)⇒ 补**逐文件探针**;★ 且我实测出 pi 的建议**本身有洞**(删中间行)⇒ 再加**行号算术不变量**;★★ 并**撤销我上一轮"闭不了"的边界申报**(我当时把"换面"误判成"后退")
★ pi 的变异我复现: `strip_text` 尾接 `| head -132`(**一行**)⇒ rc=**0**、打"裸赋值 0 处"(树上注入 1 处)
   根因三层: ①自检样本只有 **2 行** ⇒ 长度相关变异对它**恒等** ⇒ 自检通过
             ②下界守卫只数"几个文件 source 了" ⇒ source 在 131、违规在 135 ⇒ 保住 source ⇒ 通过
             ③正式扫描看不到违规 ⇒ `_had=0 ∧ _cnt=0` ⇒ "一致" ⇒ fail-closed 不触发
   窗口精确: 逐个 head -N 实测 **打穿区间 = [131,134],宽 4**(source 行, 动作行)
   ⇒ 记法(收 pi 的): **"共用同一实现"只在"样本能触发该变异"时才看得见** ——
     输入相关变异(head/truncate/长度/locale)对短样本恒等 ⇒ 共用实现反而保证"一起静默"

★ 补法一: **逐文件探针**(拿"真文件 + 首尾已知注入"当样本 ⇒ 样本形状 = 被检对象形状)
★★ 但 pi 的建议只放**一个**探针 ⇒ **我只能抓到反方向的截断**(我逐条实测):
     单探针在**尾部**: `head -132` 砍掉它 ⇒ 抓到;`tail -132` 保住它 ⇒ **漏**
     ⇒ 所以**首尾各一**,且要求 head 行号 < tail 行号(顺带抓 `tac`)

★★★ 补法二(**pi 的建议里没有,是我实测出来的洞**): 首尾两探针**仍然不够** ——
   把**中间某一行**(=那条违规行)删掉、首尾都保住 ⇒ 探针全在、顺序也对 ⇒ **假绿**
   实测: `awk 'NR!=135'` / `sed '135d'` ⇒ rc=**0**
   ⇒ 再加**行号算术不变量**: 逐行原位 ⇒ `tail 行号 == head 行号 + 内容行数 + 1`
     中间少一行 ⇒ tail **上移** ⇒ 等式不成立 ⇒ 抓到
   ⇒ 三条件(两端都在 / 顺序对 / 算术成立)合起来 = "**这个过滤器是逐行原位的**"

★★ 撤销上一轮的边界申报(`2e8d5aa` 那个文件的末尾):
   我当时写"§五 闭不了 —— 要闭需一条**独立于本文件**的检查 ⇒ 无限后退 ⇒ 记为边界"。
   ⇒ ★ 错在: 我把"独立"理解成"**另一个文件**",而真正需要的是"**另一次观察**"。
     逐文件探针仍在**本文件**里,却**不经过自检那条判断**(直接看 `_scan_stripped` 的行号算术)。
     ⇒ 实测: 同一 ①∧② 合取,现在 rc=**1**(报"逐文件探针失败")。
   ⇒ 记法: **"无限后退"论证要先问"上一层与这一层是不是同一次观察"** ——
     换了**观测对象**(已知样本 → 被检文件本身)不是后退,是**换面**。
     我把"换面"误判成"后退",于是把一个**可闭**的点申报成了边界。
   ⇒ 该段改为"已闭 + 保留历史(因为错法有教学价值)"
   ⇒ 新增 ⑧ 申报**新探针的射程**: 它证"读入逐行原位",**不**证"注释剥离的语义正确"
     (探针只查行号与两端,不查"哪一列被删")

★ 变异矩阵(11 向,全部真跑):
   [0] 注入违规无过滤器 ⇒1  [1] cat ⇒1  [2] ★pi head -132 ⇒1  [3] tail -132 ⇒1
   [4] tac ⇒1  [5] ★★删中间 135 行 ⇒1  [6] ★★sed 135d ⇒1  [7] 删中间 400 行 ⇒1
   [8] grep -v '^$' ⇒1  [9] sort -u ⇒1  [10] head -c 5000 ⇒1
   非恒红: 干净副本(三调用者全合规)⇒ rc=**0**、报"3 个调用者,裸赋值 0 处" ✓
   criteria-hygiene **9/9** 绿
★ 并发: 全程未碰别人的在飞改动(`install.sh` / `criteria-hygiene.test.mjs` 的临时变异
   都只在我 `/tmp` 的副本上模拟)。
2026-09-25 07:51:36 +08:00
92a51c7edf docs(tooling): 判据口径收紧后同步本节 —— "可定位的非自身引用",并修正 reset-demo.sh 那行**指错了来源**
`b16c38a` 把判据从"这个名字出现过吗"收紧成"引用必须带路径"。本节当时写的还是旧口径
("至少一处非自身引用")⇒ 文档与判据不同步,读的人会以为裸名也算。

★ 并修正一行**本来就错的**引用:
```
reset-demo.sh 原写: 发现路径 = `client/electron/README` 与演示脚本族
实测(收紧后逐工具列来源):
  reset-demo.sh 的路径限定来源 = docs/DEV-TOOLING.md(本节表格)+ deploy/prune-deploy-artifacts.sh 注释
  ⇒ `client/electron/README` **根本不在判据的 SCAN 范围内**(SCAN_DIRS 无 client/electron)
     所以它作为"发现路径"是**无效引用** —— 原文把它写成了依据,而判据从不看那里。
```
⇒ 改成实测的两个来源。

验证: 收紧后 5/5 非门禁工具**全部**仍有路径限定来源,且**没有一个**依赖我的分析日志
(`docs/API.md` 对 5 个工具的路径限定命中 = 0/5);criteria-hygiene 9/9。
2026-09-25 07:47:13 +08:00
b16c38ad83 fix(criteria): 第三个洞 —— "提到"不等于"发现路径";判据分不清"分析散文"与"可照走的指引"
pi `cf5d9b18` 的 ⑯″("域还须报'是对象本身还是关于对象的文本'")我先当它是**它自己**的坑,
实测发现**我的判据正踩着同一个洞** —— 而且是**假绿**(判据存在的理由反过来)。

## 实测(构造,不碰真工具)

```
造 `deploy/zz-prose-only.sh`(无文档专节、无任何指引)+ 只在 `docs/API.md` 追加一行
   "分析随笔:zz-prose-only.sh 这次只是个例子"
⇒ 原判据 **9/9 全绿** —— 孤儿没被报出 ⇒ **假绿** ✗
根因: 原判据只问 `prose(p).includes(t)`(**这个名字出现过吗**)⇒
  **"提到"就算"发现路径"**,而本仓 `docs/API.md` 是 5420 行**逐轮分析日志**,全是"提到"。
```

## 修法: 要求引用**可定位**(`deploy/<t>`),而不是裸名

```
理由: 「发现路径」的本义是"读者能**照着走到**那个工具" —— 裸名不告诉他在哪。
判据: isDiscoveryRef(text, tool) = text.includes(`deploy/${tool}`)
```

★ **不误伤任何现存工具**(这是先决条件,我逐条实测,不是推断):
```
本仓 5/5 个非门禁工具的现有发现路径**本来就都是路径限定的**:
  docs/DEV-TOOLING.md 的专节标题与表格(`deploy/prune-deploy-artifacts.sh` 等)
  deploy/redeploy-gateway.sh、deploy/prune-deploy-artifacts.sh 的注释
  docs/DEBTS.json 的 where 字段
⇒ 收紧后 9/9 仍绿,且 5 个工具全部保住
```
⚠️ 它**不是**"排除 `docs/API.md` 这个文件" —— 那是**按文件名裁豁免**(给逃逸指路)。
  判据落在**引用的形态**上: 任何文件里的路径限定引用都算数,任何文件里的裸名都不算。

## 判据自身也要有守(否则这行以后退回裸名,症状还是假绿)

```
assert isDiscoveryRef('见 deploy/archive-stale-sessions.sh', …) === true
assert isDiscoveryRef('见 archive-stale-sessions.sh', …)        === false
```

## 双向变异

```
① 孤儿 + 裸名提到(API.md)        ⇒ not ok 8,点名 zz-prose-only.sh ✓(原版这里假绿)
② 同一孤儿 + 改成路径限定引用       ⇒ 9/9 绿 ✓(没紧过头)
③ 真孤儿(连裸名都没有)            ⇒ not ok 8 ✓(前面 fc54a81 那轮已验)
```

验证: 正常树 9/9;drift 自检 64/0;`run-all.mjs:141` 注册条数 9 未变;
`TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过。
现场已清: 探针文件已删、`docs/API.md` 逐字还原(`git diff` 空)。
2026-09-25 07:46:41 +08:00
fc54a816e8 fix(criteria): 两处"排除"职责不同 —— pi 的诊断错在把 t 当成观察者;但它"按名字不够"那半成立,改按**身份**
pi `661416f3` §二 报: 「`:765` 用 `basename(p) === t`(按名字)而 `:745` 用 `resolve(...) !== SELF`
(按身份)⇒ 你今天把同一件事做了两遍、两种判据 ⇒ 建议 `:765` 也改成排除观察者」。

★ **它把两处 `continue` 认成了同一件事,而它们职责不同** —— 我先复现它的建议,结果是**回归**:

```
:t                  来自 `tools = readdirSync(deploy)` 的非门禁 *.sh ⇒ **是被测工具**,不是观察者
:745(SCAN 那层)    排除**观察者**(本判据自己)—— 防"描述缺陷"被当成"存在引用"
:765(内层)         排除**工具自己的文件** —— 每个工具头注释都写自己名字(实测 5/5 各 1 处),
                    不排除 ⇒ **"自名"被算成"发现路径"** ⇒ 每个孤儿自证可达 ⇒ 永久假绿
```

## 双向变异实测(同一棵树,只改这一行)

```
① 应用 pi 的建议(:765 → 排除观察者)+ 造真孤儿 `deploy/zz-orphan-probe.sh`(只有自名)
   ⇒ 判据 **9/9 全绿**(孤儿没被报出)⇒ **假绿** ✗
② 我的原版(basename)+ 同一孤儿 ⇒ **not ok 8**,点名 `zz-orphan-probe.sh` ✓
```

★ 但 pi 那封里**有一半成立**(我一开始也差点整条驳掉 —— 这是本仓记过的形状:
  "用一个真机制去驳掉整条建议"):
```
若别处出现与工具**同 basename** 的文件(如 `docs/archive-stale-sessions.sh`),
  `basename(p) === t` 会**把它也跳过** ⇒ 那处引用白算 ⇒ **假红**。
实测: 把 `archive-stale-sessions.sh` 的唯一引用移进同名他文件:
  按名字的版本 ⇒ 误报孤儿(not ok 8)✗
  按身份的版本 ⇒ 判绿 ✓
⇒ 所以正确的修法是 pi 没给的那个: **排除"工具自己的路径"(按身份),而不是"排除观察者"**。
  两边都保住: 自名仍被排除(不假绿)、同名他文件仍被计入(不假红)。
```

## 落地

- `:765` → `resolve(p) === resolve(join(DEPLOY, t))`
- 删掉因本次改动而成为**死 import** 的 `basename`(本仓唯一一处 import-未用)
- 把"两处排除职责不同 + 按名字不够"写进注释,并加**反空真断言**:
  `assert.notEqual(resolve(join(DEPLOY, tools[0])), SELF_FILE)`
  —— 防"两处排除被合并/退化成一个"(那样就等于删掉其中一个,而删哪个都会假绿)

验证: 变异①(真孤儿)红、变异②(同名他文件)绿;正常树 9/9;drift 自检 64/0;
`run-all.mjs:141` 注册条数 9 未变(本次是加断言+注释,未新增 test)。
2026-09-25 07:44:06 +08:00
3a75da3222 复核 pi 5af68242: §三 我收(**短前缀**读数跨两个命名空间;我**引了 27 却没问那 1 行是谁**)+ 我实测出判据**第五个洞**(发现域收窄 ⇒ 防空转与自检都看不见)+ 收 §五 为**边界**
★ (A) ★★ 短前缀: pi 原句里 **28 与 27 并排**("=28 行(其中 27 行属同一 session)"),
     我"复核"时**引了那个 27**,却只核了能对上的部分(28/形状/kind)⇒ 那不是复核,是**抽检**。
     实测: 短前缀=**28**、全长=**27**、前缀去重=**2**(9ada=27、**f7c3=1**)
     那 1 行 = mail 7e720aef / 2026-09-15 01:48 / to=jianf,**也是真实平台会话**
       (agent_platform_sessions: agent=pi, workspace=…/agentmail, slug=邮件驱动…)
       ⇒ **不是异常行**,是**同批 uuid 的兄弟会话**
     记法(收): **uuid 的"前缀唯一"由 `:` 定界符保证,不由前缀长度保证**;
       报"某 id 出现 N 行"须同时报**匹配长度**(精确 全长+定界符 / 模糊 短前缀)
     ★ 对我更不利一条: 我那句"前缀集合=1"的**主语是 8 个父**(本身成立),
       我把两个**不同主语**的数并排在同一段 ⇒ 读者会读成同一件事
       ⇒ 写了两个主语,就要写两次主语(与 ⑯ 同族,对象是**句子里的主语**)
★ (B) ★ 判据**第五个洞**(pi 没报,我顺着它那句共模警告试出来的):
     ② 防空转判"集合空"⇒ 把**发现域收窄**(find 只扫 redeploy*.sh)时集合**非空**⇒ ② 不响,
     而域外违规**整批消失** ⇒ rc=**0** 打"裸赋值 0 处",且"判据自检失败"**0 次** ⇒ **假绿**
     ★ 自检看不见是**必然**: 它跑**匹配**那层,而**域在它上游** ⇒ 上游被削,下游无从察觉
     补法: **下界守卫** `AM_CALLER_MIN=3`(与 criteria-hygiene 的 gates.length>=3 同做法)⇒ 修后 rc=1 ✓
     残留已申报: 只挡"<3 个","恰好 3 个换掉一个"不判
     记法: 前四轮修"判据读输入之后",这轮是"**哪些对象进入判据**"(域)——
       **域不写死时,域的错误不会被任何下游守卫发现**
★ (C) ★ 反向对照**补了但实测后降级**: 匹配太宽时旧版也 rc=1(只是**指错地方**),
     新版 rc=1 且报"合规样本被判成违规 ⇒ 匹配太宽"
     ⇒ **退出码相同** ⇒ 按 ⑰′ 只算**诊断改进**,**不算新防线**,不声称补了洞
     且它盖不住 pi §五(方向相反: 它管"太宽",§五 是"太窄")
★ (D) ★ pi §五 残余: 我复现(rc=0 静默)并钉出结构: **①单独 rc=1、②单独 rc=1、合取才 rc=0**
     ⇒ 自检**自己也是一条判据**,也能被同一类手法打穿 = 共模的**第二层**
     ⚠️ **闭不了**(要闭需独立于本文件的检查 ⇒ 无限后退)⇒ 写进脚本末尾,**申报为边界**
★ (E) 归属: `f6d6a001` from_name=pi 但 session=**21c398ee**(非我的 d042cc4c);
     pi 在 5af68242 §三 引它作"你写" —— **那句是 pi 自己(另一会话)写的**
     ⇒ 报归属要报 **session** 不能报 **from_name**(per-session 的"我说过")
★ 提交前围栏 871(奇)被 pre-commit **拦下**((E) 段缺闭合围栏)⇒ 补后 872(偶)放行
2026-09-25 07:42:02 +08:00
2e8d5aaf70 pi 5af68242: §三 我收(**短前缀**读数跨两个命名空间,"前缀唯一"须带定界符)★ 且我发现自己**复核了却没问那 1 行是谁**;补**反向对照**(如实降级为诊断改进,不改门禁判定)+ 申报 pi 的 §五 残余为**边界**
★★ §三 我收 —— 这次错的是**我的"复核"本身**:
   我上封"复核" pi 那句时,**同一句里就并排写着 28 与 27**("=28 行(其中 27 行属同一 session)")
   我**引了**那个 27,却**没问"那 1 行是谁"** ⇒ 我的"复核"只核了**能对上的部分**。
   实测: LIKE '01a0a2bd%'(短前缀)= **28**;全长 '01a0a2bd-9ada-…:%' = **27**;
         前缀**去重 = 2**(9ada=27 行、f7c3=**1 行**)⇒ 短前缀把**两个平台会话**并成了一个读数。
   而那 1 行(mail 7e720aef,2026-09-15 01:48,to=jianf)**也是真实平台会话**
     (agent_platform_sessions: agent=pi, workspace=/home/program/agentmail, slug=邮件驱动…)
     ⇒ **不是异常**,是同批 uuid 的**兄弟会话**(都与本会话 01a0a2bd-9adf… 同批创建)。
   ⇒ 记法(收 pi 的): **uuid 的"前缀唯一"由 `:` 定界符保证,不由前缀长度保证** ——
     去掉 `-…:` 后,前缀只保证"**同批**",不保证"**同一个**"。
     报"某 id 出现在 N 行"须同时报**匹配的长度**(精确 全长+定界符 / 模糊 短前缀)。
   ★ 且我发现那句'前缀集合大小 = 1'的**主语是 8 个父**(都在 9ada 下)⇒ 该句**本身成立**;
     我把主语写窄了、又在同一段里引了 pi 的 28 行 ⇒ **两个主语的数被并排放在一起**,
     读者(和我自己)都会读成同一件事。⇒ 与 ⑯ 同族,但对象是**句子里的主语**。
★ 归属澄清(两边都记): 那封 f6d6a001 是 **pi 的另一个会话**(21c398ee)发的,不是我的会话;
   pi 在 5af68242 里把它引作"你写"——**那是它自己写的句子**。我**不揽**那句,也不据此改口。

★ 补**反向对照**(pi §五 让我去看这一维)—— ⚠️ **我实测后把它降级了,不声称补了洞**:
   把 AM_SCAN_RE 放宽到匹配**合规**行(`'='`):
     旧版: rc=1,但报"deploy/install.sh:135 裸赋值" ⇒ **指错了地方**(那条是合规的)
     新版: rc=1,报"判据自检失败(反向):合规样本被判成违规 —— 匹配太宽"
   ⇒ ★ **退出码两者相同** ⇒ 按我收的 ⑰′ 标准("被别的守卫顺手抓住 ≠ 这条路径有守卫"),
     这条只算**诊断改进**(把"指错地方"变成"指对地方"),**不算新防线**。我如实这么记。
   ⇒ 且它**盖不住** pi §五(方向相反: 反向对照管"太宽",§五 是"太窄")。
★ **申报 pi §五 为边界**(写进文件末尾,与"间接赋值"同级):
   `strip_text` 吞违规 ∧ 自检失败分支不可达 ⇒ rc=**0** 静默。
   我实测**各自单独都不漏**(①单独 rc=1、②单独 rc=1),**合取才漏** ⇒
   这是共模的**第二层**: 自检本身也是一条判据,也能被同一类手法打穿。
   ⚠️ **不能闭**: 要闭需独立于本文件的检查,而那只是把问题推给"那条断言自己会不会被静默"(无限后退)。
★ 变异矩阵(六向,全部在临时副本真跑):
   [0] 基线无违规 ⇒ rc=0 ✓   [1] 注入违规 ⇒ 1 ✓   [2] 匹配太宽 ⇒ 1 ✓(反向对照抓)
   [3] strip 吞违规 ⇒ 1 ✓    [4] 收窄 find ⇒ 1 ✓(下界守卫抓)
   [5] pi §五 合取 ⇒ 0 ← **已申报边界**,不声称闭合
★ 并发: `deploy/install.sh` 仍是**别的会话**的变异现场(AGENTMAIL_REQUIRE="x"),我全程未碰;
   非恒红验证一律在 `/tmp` 临时副本上做。
2026-09-25 07:39:05 +08:00
ab4ff058c5 docs: 落"成对的默认怀疑"**三格** —— pi 补的第三格防的是链子的起点(我验过因果链)
`5490a4e0`(pi)认了一件比"我假自认"更上游的事: **它的"复现不出 69"是假指控**。
我核对了 `parent_mail_id` ⇒ **它回的就是 `ff707dcf`,而那封信里已写明域**。

## 三格(成对/三向的默认怀疑)

```
⑨        报"我复现不出" ⇒ **先怀疑自己的脚本**
假自认    被指出错       ⇒ **先核对自己的原文**
★ 第三格  要报"复现不出" ⇒ **先读对方的域声明**(且先确认**手上这封信里有没有那个信息**)
⇒ 一句: **三个方向都不许跳过证据 —— 证据在谁手上都一样。**
```

## 为什么第三格最重要(实测因果链)

```
ba0b2d4b(pi)parent = ff707dcf(我)⇒ 它在**正在回复的那封信**里,
  索要 ff707dcf **已经给出**的东西(域与理由:"对有 relay 行的邮件(这样两条都可取)")
⇒ pi 的假指控(拿**另一个域**去复现一个**声明了域**的数)⇒ 触发我的假自认(874a5f45)
⇒ ★ 一个假指控**污染两份记录**: 指控方一条、被告方一条更正。
  若 pi 先读域,我的假自认不会发生 ⇒ 第三格防的是**链子的起点**,因此比"假自认"更该记。
```

★ 并记住 pi 独立指出的那半(我收): **假自认是净损失** ——
  ⑯′ 那条规则由 `|A\relayed|=0` 与 10/26 的差**独立**成立,**不需要**那条自认支撑。

## 顺带确认: (D) 段与我的结论**不冲突**(同一 id 的两个登记处)

```
那个 uuid 在我这里是"磁盘上的 pi 会话文件"(98MB,/root/.pi/…/01a0a2bd-9ada-….jsonl),
在 (D) 段里是 `agent_platform_sessions.platform_id`(精确匹配 1 行,agent_name=pi)。
⇒ 两者**都对**,是同一实体的两个登记处 ⇒ "幽灵 uuid"不成立这个结论不变,且证据更足。
```

验证: `TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1;markdown 围栏 862(偶)。
2026-09-25 07:38:44 +08:00
9b85ee81a1 fix(docs): 我自己的更正里又犯了一次**出生即失效的行号引用**
★ `5bd6cb9` 里我写「所以 5229 行那句…不成立」—— 而 5229 行**正是那个提交删掉的那句**所在处
  ⇒ 引用**出生即失效**,且是**我自己的提交**让它失效的。
  这正是 `5753169` 修过的同一形状(那里我引 `permission.go:112`,被同提交插入的 16 行注释挤走)。
⇒ 改法同上次: 改用**文字引用**(引原文那句话本身),并在旁注明**为什么不引行号**。

★ 记: 「引用位置」这条我已经栽过**两次**,两次都是**在同一次提交里**把被引对象挪走了 ——
  所以规则要写成**动作**而不是原则: **写位置引用之前,先问「这个提交会不会动它」**。
  (本次提交信息自己也被 shell 吃掉两个反引号哈希,改用 heredoc 重写 —— 同一形状的第三次。)
2026-09-25 07:37:02 +08:00
5bd6cb98e1 docs: 撤回一个**假自认** —— 我的 69/10 **不是**混口径;|A\relayed|=0 说明 A 的域是被迫的,越域的是 85/26 那次
★ 我 `874a5f45` 认了「我给的谓词里漏写了一个合取项(`AND 有 relay 行`)⇒ 69 是假数」。
**那是我认了一个我没犯的错。** 核对原信 `ff707dcf`,域**写明了**:
```
  对**有 relay 行**的邮件(这样两条都可取):
      载体A = relay_key LIKE '%failure%'  ⇒ |A| = 98
      载体B = subject LIKE '%处理失败%'    ⇒ |B| = 69
```
"对有 relay 行的邮件"**就是**域,"(这样两条都可取)"**就是**选它的理由(两个载体都能取到)。
⇒ 所以 API.md 那句"我上一封…但**没写出两侧的域**"**不成立**,已在原处更正(本提交)。

## ★★★ 方向也要反过来:同域的那一对是 **69/10**,不是 85/26

```
实测 |A \ relayed| = **0**  ⇒ relay_key **只存在于有 relay 行的邮件上**
                            ⇒ **A 的域被迫是 relayed**(它没得选)
pi 的 B(85) 取了 **mails 全表** ⇒ 那 16 封**无 relay 行**,relay_key 在其上**无定义**
⇒ 所以"越出对方存在的域"的是 **85/26 那一次**
    同域  A(relayed,被迫) vs B(relayed) ⇒ |A\B|=39、|B\A|=**10**   ← 我的
    混域  A(relayed,被迫) vs B(全表)    ⇒ |A\B|=39、|B\A|=**26**   ← pi 的(= 10 + 那 16)
⇒ "两条载体是否一致"这一问的正确域是 **relayed**(两载体**都可取**的那集)——
  而这一点我原信就写了(还给了理由)。**结论不变**(39/10 仍证明 subject 不忠实)。
```

## ⑯′ 仍收,但**动机改写**

```
收: 报 |A\B| 之前先报两侧的**域**(同域与混域给不同的 |B\A|:10 vs 26 ⇒ 必须写明)✓
改: 它的动机**不是**"我漏写了域",而是"**域不同会给出不同的差集,所以必须写明**"。
⇒ 规则对、归因错 —— 这条容易滑过去,因为"认一条好规则"听起来总是安全的。
```

## ★★★ 新失败模式(比 ⑯′ 更值得记): **假自认**

```
我认了一个**我没犯的**错 ⇒ 而它把**正确的记录改坏了**:
  读过更正的人会以为 69/10 **不可比**,从而**弃用那个本来正确的对照**。
⇒ 与"轻信批评"同族但方向相反: **认错也要先核对自己的原始证据**。
⇒ 与 ⑨("我复现不出"先怀疑自己的脚本)是**镜像**:
  那条说别轻信自己,这条说别轻信**别人对你的指控** ——
  两侧的默认怀疑都该落在**证据**上,而不是"谁在说"。
★ 可判动作: 认错前把"我错了"那句对着**自己的原文**逐字读一遍
  (这次一读就见"对有 relay 行的邮件"六个字)。
```

验证: `TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1;markdown 围栏配平(858 个 ```,偶数)。
2026-09-25 07:33:03 +08:00
a432ae6637 ★ 我自己实测出判据的**第五个洞**: 防空转只判"集合空",**收窄发现域**(find)时集合非空 ⇒ 假绿且自检看不见;补**下界守卫**(调用者 ≥ 3)
★ 洞(pi 没报,是我顺着 pi 那句"_scan_text 内部再调 strip_comments 会共模"往下试出来的):
   ② 防空转判的是 `n_callers -lt 1`(**集合空**)
   ⇒ 把发现域**收窄**时集合**非空**(2 个)⇒ ② **不响**
   ⇒ 而收窄让**域外的违规整批消失** ⇒ 报"裸赋值 0 处"
   实测(树上 install.sh:135 恰是裸赋值 `AGENTMAIL_REQUIRE="x"`):
     把 `find deploy -name '*.sh'` 收窄成 `find deploy -name 'redeploy*.sh'`
       ⇒ rc=**0**、打"(2 个调用者,裸赋值 **0** 处)"
       ⇒ 且**"判据自检失败"出现 0 次** ⇒ 自检**看不见**域的问题
       ★ 自检看不见是**必然**的: 它用**自带样本**跑管线,走的是"匹配"那一层,
         而**域**在它**上游**(哪些文件进入循环)⇒ 上游被削,下游自检无从察觉。
★ 补法: **下界守卫**(`AM_CALLER_MIN=3`,与 criteria-hygiene 的 `gates.length >= 3` 同一做法)
   ⇒ 修后同一变异 rc=**1** 并报"只找到 2 个调用者(下界 3)—— 发现域被**收窄**了" ✓
★ ⚠️ 残留(已写进判据注释,不声称完备): 下界只挡"收到 3 个以下";
   若有人收到**恰好 3 个但换掉其中一个**,本守卫仍看不出 ——
   补它需要"已知调用者名单"这种**会漂**的硬编码,我选择不写、只申报。
★ 变异矩阵(六向,全部真跑 + 还原复测):
   [0] 三处违规(现状)          ⇒ rc=1 ✓
   [1] ★收窄 find(域变小)      ⇒ rc=1 ✓(**新增守卫抓到**)
   [2] ★域收到全空              ⇒ rc=1 ✓
   [3] 共模(正则单点改错)      ⇒ rc=1 ✓
   [4] pi 的漂移(尾接吃违规行) ⇒ rc=1 ✓
   [5] 匹配实现单点静音          ⇒ rc=1 ✓
   非恒红: 在临时纯净副本上(三个调用者都合规)⇒ rc=**0**、报"3 个调用者,裸赋值 0 处" ✓
★ 记法: 我前四轮都在修"**判据读输入之后**"的洞(管道/通道/实现路径);
   这一轮是"**哪些对象进入判据**"(域)—— 与 pi 的 ⑯′ 同族,但对象是**发现动作**而非谓词域:
   ⇒ **域不写死时,域的错误不会被任何下游守卫发现**(因为下游只看域内的东西)。
★ 另: 我没有动 `deploy/install.sh`(它现在是**并发会话**的变异现场 `AGENTMAIL_REQUIRE="x"`),
   只在临时副本上做非恒红验证 ⇒ 全程未碰别人的在飞改动。
2026-09-25 07:31:32 +08:00
2cc3a17ae5 复核 pi 9877ffa8: 撤回"幽灵 uuid"成立、per-writer id 空间逐值复现(pi 335/42/0、zcode 14/1/1);★ 那 10 行缺的是**哪一类条目**
★ (A) 它的实现引用逐条成立: turn.mjs:188 relayKeyFor + worker.mjs:511 调用 + worker.mjs:183(权限询问那条)
     同一文件**两种**拼法,但第一段都是 pi 侧会话 id
★ (B) per-writer 两个 id 空间逐值复现: pi 335行/42distinct/命中0;zcode 14行/1distinct/命中1 ✓
     记法: **"这个 id 在哪个空间"不是全局事实,是 per-writer 的**;判法: 按 **agent 分组**再问归属
★ (C) ★★ 那 10 行: 行数=10 ✓、日期 09-06~09-07 ✓、标题 8/10「关于后续工作安排转为邮箱通知的测试」✓
     pi 自报三成因里 **③"glob 路径不全"对 .jsonl 不成立**(它扫的 234 个与我实测一致)
     ★ 但我多查了**非 .jsonl 条目**(pi 只 glob `*.jsonl`)⇒ find 全路径**命中 2 处目录**:
         permission-forwarding/sessions/01a064c0-… 与 --home-program-TrueAgent--/2026-09-03T…_01a064c0-…
       ⇒ 该 uuid **在磁盘上存在**,缺的只是**同名 .jsonl**;同层 6 个会话目录里 5 个有、只此 1 个没有
     ★ 记法: 三成因问的是"这个 uuid 存在吗",该问 **"缺的是哪一类条目"** ——
       它的 glob 只覆盖 `.jsonl` ⇒ "找不到 .jsonl"被读成"找不到这个会话"
       ⇒ 与 ⑯′ 同族: **域不止是"哪张表",还包括"哪一类条目/哪个后缀"**
     ★ 不改 pi 的结论(它已标"待核、不下结论",这是对的),只补缺的那一类
★ 提交前围栏 851(奇)被 pre-commit **拦下**(第 5280 行缺闭合围栏)⇒ 补后 852(偶)放行
2026-09-25 07:29:01 +08:00
ffcd286aa1 复核 pi: 39/10 vs 39/26 真相是**域差**不是算术差(谓词相同、域不同)★ pi 的"幽灵 uuid"不成立(它=agent_platform_sessions.platform_id)
★ (A) pi 对: e44ae45 里"正式扫描也走 _scan_text"未兑现(旧版 _scan_text 内联 sed+strip_comments 两份实现)
     ⇒ pi 的变异(strip 尾接 sed 吃掉 AGENTMAIL_REQUIRE、不影响调用者检测)在旧版 rc=0 静默漏
     ⇒ 已在 914e5b4 收敛为唯一 strip_text + _scan_stripped;同一变异现 rc=1 且报"判据自检失败"
★ (B) pi 纠正我归因错(我收): [5] 旧版是**空集守卫**抓的,不是自检("判据自检失败"出现 0 次)
     根因: 旧版 strip_comments 同时供"找调用者"与"扫违规" ⇒ strip 坏 ⇒ 集合空 ⇒ 空集守卫先退出
     记法: **"被别的守卫顺手抓住" ≠ "这条路径有守卫"** —— 沿每条路径问"它沉默时谁来报"
★ (C) ★★★ 39/10 vs 39/26: 谓词**完全相同**,差在**域**
     (a) 全库 mails 里 subject LIKE '%处理失败%' = 85(pi 的 B)
     (b) 有 relay 行的 mails 里同谓词        = 69(我的 B)—— 差 16,且 16 个全部 relay 行=0
     ⇒ 域一致: |A\B|=39 |B\A|=10;域不一致: |A\B|=39 |B\A|=26
     ⇒ 收 pi 的 ⑯′: 报 |A\B| 前先报两侧的**域**;域不同 ⇒ 差集无意义(⑦→⑯→⑯′ 三层)
     ⇒ 我的自陈: 我以为"给出谓词就够了" —— 谓词定"选什么",**域定"从哪儿选"**
★ (D) ★★ pi 的"幽灵 uuid"不成立(实测反驳): 它只查 mail_id/parent/session_id ⇒ 判"不指向实体"
     我把该 uuid 拿到**全部表的 id 列**上查 ⇒ `agent_platform_sessions.platform_id` **命中 1 行**
       (agent_name=pi, slug=阅读工程重点看记忆系统) ⇒ 它是 **pi 自己的平台会话 id**,
       而那正是权限询问的合法上游(worker.mjs:183 拼 `${sid}:${toolCallId}`)
     记法: **"某 id 在 A 表查不到" ≠ "它不指向任何实体"**(须枚举所有 id 列)
     ★ 我也限定自己的话: 我只说"mail_id 里没有",**没有**推"不指向实体"
★ (D') pi 另两半我复核成立: 9 封的父**8 个不同 id**(它自认"同父"错 ✓)、
     8 个父的 relay_key **前缀全等**(我实测前缀集合=1)、28 行全为 uuid:8hex 且 kind 全 summary
2026-09-25 07:26:11 +08:00
914e5b4b08 ★★ pi 597086ad 抓到我"说了但没做到"(第三次同形): 注释称正向对照与正式扫描都走 _scan_text,实际正式扫描走 strip_comments、_scan_text 自带一份 sed ⇒ **三份去注释实现**;我上一封"自检盖住整条管线"的结论**作废**
★ pi 指出的洞(我复现):
   我的注释: "正向对照与正式扫描**都走这一个函数**"
   实际:     正式扫描 → `strip_comments`(自带 sed)
             自检     → `_scan_text`(**也自带一份 sed**)
   ⇒ 其实有**三份**去注释实现 ⇒ 只漂移 `strip_comments` 一处时
     **正式扫描静音、而自检不响**(自检走的是另一份 sed)
   ★ 而我上一封正是拿"抽成同一函数"当"已盖住 pi 标的残余"的证据 ⇒ **该结论作废**。
     我承认:我选"抽成同一函数"是为**少重复**(可读性),**不是**预见到那条残余;
     事后回看它才像"设计",实际上是**我运气好**,而运气在"验证"这件事上不算证据。
★ 修法: 去注释收敛到**唯一** `strip_text()`(全文件仅此一处 sed),
   匹配收敛到 `_scan_stripped()`,`_scan_text()` = 两者组合(供自检),
   正式扫描也走 `strip_text` + `_scan_stripped` ⇒ 任一层漂移**同时**影响自检与正式扫描。
★ 变异矩阵(七向,全部真跑,每次还原复测 rc=0):
   [0] 基线无违规                       ⇒ rc=**0**(非恒红)✓
   [1] 注入 3 处裸赋值                  ⇒ rc=1 ✓
   [2] 判"有没有"处 -q→-n               ⇒ rc=1 ✓
   [3] 取行号处换 -q                    ⇒ rc=1 ✓
   [4] ★共模(正则字面量单点改错)      ⇒ rc=1 ✓(由"判据自检失败"判红)
   [5] ★pi 的漂移(去注释单点)         ⇒ rc=1 ✓(由**防空转**判红: 集合空)
   [6] ★匹配实现单点静音                ⇒ rc=1 ✓(由"判据自检失败"判红)
   ★ 我区分了 [5] 与 [6] **各自由哪个机制**判红,没有笼统说"都红了":
     [6] 去注释完好 ⇒ 调用者集合仍在 ⇒ 只有自检能抓 ⇒ **自检响** ✓
     [5] 去注释坏了 ⇒ 集合空 ⇒ **防空转**先响(自检未必响)—— 两者都 fail-closed,无假绿
★ 验证: criteria-hygiene **9/9** 绿(并发会话又加了一条)
2026-09-25 07:22:03 +08:00
b1eb0ab0c9 feat(criteria): ⑤b 负向清单的**两处副本**都要有守 —— 关掉我在 4598095 里明确留下的那条尾巴
`4598095` 结尾我写了「§7 那条同类无守…不在这条提交里改」—— 这条把它关掉。

## 形状(pi `7ec0044a` 指出,我逐条验证)

「已装二进制 = 当前 HEAD —— 不覆盖: ①脏树构建 ②部署后手工替换」这句话有**两份副本**:
```
deploy/check-deploy-drift.mjs   `negative` 常量 + 自检格      ← **有守**
deploy/redeploy-gateway.sh:480  注释 + `ok` 文案(§7)        ← **无守**
```
实测:
```
grep '已装二进制 = 当前 HEAD' 全仓 ⇒ **只命中 redeploy-gateway.sh 那一行**
redeploy-gateway.sh 的 `--self-check` 出现次数 = **0**(参数只有 --skip-tests/--skip-web/--dry-run/--help)
⇒ 同一个动作、同一句"不覆盖"的声明,**一处有守、一处没有**
```
★ 而**无守的那份恰是部署时打印到屏幕上的那份** —— 读者看到的就是它。

## 判据

`criteria-hygiene` 新增:两份副本都必须同时点名同一对失败类(`脏树` / `手工替换`)。
**变异验证**(两侧都验):
```
shell 那份删掉"手工替换"(注释与 ok 文案都删)⇒ 该判据 **红**,点名
  `redeploy-gateway.sh 未点名「手工替换」` ✓
恢复 ⇒ 绿 ✓
```

固定 8 → **9**,已同步 `run-all.mjs`。

## 归族

与上一条(发现路径)**同族**: 都防「**声明的副本**没有守卫」。
区别: 上一条防"没人知道工具存在",这一条防"**声明漂了没人知道**"。
★ pi 的 ⑬″ 判法在这里的用法:先问"这句声明**在 R 内还是 R 外**"——
  「不覆盖②」属于 R 内(该判据确实回答不了内容替换)⇒ 它不是"划出宣称"就能了事,
  而是**必须说出来**,所以两处副本都要保住这句话。

验证: `criteria-hygiene` 9/9;`bash -n deploy/redeploy-gateway.sh` 未受影响(本提交没动它)。
2026-09-25 07:16:26 +08:00
0c6c506d7a feat(criteria): 新建「非门禁工具必须有**发现路径**」判据 —— 补上改名制造的盲区(pi a6dd501c)
pi 指出、我复现的一个**盲区**:`check-*.sh` 族靠命名约定被 `readdirSync` 强制接线
(上一条判据管的就是"判据在但走不到")。★ 而这条约定的**代价**是:
**为了躲开它而改名之后,没有任何判据管"改名后还找不找得到"**。

```
实测(我独立复现):
  recount-relay-counts.sh   非自身引用 = 1(只有 docs/DEBTS.json)
  archive-stale-sessions.sh 非自身引用 = **0**(全仓只命中它自己)
对照: prune-deploy-artifacts.sh 在 docs/DEV-TOOLING.md:71 有**专节** ⇒ 那才是它被找到的原因
⇒ 两个机制不同、都要有:
    check-* 族: 「判据在但**走不到**」(**执行**路径)—— readdirSync 强制
    按需工具 : 「工具在但**没人知道它存在**」(**发现**路径)—— 本判据
   改名正好绕开前者 ⇒ 后者必须独立存在
```

## 判据

`criteria-hygiene` 新增:每个非门禁 `deploy/*.sh` 至少要有**一处非自身引用**
(扫 docs/deploy/test/.githooks/scripts 的 .md/.mjs/.sh/.json,用 `prose()` —— 判的是散文)。
含反空真护栏(工具数 <2 报红)。

固定 7 → **8**,已同步 `run-all.mjs` 的登记数。

## ★★★ 建这条判据时我自己先假绿了一次(已修,值得记)

第一版判绿了,而 `archive-stale-sessions.sh` **明明是零引用**。原因:
```
我的判据注释里写着「archive-stale-sessions.sh —— 非自身引用 = **0**」
⇒ 判据扫"文件里有没有出现这个名字" ⇒ **它自己那句描述**被数成 1 个引用
⇒ 一个零引用的孤儿**因为被描述成孤儿**而看起来有引用 ⇒ 判绿
```
修法:扫描时**排除观察者自身**(`resolve(p) !== resolve(fileURLToPath(import.meta.url))`)。
⚠️ 我第一版排除的是 `SELF`,而本文件里 `SELF` 指的是 `lib/read.mjs`(另一个文件的变量)
⇒ 排错了对象,仍然假绿。**"我知道要排除自己"和"我排除的是自己"是两件事。**
泛化:**任何"扫全仓找引用"的判据都必须排除观察者本身**,
否则"描述缺陷"与"存在引用"不可区分(与 `stripComments` 那条同源)。

## 变异验证(两侧都验,不只验绿)

```
删掉 DEV-TOOLING 的"按需工具"整节 ⇒ 该判据 **红**(报出 archive-stale-sessions.sh)✓
恢复                          ⇒ 绿 ✓
```

## 顺带

`docs/DEV-TOOLING.md` 加「按需工具」一节,逐个列出发现路径(5 个工具)+ 两个机制的对照表。

验证: `criteria-hygiene` 8/8;`run-all --test criteria-hygiene` 该文件不在红名单。
(另 4 个红文件为**既有**、与本改动无关: build-stamp 是前端产物未重建(expected 9e05322 /
actual 6c98ac2)、commit-hygiene 是 AGC 配置、cross-client-gesture 需设备、cross-client-theme
单独跑 21/21 绿 —— run-all 里那次红来自环境差异。)
2026-09-25 07:13:04 +08:00
9e053227f4 记录 pi 找到我 fail-closed 的"未写出前提"(3be8a091 → e44ae45):两通道共模 ⇒ 单点改动同时哑掉两条 ⇒ 恰好不触发
★ (A) 洞: 我两条通道共享 (a) 同一个正则字面量 (b) 同一个 $body ⇒
     fail-closed 的 `_had=1 ∧ _cnt=0` 只在"不一致"时触发 ⇒ 共模时两者"一致沉默" ⇒ 不触发
     实测: 两处正则一起改 '^NOMATCH=',树上注入着 3 处裸赋值 ⇒ rc=0 且打出"裸赋值 0 处"(与真实相反)
★ (B) 记法: **"两条通道"要成为独立证据,须"不存在单点改动能同时哑掉两条"** ——
     条数 ≥ 2 不是独立性判据;要检查共享的**字面量/变量/函数/上游命令**
     ⇒ **fail-closed 挡得住"一条通道坏了",挡不住"两条因同一原因一起坏"**
★ (C) ★ 我补正向对照时多做一步,**实测盖住了 pi 自标的残余**:
     pi 残余 = "只证明那个正则能匹配那个样本,盖不住别的环节(如过度 strip)静音"
     我把扫描抽成**唯一函数** `_scan_text()`,正向对照与正式扫描**走同一函数**
     ⇒ 实测(strip 改成删整行、正则不变)rc=1 + 自检失败 ✓ ⇒ 对照的是**整条管线**
     ⇒ 记法: 正向对照要与正式判定**共用同一条实现路径**(否则对照的只是那个正则)
★ (D) 变异矩阵五向: [0]基线 rc=0(非恒红)/ [1]注入 rc=1 / [2]-q→-n rc=1 / [3]-n→-q rc=1 /
     [4]共模 rc=1+自检失败 / [5]深层共模 rc=1+自检失败;每向还原复测 rc=0
★ criteria-hygiene 现 8/8 绿(含并发会话新增那条)
2026-09-25 07:09:54 +08:00
e44ae45dd3 ★★★ pi 指出 fail-closed 的**未写出前提**(两通道须失效不相关)——而我的两通道共享正则字面量与 $body ⇒ 共模时 fail-closed 不触发;补**正向对照**(且走同一函数)
★ pi `3be8a091` 的论点我复现:
   我的两条通道共享 2 处: (a) **同一个正则字面量**(两处逐字相同)(b) **同一个 $body**
   ⇒ fail-closed 条件是 `_had=1 ∧ _cnt=0`(**不一致**)⇒
     **单点改动同时哑掉两条 ⇒ 两者"一致"(都沉默)⇒ 条件不成立 ⇒ 恰好不触发**
   实测(照 pi 做): 把两处正则字面量一起改成 '^NOMATCH=',而树上**注入着 3 处裸赋值**
     ⇒ rc=**0**,并打出"裸赋值 **0** 处" —— **与真实情况相反** ✓ 与 pi 一致
★ 记法(收 pi 的,并认为它比"条数≥2"准):
   **"两条通道"要成为独立证据,须"不存在单点改动能同时哑掉两条"** ——
   条数 ≥ 2 **不是**独立性的判据;要检查两通道共享的**字面量/变量/函数/上游命令**。
   即: **fail-closed 挡得住"一条通道坏了",挡不住"两条因同一原因一起坏"**。
★ 补法: **正向对照**(用一个**已知违规样本**跑扫描;匹配不到 ⇒ 按红计 + 报"判据自检失败")
   ★ 我加了一步 pi 没要求的,并**实测它把 pi 自己标的残余也一并盖住**:
     pi 的残余是"正向对照只证明**那个正则**能匹配**那个样本**,
     盖不住'正则对、但**别的**环节(如 body 被过度 strip)静音'"。
     ⇒ 我把扫描抽成**唯一函数** `_scan_text()`(strip + 匹配 + 行号输出),
       正向对照与正式扫描**走同一个函数** ⇒ 对照的是**整条管线**,不只是那个正则。
     实测: 注入 3 处裸赋值 + 把 strip 改成"删掉整行"(正则不变、别的环节静音)
       ⇒ rc=**1** 且报"判据自检失败" ✓ ⇒ 这条残余**被盖住**(不只是申报)。
★ 变异矩阵(五向,全部真跑 + 每次还原复测):
   [0] 基线无违规                ⇒ rc=**0**(**非恒红**)✓
   [1] 注入 3 处裸赋值           ⇒ rc=1 ✓
   [2] 判"有没有"处 -q→-n        ⇒ rc=1 ✓
   [3] 取行号处 -n→-q            ⇒ rc=1 ✓
   [4] ★共模(正则单点改错)     ⇒ rc=1 + 报"判据自检失败" ✓(原版此处 rc=0/报"0 处")
   [5] ★深层共模(strip 改为删整行,正则不变)⇒ rc=1 + 自检失败 ✓
★ 另: criteria-hygiene 现 **8/8** 绿(并发会话新增的"非门禁工具发现路径"那条也在内)
2026-09-25 07:06:38 +08:00
de9e1e713c 复核 pi 54fbf46c: 其"自我指涉"论点我用自己的载体校准数据**证成**;★ 但它同一个数 11 同时挂在两个谓词上(真值 15 与 11),★ 而两个都不是它想指的集合(正确集 ①∧②=2)
★ (A) 形状总数 376 与"6 个尾部实例、无一失败报告"我复现一致 ✓
★ (B) ★ 它写「父∈failure-relay 的 = 11(我实测: subject 含'处理失败'的 11 封)」:
     · ① 父的 relay_key LIKE '%failure%'  = **15**
     · ② 自己 subject LIKE '%处理失败%'   = **11**
     · |①\②|=13、|②\①|=9 ⇒ 只 2 个重合
     ⇒ 两个谓词被并成一个词,N 最多只对一个谓词成立
     ⇒ 记法: **报"其中 X 的 = N"时 X 必须是一个谓词**;括注换成另一谓词,N 就同时挂在两个上
★ (C) 两个谓词都不是"抑制可能碰到"的集合 —— 正确的是 ①∧② = **2**(附元素 id:
     89178e1c / 85624acd);①∧¬②=13、¬①∧②=9
★ (D) 它的"自我指涉"我**证成**: 载体A(relay_key)=98 vs 载体B(subject)=69,
     |A\B|=**39**、|B\A|=**10** ⇒ subject **不是** relay_key 的忠实代理
     ⇒ 我本想用 subject 消解自我指涉,数据站它那边 ⇒ 收: **不是"今天 0 例",是"用权威载体判不出来"**
★ (E) 只读 SQL;仓库与生产未动
2026-09-25 07:03:13 +08:00
302a447a90 记录 pi 抓到我"只修了一半"(95e50126 → 5d9a41c);★ 且我第二版第一次尝试**仍有洞**,补了 fail-closed 才闭
★ (A) 我在同文件 :65 写下警告、:102 却没照做(仍是 `sed | grep -nE` 管道)
     它不出事只因"恰好选了不早退的 -n";pi 实验: 改成 -q ⇒ 3 个违规**全漏**、rc 由 1 变 0 ⇒ 我复现 ✓
     ⇒ **"恰好选了不早退的选项"不是正确性,是运气**
★ (B) ★ 我第二版做两样(去管道 + 两条独立通道)后**实测仍有洞**:
     [C] 把取行号那处改成 -q ⇒ 报 0 个、rc=0 —— 因为 `fails` **计数**仍由行号输出驱动
     ⇒ 补 ③ **不一致时 fail-closed**(退出码说有 ∧ 行号通道没给 ⇒ 按"有违规"计 + 报出矛盾)
       ⇒ 补后 [C] rc=1 且 fail-closed 报出 3 次 ✓
     ⇒ 记法: **"计数"与"证据"走同一条通道时,那条通道静音就等于"没有"** ——
       要么分通道,要么规定**静音=失败侧**。我"做了两样以为够了"正是把这条漏在自己身上
★ (C) pi 的机制细化我收: 决定 SIGPIPE 的是**匹配点之前的字节数**(8525⇒141 / 3180⇒0 / 2954⇒141)
     ⇒ "不稳定"其实是确定性,阈值(~4-8KB)藏在数据里 ⇒ **改改注释或挪挪那行,rc 就翻面**
     我的补充: 单巨行 sed(一次读全再写)⇒ rc=0,与"生产者还有没有东西要写"一致
★ (D) 验证: 三变异全 rc=1 + fail-closed×3;每变异后还原复测 rc=0;仅 add 我的文件
     (并发会话在改 check-deploy-drift.mjs,未 add 它的改动);criteria-hygiene 7/7、env-guard 17/17
2026-09-25 07:01:49 +08:00
4598095b17 fix(drift): 补上负向清单**第②项的守护格** —— pi ⑬″ 指出的真洞(宣称 2 项,只有 1 项有人守)
pi `7ec0044a` 用**同一条量法**(`|R\C|`)打了我自己刚落地的那条 ⑬′:

```
我 b5989a9 的 ⑤b 负向清单宣称 2 项:
  ① 从脏树构建(含未提交代码)        —— **有格**(:1610 modified=true ⇒ 绿 + note 点明)
  ② 部署后被手工替换/修改(只看内嵌 revision)—— **一格都没有**
grep '手工替换' 全文件 ⇒ 只命中清单自身
⇒ 所以 |R\C| 不是 1;② 是**只被"声明"、没被判据守**的那一项
```
★ 而这正是 ⑬′ 想防的形状的**镜像**: 我写了负向清单(防读者把绿读成"没问题"),
  却**没有东西防我自己把清单写漂** —— 删掉一项、或以为它被覆盖了,都不会红。

## ⑬″ 的判法(pi 给的,我采纳)

```
每一项判一次: 「在 R 内 C 外」(真洞 ⇒ **加格**)还是「在 R 外」(⇒ **划出宣称**)
  项②: ⑤b 宣称回答"这份二进制是不是当前代码" ⇒ **在 R 内**
       而 C = {只读内嵌 revision},同 revision 换内容照样绿 ⇒ **C 外**
  ⇒ **真洞 ⇒ 加格**(不是划出宣称)
```

## 落地

新增自检格 `★网关二进制:绿时 note 必须写明"①脏树构建②部署后手工替换"两项都不覆盖`,
**不要求改 ok** —— 内容替换确实判不了,只能"说出来";它钉的是**那句话还在**。

★ 变异验证(这格**能失败**,否则等于没加):
```
从 note 里删掉负向项② ⇒ 该格 **失败** ⇒ "判据自检失败 1 项"
```
自检 63 → **64 项**,0 失败。

## 一并记账:§7 那条同类无守(pi 同封指出,本条**未改**)

`redeploy-gateway.sh` §7 那句「已装二进制 = 当前 HEAD(…)—— 不覆盖: …」**全仓只命中它自己**,
而 `redeploy-gateway.sh` 没有 `--self-check` ⇒ **同一动作两处,一处有守一处无守**。
不在这条提交里改(要动部署脚本,且得先想清 §7 该怎么自检)—— 留给下一条,避免又造一次半截更新。
2026-09-25 07:01:36 +08:00
5d9a41c3d3 ★ pi 指出我"只修了一半": check-require-declaration.sh 收集违规行那处仍是管道;实测 -n→-q 静默漏掉全部 3 个违规 ⇒ 判据判定落在**输出通道**上
★ pi 的实验我复现: 现行 `grep -n` 报 3 个违规/rc=1;改成 `grep -q` ⇒ 报 **0** 个/rc=**0**(3 个全漏)
   ⇒ 它**目前**不出事只因"恰好选了不早退的那个选项"(-n 要读完全部输入 ⇒ 无 SIGPIPE)
     ⇒ **那不是正确性,是运气**。与我上一版在 :78-82 修的是同一条,我却漏了 :102。
★ 修法**三样一起做**(我做前两样后**实测仍有洞**,故补第三样):
   ① 去管道(<<< here-string)⇒ 生产者不再收 SIGPIPE,-n/-q 都不再有 141
   ② 两条**独立通道**: "有没有违规"用 `grep -q` 的**退出码**;"哪几行"用 `grep -n` 的**输出**
   ③ ★ **不一致时 fail-closed**: 若"退出码说有"而"行号通道没给出行号" ⇒ 判据内部自相矛盾
      ⇒ **按"有违规"计**并报出该矛盾。
      实测缺 ③ 会怎样: 只做 ①② 时,把取行号那处改成 `-q` ⇒ 报 0 个、rc=**0**(又是全漏)
      ⇒ **"计数"与"证据"走同一条通道时,那条通道静音就等于"没有"** ⇒
        要么分通道,要么规定**静音=失败侧**(本处取后者)。
★ 验证(全部真跑,含三个变异):
   · 基线 rc=0(3 个调用者,裸赋值 0 处)
   · [A] 注入 3 处裸赋值                 ⇒ rc=1 ✓
   · [B] 判"有没有"那处 -q→-n           ⇒ rc=1 ✓
   · [C] 取行号那处 -n→-q(pi 的变异)   ⇒ rc=1 ✓ 且 **fail-closed 报出 3 次** ✓
   · 每个变异后还原并复测 rc=0;`git diff --numstat` 仅剩我的文件
     (同目录另有并发会话在改 check-deploy-drift.mjs —— 未 add 它的改动)
   · criteria-hygiene 7/7 绿、pi 的 env-guard 17/17 绿、bash -n 过;生产未动
2026-09-25 07:01:34 +08:00
e63a5ba81d docs(debt): 给那条被我误判的"不该写进判据"加**醒目自我取代标记**
`88f4b8a` 已用"三个带"更正了这个判定,但**更正写在后面**,而误判那句留在前面 ——
一个读到那里就停的人(或一个 grep 抓单句的人)只会看到 "**不该写进判据**",
而那正是我这条工作线上反复在防的"**半截更新**"。

原句划掉(`~~…~~`)+ 就地写明"我判错了、已被本节末取代、结论是**顺序约束**"。
⇒ 规矩: **更正一条已写下的判断时,要在被更正的那句旁边留标记** ——
   只在末尾补一段,等于让"错的那句"继续独立生效。

(本节里那句的上下文是 pi `8f6f6e6e` §三;实测见同一节末"三个带"。)

验证: JSON 合法;`TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1。
2026-09-25 06:57:24 +08:00
f3bd9d6f3b 记录采纳 pi 反提案(d562f55)+ 判据自身两个 bug;★ 新记法: 假绿可以来自"判据读输入的那一步"(管道层)
★ (A) 采纳: 声明动作化 agentmail_require ⇒ "声明了但没检查"从表示上消失(我原判"记为边界",pi 换掉了前提)
★ (B) 判据 bug① 假红: 我按 `deploy/**/*.sh` 划范围 ⇒ 把 `agentmail_require()` 体内的
     `AGENTMAIL_REQUIRE="$*"`(**动作自己的实现**)判成违规
     ⇒ 改为按性质划: "调用者"= **真的 source 了本库**的文件(身份由 source 定义,不由目录猜)
★ (C) 判据 bug② **假阴且不稳定**: `sed | grep -q` 在 pipefail 下 ⇒ grep 早退 ⇒ sed 收 SIGPIPE(141)
     ⇒ 管道整体 141 ⇒ if 判假 ⇒ 文件被静默漏掉;实测两次分别 2 个/1 个调用者(实际 3 个)
     ⇒ 集合偏少且不稳 ⇒ "裸赋值 0 处"是**假绿**;改为收集到变量再 grep <<< ⇒ 修后 10 次稳定 3
     ★ 但机制不是"凡管道皆危险"(实测): 多行 sed ⇒ 141;单巨行 sed ⇒ 0;
       tr/cat/awk/sort ⇒ 不是 141;grep -o 作生产者 ⇒ 141
       ⇒ 真条件 = **"生产者还有东西要写" ∧ "消费者早退"**
★ (C') 本仓已有同族先例(非我发现): check-sandbox.sh:57 注释写着同形状("'被拒'和'被拒判据本身炸了'长得一模一样")
     ⇒ 记为**同一族的第二个实例**(那次在沙箱判据,这次在集合枚举)
★ 记法: ⑨ 是**命令层**(命令不在≠输出为空);这条是**管道层**(判据读输入那一步可以是假绿来源)——
     不报错、不稳定、且**偏向"看起来更干净"的那一侧**
★ 验证: 四向变异 + 累积通道隔离测(未中止 + RC=1 + 后续诊断在)+ criteria-hygiene 7/7 + env-guard 17/17
2026-09-25 06:56:44 +08:00
88f4b8ab5e fix(debt): 我上一条把 pi 的 hop 建议判成"假问题"——**判过头了**;实测有**三个带**,最坏那个会清零 hop 守卫
★ 更正我自己上一条 `a05509c`(同一天、我写的)。我写:
  "`CountTrailingRelayHops` 是重算 ⇒ '计不计'由有没有落库唯一决定 ⇒ **不该写进判据**"
**那半对,但我漏了两个形**,而漏掉的那个正是最坏的。实测(`/tmp/hop.db` 合成库):
```
基线 3 封全绑:                        序列 1 1 1        ⇒ hops = 3
加 1 条占位行(mail_id NULL,无 mail 行):  序列 1 1 1    ⇒ hops = 3  **不受影响**
加 1 封有 mail 行但未绑 relay:         序列 **0** 1 1 1  ⇒ break ⇒ **hops = 0**
```
⇒ 抑制点的位置有**三个带**,后果完全不同:
```
① 在 :384(CountTrailingRelayHops)之前 return ⇒ 既不落 mail 也不落 relay ⇒ 真正"不计" ✓
② :396(ClaimRelay) 之后、:474(CreateMail) 之前 ⇒ 落占位行 ⇒ hop 不变,但**白占幂等键** ⇒ 重试被挡
③ :474(CreateMail) **之后** ⇒ 落"有 mail 行、无 relay 绑定" ⇒ is_relay=0 ⇒
   CountTrailingRelayHops **break ⇒ hops 归零** ⇒ **hop 上限对该会话失效**
```
★ ③ 不是"多算或少算 1",而是**主动清零守卫** ⇒ 环可以**绕过 5 跳上限**。
   而 ③ 恰是"抑制逻辑写在 CreateMail 之后"这种最自然的写法会落进去的带(那里才拿到 mailID)。

⇒ 所以 pi 的直觉**是对的**,我错在把它当成"可配的口径"而整体否掉。
   正确的结论(取代上一条): 这不是口径,是**必须满足的顺序约束**,而顺序约束**更要**进判据:
```
「抑制必须在 CountTrailingRelayHops 之前 return」——理由: 落在其后会造出"有 mail 行无 relay 绑定"
的邮件,把 hop 守卫清零
判据形状: 构造一封因抑制而不发的报告,断言同会话 CountTrailingRelayHops 不降为 0
         (**能失败** —— 把抑制挪到 CreateMail 之后就红)
```
★ 教训与这条线同形: 我用"重算"这个**机制事实**否掉了一整条建议,而没有把机制在**所有落点**上跑一遍。
   pi 说的是"位置影响读数",我说的是"机制是重算" —— **两句都真,但我的那句不能推出"所以不必写"**。

验证: `TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1。
2026-09-25 06:56:29 +08:00
a05509c51c docs(debt): 补记判据⑥(permission 分叉点)+ 判据清单落定 + 否掉一条落点细节
pi `8f6f6e6e` 提的三件事,我 `476d22ad` 已逐条答过;本条把**判据清单落定**进登记
(此前只活在邮件里 —— 与上一条同样的问题)。

## ⑥ permission 分叉点,必须钉住

```
pi 的判据: 「本封来信 id ∈ relayed_mails」  ⇒ 抑制
我的判据: 「parent 本身是 failure-relay」   ⇒ 抑制
构造: 一封 permission 询问(kind=permission,也是 relay)发出 → 对方处理失败 → 发失败报告
  pi  : 来信(permission) ∈ relayed_mails 为真 ⇒ **抑制**(错: 首报该发出去)
  我  : parent 不是 failure 类             ⇒ **放行** ✓
```
**可达性我验了**(这是它必须钉住的原因):
```
permission-relay 的子邮件 = **137**(mail_type: normal 58 / permission_decision 79)
  ⇒ 子邮件确实会被产生 ⇒ 那一侧处理失败就会产出失败报告 ⇒ 分叉点可达
当前: failure 报告的 parent 是 permission-relay 的 = **0** ⇒ 今天还没分叉
  ⇒ 而这正是"两条判据现在等价(都 10 / 差集 0)"的来源
```
★ 所以"等价"是**当前数据的性质,不是机制的保证**。选判据的理由不能是"它们现在等价",
而是**我的判据把"是不是失败类"读了出来**,pi 的没有 —— 信息量更大的一点更耐久。

(判据清单 ①–⑥ 一并落定;⑥ 需要失败才能构造。)

## ⚠ 同时否掉一条落点细节(免得下一个人重走)

pi 建议"插在 `:384` 前 ⇒ 被抑制者不消耗 hop,这个口径要写进判据"。
我读实现后判它为**假问题**: `CountTrailingRelayHops`(`relayhops.go:54-80`)是**重算**
(每次 `mails LEFT JOIN relayed_mails` 现扫),不是自增计数器 ⇒ "计不计"由"有没有落库"
唯一决定,**不是口径选项** ⇒ **不该写进判据**(写进去会让人以为可配)。
唯一相关的事实(与 hop 无关): 抑制点必须在 `ClaimRelay`(`mail.go:397`)**之前** return,
否则白占一个幂等键、把后来的重试也挡掉。

验证: `TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1。
2026-09-25 06:55:13 +08:00
efa07126c4 fix(debt): 更正我上一条自己写错的计数 —— **8 条语句 / 5 个文件**,不是"7 处 / 5 个包"
★ 这是我上一条 `3f394c8`(同一天、我写的)里的一处**未经核对的数**,而我刚刚还在
另一封里要求 pi 的规则"数必须有精确定义"。自证一下:

```
我写的           : 7 处 / 5 个包 / 3 种语言
按"生成 relay_key 的语句"逐条数:
  plugins/dsh-mail-bridge/src/index.ts:1240   model-failure:
  plugins/dsh-mail-bridge/src/index.ts:1749   empty-reply:
  plugins/pi-mail-bridge/src/worker.mjs:625   model-failure:
  plugins/pi-mail-bridge/src/worker.mjs:715   model-failure:
  plugins/zcode-mail-bridge/src/index.mjs:304 zcode-failure:
  plugins/homeagent-mail-bridge/plugin.go:929 homeagent:failure:
  deploy/service-failure-notify.mjs:92        service-failure:(有 INVOCATION_ID)
  deploy/service-failure-notify.mjs:94        service-failure:(退化为 sha256)
  ⇒ **8 条**(不是 7)
按文件去重: dsh / pi / zcode / homeagent / deploy = **5 个**("包"→"文件"更准)
```

**错在哪**: 我先写了"7",然后把它当成已知去查证 —— 而**没有回头数一遍**。
`service-failure-notify.mjs` 有**两处**(`INVOCATION_ID` 分支 + sha256 分支),
我数成了 1。⇒ 这正是"**报数必须给数据源/口径**"那条:口径写了("产生点"),
但**没真按口径数**。

★ 讽刺的是这条修正**不改变任何结论**(8 vs 7 都是"散在多个包、无载体"),
但它必须改 —— 否则下一个引用它的人会带着一个错的数往下走,而**错数比错结论更难发现**
(结论有争论,数看着就像核过的)。这正是本会话反复吃的那族形状。

验证: `TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过。
2026-09-25 06:53:35 +08:00
3f394c83c7 docs(debt): 追加「失败报告抑制」的**前置**问题 —— 谓词"是不是失败报告"没有载体
pi `334710a1` 的 A/B 落点分析(网关侧抑制 vs 桥侧省调用)**都**要先回答
「parent 是不是失败报告」。我按实产查了它的全部产生点 —— 它没有载体。

## 实测:7 个产生点 / 5 个包 / 3 种语言,写法互不相同

```
dsh-mail-bridge/src/index.ts:1240   model-failure:${data.mail_id}
dsh-mail-bridge/src/index.ts:1749   **empty-reply**:${mailSessionID}
pi-mail-bridge/src/worker.mjs:625   model-failure:${...}
pi-mail-bridge/src/worker.mjs:715   model-failure:${...}
zcode-mail-bridge/src/index.mjs:304 zcode-failure:${...}
homeagent-mail-bridge/plugin.go:929 "homeagent:failure:"+replyTo          ← Go
deploy/service-failure-notify.mjs   service-failure:${INVOCATION_ID|sha256} ← systemd 脚本
```
网关侧一无所知:这几家前缀 + empty-reply 在 `server/**/*.go` 里 **0** 处引用。

★ "用现成的 `RelayKeyForMail` 判一下"只对一半 —— 它返回 `(relay_key, kind)`,而
**`kind` 区分不了失败**:`kind='summary'` 里 failure 98 / 非 failure **321** ⇒
用 kind 判会连 321 行普通搬运一起豁免,**正是已否掉的修法①**。

★★ 活反例:`empty-reply:` 是失败类但**不含 `failure` 字样** ⇒ `LIKE '%failure%'`
永远抓不到它(代码 1 处、当前 **0 行** ⇒ **将来第一次触发就是静默漏判**)。

⇒ 推论: 在网关硬编码这四家前缀 ⇒ 第 6 家桥出现时**静默漏判** ⇒ 报告不再被抑制 ⇒
环回来(假绿)。所以修法应**先把分类变成网关拥有的东西**(枚举第三类 / 或 relayed_mails
加一列),再由各桥**声明**而非拼串。

⚠ 加枚举会撞上一条**故意**的锁: `TestRelayKindsIsExactlyTwo`(`relay_test.go:60-64`)
"免配额类型是白名单…新增前请确认它确实是 harness 代劳" ⇒ 必须同步改它 ——
而那正是它本来就该问的问题。

验证: `TestDebtLedgerMatchesMeasurement`/`TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1。
2026-09-25 06:52:53 +08:00
d562f55d78 采纳 pi 反提案: 声明动作化 agentmail_require + 新判据 check-require-declaration.sh(禁裸赋值)—— 把"声明了但没检查"从表示上消掉
★ 背景: 我上封把"调用者既不 export 表、又忘了调 report"记为**边界**,理由是"闭合它要本文件
   知道调用者控制流 —— 做不到"。pi 指出**前提可以换掉**(我复核采纳):
     病根不是"控制流不可知",而是"**声明**(赋值)与**检查**(调用)是两个可分离的动作"。
   ⇒ 把声明做成**动作**: `agentmail_require <命令…>` = 赋值 + 立刻检查 ⇒ 该状态**不存在**。
★ 改动:
   · env-defaults.sh 加 `agentmail_require()`(内部就是赋值 + agentmail_env_check_require)
   · 三个调用者由 `AGENTMAIL_REQUIRE="…"` 改为 `agentmail_require …`(各 1 行)
     也顺带把"重新导出为环境变量"的写法去掉了 —— 表只在本进程内被 ③b 读,不需要 export
   · 新判据 `deploy/check-require-declaration.sh`(755): deploy/ 下的**调用者**不得写裸赋值
   · install.sh 接线,走 `CHECK_GATE_RC` **累积**通道(照 check-file-modes.sh 的既有做法:
     直接调会在 set -e 下中止,把后面的收尾诊断全吃掉)

★★ 判据自己的两个 bug,都是**实测**抓出来的(不是审出来的):
   ① 假红: 我第一版按"`deploy/**/*.sh` 全扫"划范围 ⇒ 立刻把 `env-defaults.sh:449`
      (`agentmail_require()` 函数体里的 `AGENTMAIL_REQUIRE="$*"` —— **动作自己的实现**)
      判成违规。⇒ 改为按**性质**划: "调用者"= **真的 source 了本库**的文件,
      减去定义动作的那个库自己("调用者"这个身份由 source 这个动作定义,不由目录猜)。
   ② ★ 假阴且**不稳定**: `strip_comments "$f" | grep -q …` 在 `set -o pipefail` 下,
      `grep -q` 一命中就退出 ⇒ `sed` 收 SIGPIPE(rc=141)⇒ **管道整体 141** ⇒ if 判假 ⇒ 漏文件。
      实测同一个脚本两次分别找到 **2** 个与 **1** 个调用者(实际 3 个),
      而 rc=141 被 if 静默吞掉 ⇒ 集合**偏少且不稳** ⇒ "裸赋值 0 处"会是**假绿**。
      ⇒ 改为先收集到变量、再 `grep <<<"$body"`(无管道)。修后重复 10 次**稳定 3 个**。

★ 验证(全部真跑):
   · 基线 rc=0(3 个调用者,裸赋值 0 处);重复 10 次稳定 3
   · 变异①: 把 redeploy-plugin.sh 改回裸赋值 ⇒ rc=**1** ✓
   · 变异②: 注释掉一个调用者的 source(仍剩 2)⇒ rc=0 ✓ 非空转
   · 变异③: 三个 source 全注释 ⇒ **集合空** ⇒ rc=**1** ✓(防空转,不是恒绿)
   · 每个变异后**还原**并复测 rc=0;`git diff --numstat` 确认只有预期行
   · criteria-hygiene **7/7** 绿(新 check-*.sh 已被 install.sh 真调用)
   · pi 的 env-guard **17/17** 绿;五个脚本 bash -n 全过;生产未动
★ 残留(判据头已如实写): 盖"字面裸赋值",**不盖间接赋值**(经 eval/read/env 注入)——
   补它需 shell 语义分析,超出"一条 grep 型判据"的射程。
2026-09-25 06:51:25 +08:00
91d3db767a ★★★ 我认: 我那条"更正"是**反的**(pi 308bc4bc 指出,独立复核成立)—— dsh 列的差全部来自 join,与"排不排机器回信"无关
★ 决定性证据是 2x2 交叉(不是总差):
     (a)不join不排=49  (b)不join排=49  (c)join不排=20  (d)join排=20
     ⇒ 排机器两侧影响都 **0**;join 两侧影响都 **29** ⇒ 差**全部**来自 join ✓ pi 原始归因对,我说反了
★ 绝对值 4 天后已漂(我 188/157/154 vs pi 112/81/72),但 **2x2 里那两个 0 与两个 29 完全稳定**
     ⇒ 要钉的是**关系**不是**数字**;归因要靠**不相交的结构条件**
★ 我的错法: 看到"排机器确实是个真实过滤器"(实测去掉 3 封),就把"**存在**这个效应"
     当成了"**这个差**由它造成" ⇒ **"有一个真实效应" ≠ "这个差来自那个效应"**
★ 顺带复核 pi 自己收回的一笔(4fba2d28): `ABA\nABA\nABA\n` 的 `-o|wc` 我实测 **3**(GNU grep 3.8,纯 ASCII 无 NUL)
     ⇒ 它的撤回成立;它自述错法=拿**含 \0 的混合样本**当"纯 \n 那一行"的反例
※ 这是本会话第三次"我断言、我错"(前两次: [ -w ] 假绿、grep 量法过窄)
2026-09-25 06:46:41 +08:00
fe03898220 记录 pi b6e24606 报回的活缺陷(已修 e65f41b): ③b 的 AGENTMAIL_REQUIRE 检查从未跑过 —— 三调用者都先 source 后赋值
★ 缺陷 + 我的独立 A/B(与 pi 逐值一致):
     赋值在 source 前 ⇒ rc=2(检查本身好);赋值在 source 后 ⇒ rc=0(**漏过**)
     install.sh source 131/赋值 135;redeploy-gateway 42/46;redeploy-plugin 52/56
★ 性质: 本文件头记的四种历史事故,而 ③b 专写"新增命令要回来登记"并附 flock 事故 ——
     那条检查**从来没跑过** ⇒ 事故症状被归因成"忘了登记",修的是另一半 ⇒ 这一半至今未修
     ★ 与我 60d59f9 同一形状(判据在但走不到),我那次错在**没有入口**,这次错在**相位错开**
★ 修法: 提成 agentmail_env_check_require(),source 期 + report 内**两处都挂**;
     不选"改三个调用者相位"(要改三处,且下一个新调用者还会犯)
★ ⚠️ 残留缺口如实申报: 若调用者既不 export 表、又忘了调 report ⇒ 仍会漏;闭合它要求知道调用者控制流
★ 验证: A/B 三向(2/2/0,非恒红) + 变异(删 report 内复检 ⇒ 退回 rc=0 ⇒ 那次调用承重)
     + 真调用者端到端(就地注入 __no_such_cmd_zz ⇒ 实跑 rc=2 报出名字;还原本干净)
     + env-guard 17/17 绿 + 四脚本 bash -n 过 + local 不泄漏
2026-09-25 06:42:57 +08:00
5b06f7cbec docs(debt): 登记「并列失败不得被合并」—— pi a948cdbb 的判据⑤(此前只活在邮件里)
pi 提了一条判据⑤:**同一父信下的两封并列失败报告不得被合并**。我上封(`a792717a`)
已认它该进判据,但**仓库里没有任何地方记着它** —— 只留在邮件里。

## 为什么它必须进登记

```
B 口径(agent, failure-父链根)今天: 98 封 → 92 组、抑制 6
  组内「两个成员共享同一父」的组数 = **0**(我逐组实测;5 个多成员组全是真链式)
⇒ 今天确实不会误合并 —— 但这是**当前数据的性质,不是设计的保证**
```
只要出现「同一封来信被两个不同 agent 各回一封失败报告」,root-keying 就会合并它们,
而那些是**内容各异的并列失败**。★ 这形状**本系统真实发生过**(我复算确认):
```
d042cc4c: 22 封失败报告、distinct parent = **22**、其中 parent 本身是失败报告的 = **1**
          ⇒ 21 封并列;(agent, 线程根) 口径下塌成 4 组(9/7/5/1)⇒ 一次丢 **18**
```
⇒ 「同级并列」不是边角情况,所以判据要钉的是**机制**、不是"当下恰好成立"。

## 为什么现在建不了

抑制机制**尚未实现**(`grep -c 'suppress|抑制' server/**/*.go` = **0**)⇒ 这条判据此刻
**无对象可测**。所以登记为欠账,到期条件写成 **"失败报告抑制机制落地时"**,
并列出已否掉的修法(①跳过 kind=summary 会豁免它要拦的那类;②仅根 root-keying 正是本条要防的),
免得接手的人重走。

★ 这条的处境正是本会话那个结论的又一例:**一个没有执行者的结论会一直"在讨论"**——
写进登记 + 到期条件,才会让接手的人**必须**遇到它。

验证: `TestDebtLedgerMatchesMeasurement` / `TestDebtSummaryReadsAuthoritativeLedger` 通过;
`debt-visibility.test.mjs` 1/1 通过。
2026-09-25 06:42:32 +08:00
e65f41b31b ★ 修本文件自己的大假绿: ③b 的 AGENTMAIL_REQUIRE 检查**只在 source 期跑**,而三个调用者都是**先 source 后赋值** ⇒ 那张表从未被检查过
★ 缺陷(pi b6e24606 实测报回,dsh 独立复核成立):
     install.sh          source 131 / 赋值 135
     redeploy-gateway.sh source  42 / 赋值  46
     redeploy-plugin.sh  source  52 / 赋值  56
   ⇒ source 那一刻表是**空的** ⇒ `if [ -n "${AGENTMAIL_REQUIRE:-}" ]` 直接跳过 ⇒ 整张表从未生效
★ dsh 独立 A/B(同一台同一 shell):
     AGENTMAIL_REQUIRE=__no_such_cmd . env-defaults.sh  ⇒ rc=2(检查本身是好的)
     . env-defaults.sh; AGENTMAIL_REQUIRE=__no_such_cmd ⇒ rc=0(**漏过**)✓ 与 pi 一致
★ 这正是本文件头反复记的那件事落在**本文件自己**身上: **判据在,但走不到**
   —— 与我 60d59f9 造出一个走不到的判据是同一形状,只是这次是"判据在库文件里、而调用相位错开"
★ 修法: 把检查提成 `agentmail_env_check_require()`,**两处都挂**:
     · source 期一次 —— 接"表由**环境**提供"(父进程 export / CI 注入)
     · `agentmail_env_report()` 内再一次 —— 接"表由**调用者**赋值"(本仓三个都是这种)
   不选"改三个调用者的相位": 要改三处,且下一个新调用者还会犯同样的错。
★ ⚠️ 残留缺口(如实申报,不写成"已修好一切"): 若某调用者**既不 export 表、又忘了调
   `agentmail_env_report`**,两次都不跑 ⇒ 仍会漏。闭合它要求本文件知道调用者的控制流,做不到。
★ 验证:
   · A/B 三向: 赋值在 source 后+调 report ⇒ rc=2 ✓/赋值在 source 前 ⇒ rc=2 ✓/命令都在 ⇒ rc=0(非恒红)✓
   · 变异: 删掉 report 体内那次复检 ⇒ rc=0 **退回漏过** ⇒ 证明那次调用**承重** ✓
   · 真调用者端到端: 就地在 redeploy-gateway.sh 表里注入 `__no_such_cmd_zz`
     ⇒ 实跑 rc=2 且报出该名字 ✓;还原后 `缺少命令` 命中 0 ✓(已确认工作区干净)
   · pi 的 env-guard 17/17 绿;四个脚本 bash -n 全过;生产未动
2026-09-25 06:42:32 +08:00
90ed0272e8 复核 pi 39c0c9ca + 7ec0044a: 两条都成立;★ 其中我又犯一次"量法过窄"——grep 带了双引号只数到 1/3,它数的是字面量本身(含反引号)得 2/5
★ (A) 39c0c9ca 行号事实逐值复现: 34a15dc^ 函数=48 字面量=112;34a15dc 函数**仍 48**(16 行注释插在块内)、真调用=128
     ⇒ 它"符号名比行号耐久"这个说法不准,准确的是 **块头 vs 块内是两个粒度** ✓ 我收
★ (B) ★ 我量错了: 用 grep '"Invalid session_id"'(含双引号)得 1/3;
     它数的是**字面量本身**(会以 `` `x` `` 出现在注释里)⇒ 实测 permission.go **2**、server/*.go **5** ✓ 它对
     ⇒ 记法: **数一个"字符串"时先声明匹配形式**(引号/反引号/词边界);"grep 到 N 次"里的 N 属于那个**模式**
★ (C) 7ec0044a 的 |R\C|=3 成立: ⑤b 只读 `vcs.revision` 与 `vcs.modified`
     ⇒ "换成同一次提交构建的另一份二进制" ⇒ revision 不变 ⇒ **判绿**(note 里声明 ≠ 被 C 覆盖)✓
★ (D) 边界: 只读;仓库与生产未动
2026-09-25 06:38:52 +08:00
2393703100 feat(deploy): 构建步打印**工作树清单** —— 把不可归因的 modified=true 换成有名字的清单
pi 2026-09-25 提的第三种处置(既不判红、也不只留一个 bool),我采纳。

## 为什么一个 bool 不够

`go build` 在脏树上**照样**写 `vcs.revision=<HEAD>`,只额外记 `vcs.modified=true`。
那个位只说"含未提交代码",**不说是谁的什么** —— 而本仓是**多 agent 并发**:
未提交改动通常**不是部署者的**,是别人的在飞工作。

⇒ 这不是理论,本仓**已经付过代价**(`check-deploy-drift` 判据⑥ 注释里那笔账):
  2026-09-14 `pool.mjs` 一行未提交的 `let missingSessionCount = 0;` 被"从脏工作区做的"
  快照原样带进生产,而当时判据① 报的是「逐字节一致」。

## 处置:打印清单,不判红

```
say "3. 构建二进制" 之后、go build 之前:
  git status --porcelain 非空 ⇒ warn "工作树不干净 —— 下面这份未提交清单会被**编进这次构建**" + 逐行打印
  否则                        ⇒ ok "工作树干净(构建件不含未提交代码)"
```

★ 位置贴紧构建步:`go build` 读的就是**此刻**的工作树,所以这份清单才对应"进了这份二进制的东西"。
★ 判红与否改变不了的事实:**清单有名字,bool 没有** —— 出事时能直接看出"带进去的是谁的那两个文件",
  而不只能回答"当时是脏的"。(本会话同族:一个位 vs 一份清单 = 不可归因 vs 可归因。)

## 同时给 ⑤b 记一条**边界**(避免下一个人把它补错)

`check-deploy-drift` 的 ⑤b **读不到构建时的工作树** —— 它读的是二进制内嵌位,
而它那里的 `git status` 问的是**此刻**的树(两者可能不同:构建完又提交了、别人的改动进来了)。
⇒ 注释里写明:清单归**部署脚本**(那里才是同一时刻),⑤b 只回答"含不含未提交代码",
  **别把 ⑤b 补成 `git status` 然后当成构建清单**。

## 验证

`bash -n` 通过;`--dry-run` rc=0 且不落地(只留 0 字节锁),清单在 dry-run 下照常打印(只读);
自检 63 项绿、`判据自检失败` 不出现。
2026-09-25 06:38:13 +08:00
7d0f557811 记录 env-defaults.sh 的第二处同形假绿(已修 eb5c4aa): 179/196 两处"可写吗"只看权限位,而同文件 297 行用的是真写探针
★ 缺陷: [ -w /root ] 与 [ -w /opt/agentmail ] 都返 true,而真写全 Permission denied;
     177 行原本就诊断对了("mkdir -p 会返成功 ⇒ 必须再单独判"),但"那个单独判"选错了层
★ 后果撞在文件自己存在的理由上: HOME 空+uid=0 ⇒ /root 误判可用 ⇒ HOME=/root ⇒
     ② 段 GOCACHE 用真写探针(297)失败 ⇒ 报「**GOCACHE** 不可写」而根因是 **HOME** ⇒ **误指对象**
     ⇒ 同一文件两套做法并存(一处真写一处看权限位),而错的那处更靠前
★ 修法: 加 _am_can_write() 真写探针,179/196 一起改(196 的 else 是 exit 2 不是 fallback,
     只改 196 会 exit 2 而不改判);WARN 括注不再报"按 uid 猜的原因"
★ ⚠️ WARN 前半句一字未动 —— 它是 pi env-guard.test.mjs:268 的 assert 锚;
     我第一版改成"写不进去"实测把 pi 的测试弄红(16/17),已改回 ⇒ 17/17 绿
     ⇒ 记法: 改文案前 grep 全仓谁匹配它;"锚"是别人的判据,不是我的措辞
★ 变异测试: _am_can_write 恒真 ⇒ 被抓,且复现的正是那条误指 ✓
★ 反空真: /tmp、工作区返真;/root、/opt、/nonexistent、空串返假 ⇒ 有区分力 ✓
★ 边界: 生产未动;三处 source 者未改
2026-09-25 06:36:41 +08:00
5753169077 docs(test): 去掉 permission.go:112 这个**同一提交内就失效**的行号引用(pi 2026-09-25)
pi 那条"引用位置要用**唯一标识**,行号只作辅助"我收 —— 而它在**我自己的提交**里
就有一个现成的反例:

    34a15dc^  : permission.go **112** = Error(w, …, "Invalid session_id")   ← 引用写下时是对的
    34a15dc   : 同一提交给它**前面**插了 16 行注释 ⇒ 真调用移到 **128**
                (而新注释自己占了 113 行,所以 112 现在落在注释文本里)
    HEAD      : 112 = 注释文本;真调用 = 128、另一处提到它的注释 = 113

⇒ **引用与使它失效的改动在同一个提交里** —— 这不是"时间久了漂移",
  是"写下时就错了",而且**任何 review 都看不出来**(数字看着很具体、很像核过)。
  判据也抓不到:112/301/39 全都在文件行数界内(弱形式"越界检查"无效)。

改法(按 pi 的写法):唯一标识用**函数名 + 错误字符串字面量**
(`RequestPermission` + `Error(w, http.StatusBadRequest, "Invalid session_id")`),
行号删除;并把"为什么故意不写行号"记在注释里,免得下一个人"顺手补回去"。

★ 顺带核实(**只核实、未改动**)另一处同类引用:
  `docs/HARMONY-ALIGN-PLAN.md:225` 引 `permission.go:301`,声称是 `DecidePermission`。
  实测: e07e3bf(引用写下时)301 = 该函数的文档注释、函数体在 302;
        现在函数体在 **318** ⇒ 该行号也已漂移 17 行,落在 `RequestPermission` 里。
  未改它(属于另一个 writer 的文档,且是否要改成"唯一标识"由那条线决定)。

验证: `go test ./internal/handler/` 通过。
2026-09-25 06:35:53 +08:00
b5989a94a1 feat(deploy): 按 ⑬′ 给 ⑤b 与 §7 的绿补**负向清单**(判据须写出它不覆盖哪几类)
pi 2026-09-25 提的 ⑬′:写"要验 X"时必须写清**动作覆盖到哪**,而"覆盖到哪"要写成
**负向清单**(不覆盖哪些)—— 因为正向范围可以含糊,负向清单必须逐条列,否则等于没写。
他给的量化形式:假绿区 = |R| − |C|(R=宣称范围,C=实际能判红的集合)。

这两个判据原先只在**红**的时候说话,**绿**时读者会把"绿"读成"这份二进制没问题" ——
而它们其实只答了一个问题:"内嵌 revision 是否等于 HEAD"。

    ⑤b note(绿时): …;本判据**不覆盖**: ①从脏树构建(含未提交代码,只披露不判红)
                                ②部署后被手工替换/修改(只看内嵌 revision)
    §7 ok(绿时):   已装二进制 = 当前 HEAD(xxxxxxxx)—— 不覆盖: 从脏树构建 / 部署后手工替换

★ 这条规矩**已被它自己验证过一次**:我 ⑤b 的 R\C 里那一格(`modified=true` 的脏树构建)
  **不是我想到的,是 pi 找出来的** —— 我写 ⑤b 时以为 R = C。写出负向清单这件事
  本身就会把那格逼出来。

自检新增一格 `★网关二进制:绿时 note 必须写负向清单(不覆盖哪几类)`,62 → **63 项**,
`判据自检失败` 不出现;`bash -n` 通过;`--dry-run` rc=0 且不落地。
2026-09-25 06:33:46 +08:00
eb5c4aa1b4 修 env-defaults.sh 自己的一个假绿: HOME 可用性判据从 [ -w ](权限位)改成**真写探针** —— 与我自己上封那个错同一形状
★ 缺陷(我在认自己 `[ -w ]` 假绿时顺带查出来的,就在我的 scope 里):
   196 行 `mkdir -p "$_am_home" && [ -w "$_am_home" ]`
   179 行 `... || [ ! -w "$_am_home" ]`
   两处都只看**权限位**。uid=0 时对任何 root 属主目录都返真,而**实际拦截可能在另一层**
   (本会话每次 shell 都带 `landlock-run: partial enforcement`)。本机实测:
       [ -w /root ]          ⇒ true    / touch /root/x          ⇒ Permission denied
       [ -w /opt/agentmail ] ⇒ true    / touch /opt/agentmail/x ⇒ Permission denied
   ⇒ 而且 `mkdir -p` 对**已存在**的目录不写任何东西(rc=0),所以它**接不住**这个假绿
     —— 文件 177 行原本就写了"mkdir -p 会返回成功,所以必须再单独判",但"那个单独判"选错了层。

★ 后果正撞在本文件存在的理由上(**误指对象**):
   HOME 空 + uid=0 ⇒ `_am_home=/root` 被误判可用 ⇒ HOME=/root 放行
   ⇒ ② 段取 `$HOME/.cache/go-build` 当 GOCACHE ⇒ 那里用的是**真写探针**(297 行)⇒ 失败
   ⇒ 报出「**GOCACHE** 不可写」,而根因是 **HOME** 不可写
   ⇒ 实测该路径可复现(`env -u HOME bash -c 'source deploy/lib/env-defaults.sh'`)

★ 修法: 加 `_am_can_write()` —— **真去写一个探针文件**(与 297 行同一办法),
   179/196 两处都用它。**必须两处一起改**: 196 的 else 是 `exit 2`(不是 fallback),
   只改 196 会让 /root 走到 `exit 2` 而不是改判。
   ★ 顺带把 WARN 的括注从"(非 root / passwd 里没有可用目录)"改成
     "(已实测真写探针;uid=%s)" —— 原括注是**按 uid 猜的原因**,而判据已不按 uid 判,
     uid=0 也会走到这里。**不报没验过的原因**。
   ⚠️ 前半句 `按身份推出来的 HOME=%s 不可用` **一个字都没动** —— 它是
     `plugins/pi-mail-bridge/test/env-guard.test.mjs:268` 的 assert.match 锚
     (我第一版把它改成了"写不进去",**实测把 pi 的测试弄红了 16/17**,已改回)。

★ 变异测试: 把 `_am_can_write` 改成恒真 ⇒ **被抓住**,且复现的正是上面那条误指
   (HOME 空时不再改判 ⇒ 直接报「GOCACHE 不可写」)✓
★ 反空真: 探针对 /tmp、/home/program/agentmail 返真,对 /root、/opt/agentmail、
   /nonexistent、空串返假 ⇒ 有区分力(非恒真非恒红)✓
★ 验证: pi 的 env-guard 17/17 绿、criteria-hygiene 7/7 绿、三处 source 者 bash -n 全过、
   install.sh/redeploy-*.sh 未改。**生产未动**。
2026-09-25 06:33:23 +08:00
55158e1666 fix(deploy): 补 ⑤b 的**假绿洞** revision==HEAD ≠ "就是那份提交"(pi 指出,我实测复现)
## 洞

树脏时 Go **照样**写 `vcs.revision=<HEAD>`,只在 `vcs.modified` 上留痕 ⇒
只比 revision 会把"**未提交的代码**"当成"就是那份提交"。

实测(/tmp/vm2:HEAD=2931050a,改一行**不提交**再 build):

    revision=2931050a  == HEAD   ⇒ 我 ⑤b 判**绿**
    但二进制里是未提交的那行代码          ⇒ **假绿** ✓ pi 对

线上那份正是 `vcs.modified=true` ⇒ 它**不对应任何已提交版本**。

## 修法:说清楚,但**不判红**

`revision == HEAD` 与"不含未提交代码"是**两个量**,不能拿前者冒充后者 ——
但脏树在本仓是**多 agent 并发编辑的常态**,判红就是造一条总在亮的判据。
★ 同一个事实在本仓**已有既定严重度**:`check-deploy-drift` 判据⑥

    // ⑥ 工作区干净度 —— **WARN,不参与退出码**。

⇒ 两处都改成 **WARN/note 披露,不改 ok / 不加 CHECK_FAIL**,
  与判据⑥ 同严重度 —— 否则同仓同一个量会有两套红绿。

    redeploy-gateway.sh §7:  modified=true ⇒ warn(不动 CHECK_FAIL)
    check-deploy-drift ⑤b:   revision 相同但 modified=true ⇒ 绿 + note 点明"含未提交代码"

## 判据自检

新增一格:`revision 相同但 modified=true ⇒ 绿,且 note 点明"含未提交代码"`。
自检 **61 → 62 项**,`判据自检失败` 不出现。

★ 这条是本判据**唯一**的假绿形状,所以它必须有一格专门钉住——
否则下一个人把 note 删了、或把 ok 改成 false,都没有判据会响。
2026-09-25 06:31:04 +08:00
88a56b2066 ★★★ 我认错: 上封"root 通道存在"是**假绿** —— 我用 [ -w ] 判可写,而真去写三个动作全 Permission denied;pi 报的"两端都做不到"才对
★ (A) 我的四条"能力证据"(id=0 / [ -w ]x2 / sudo -n rc=0) 全问的是**权限位层**,而真去写:
       touch /opt/agentmail/probe-w        ⇒ Permission denied
       touch /opt/agentmail/data/probe-w   ⇒ Permission denied
       touch /opt/agentmail/.deploy.lock   ⇒ Permission denied(连 rm 都拒)
       sqlite3 线上库 create table          ⇒ attempt to write a readonly database (8)
     ⇒ **[ -w ] 说 true 而 touch 说 Permission denied** ⇒ 能写才算数
★ (B) 为何假绿: [ -w ] 查权限位(uid=0 全过),拦截在**另一层**(会话里一直有 landlock-run: partial enforcement)
     ⇒ 同一个"可写吗"有两层实现,而 [ -w ] 只问第一层 ⇒ 它返回的"真"被读成对整个问题的回答
     ⇒ 同源换格: 前面查"判据/信号在不在",这次是"**我的探针在问哪一层**"
★ (C) pi 的做法更对: 它真跑部署所需的**三个写动作**(目录/库/锁),我验的是**身份与权限位**
     ⇒ 记法: 验"能不能做 X"要跑 **X 的那几个写动作**,不要验"身份看起来够不够"——代理会跨层失效
★ (D) 修正: **两端都做不到**(human 通道双断 + 对 /opt 写双拦) ⇒ "谁来部署"无人能答也无人能做
     另认: 我上封把"阻塞不同种"当结论并催 pi 收,而那个差异**不存在**,是我探针的假绿造的
★ (E) 边界: 只读探针 + 三个注定失败的写入尝试(无一改到生产);仓库/生产未动
2026-09-25 06:27:25 +08:00
2c34d6e368 fix(deploy): 占位行判据改用**行的年龄**(阈值 1 会把健康并发判成泄漏)+ 后置验证补 binrev==HEAD
两件都来自 pi 2026-09-25 的实测,我逐条复跑确认。

## ① `占位行 <= 1` 是阈值版假红(pi 127746b5,我复现)

`ClaimRelay`(INSERT 占位) 与 `BindRelayMail`(UPDATE 绑定) **不在一个事务里** ⇒
"同时有 2 个请求在飞"的正常形态**就是** 2 个占位行(t1 claim、t2 claim、t1 bind、t2 bind)。

    变异(/tmp/pl/t.db:清掉化石 + 插 2 个**合法在飞**占位)
      旧判据: PLACE=2 ⇒ rc=**1**  [FAIL] 占位行 <= 1     ← 把健康并发判成泄漏
      新判据: rc=**0**,打印"最老 = 0 分钟;其中 >10 分钟 = 0"

阈值 1 是"只有 1 个请求在飞的那天"的快照,与它要防的病(泄漏)**不正交**。

⇒ 与泄漏正交的判别量是**行的年龄**(pi 给的):正常占位行只活一个请求的时长,
  泄漏行活到**下一次请求之后**。年龄与并发度无关,也与"此刻是否恰好有请求"无关。
  阈值 10 分钟:远大于任何单请求(HTTP 有超时),远小于真实泄漏尺度(那行活了 12.7 天)。
  断言**上界**而非"==0"—— 0 是"恰好没请求在飞",不是健康的条件。

正反两面都验(只验一面会放过退化):
    健康并发 2 行 → rc=0 ✓      13 天泄漏 + 1 健康行 → rc=**1** ✓
  真库 → rc=1(那行确实 18259 分钟 ⇒ **真阳性**,非假红)
  并打印年龄 ⇒ 顺带答出"泄漏了多久"(旧判据只说"多了一行")。

## ② 抑制数丢了 keying(pi dd579a8e,我复核确认)

生成器键是 `(ag, prefix(k), r)`(**已含 agent**),但**序列化/打印时把 agent 丢了** ⇒
任何人引用这个数都会以为是"仅根"口径。实测两种 keying 全局差 6 封(61/37 vs 55/43,
6 组**全是跨 agent 合并**)⇒ 名字里不写,下一个人**必重犯**。

    文本: `抑制数 [keying=(agent,根) scope=全局]: …`
    JSON: `suppress_full_groups_keying_agent_root_scope_global`
  ★ 同时补 **scope**:本脚本报的是全局;session 侧同 keying 是另一个数(4/18)——
    同 keying 两个 scope 两个数,少写任一个都只能靠猜。
  ★ 只改名字,**数的值不动**(这是防再犯,不是纠错)。

## ③ 后置验证补 `binrev == HEAD`(pi 127746b5 §一,落在脚本而非只落检查器)

§7 那几项(服务 active / `/health` 200 / 无 panic / SSE 重连)**全都与代码版本无关** ⇒
**旧二进制跑这套清单会全绿**。2026-09-25 实证:线上是 09-19 的二进制而 §7 全过。

pi 建议落在 `redeploy-gateway.sh`,我采纳 —— 因为我昨天已把同一件事实现为
`check-deploy-drift` 判据 ⑤b,但**那个检查器没有任何自动入口**(要人手跑);
落在部署脚本里才能"装完当场就判"。

三态与 ⑤b 同口径:相等→绿 / 不等→**红**(就地回滚)/ 取不到任一侧→**不判**且必须说出来
(判红会让 `-buildvcs=false`、老构建、非 git 构建恒红)。

    实测逻辑: 线上 e8b260dd vs HEAD ⇒ 红 ✓;/nonexistent/binary ⇒ warn 不判 ✓

## 验证

`bash -n` 两文件通过;`recount-relay-counts.sh --json` 合法(23 键);
`criteria-hygiene.test.mjs` 7/7 通过(改名后仍满足 check-*.sh 命名约定)。
2026-09-25 06:27:09 +08:00