---
share_worthy: maybe
audience: UX, Dev
one_liner: 判据全对的闸也可能净效果为负——看逃逸方向内容流向哪；顺带三次「闸绿但没在查我的东西」实证
evidence: 8c1a4ec0 · 30c2ab86 · AGENTS.md §直 commit master 时：写 changeset 的封路理由块
---

# 一个判据全对的闸，仍然可以净效果为负

> 2026-09-11 第三条线。缘起 = 上一轮报给 owner、他还没拍的两件：
> ① changeset 包名要不要上闸 ② 56 份未消费 changeset 的发版决策。
> 结果：① 拍「不加闸 + 规则层一行」（`8c1a4ec0`）· ② 拍「继续攒」（`30c2ab86`）。

---

## 一 · 判据可以完全正确，而这条闸不该加

这是本轮最值得留的一条，因为它不是「判据不够好」那类问题。

现取实测：`changeset status` 的退出码是个**很好**的判据 —— 三臂故障注入全红（包名错 /
frontmatter 畸形 / bump 级别拼错），目录不存在与无 git 仓均 fail closed，detached HEAD 安全，
唯一假绿方向是空 `.changeset/` 恒绿而那本来就是正确状态。它还是**终态事实**（真实消费路径本身），
比「解析 frontmatter 比包名字符串」那种代理判据宽得多、也不会随 changesets 内部实现漂移。

**否掉它的不是判据，是逃逸方向。** 判据 = 「exit 0」⇒ 被它拦住的人最省事的规避动作是
**删掉那份 changeset**，删了就绿。而本仓**没有任何闸守「该写 changeset 却没写」**
（两个挂载面 grep `changeset` 零命中）⇒ 加这一条，失败模式从「坏记录」变成「无记录」，
而无记录**不触发任何信号**。原缺陷至少文字还在、发版那一刻必炸出来让人看见。

⇒ 可复用的判法，补在 memory `gate-criterion-escape-direction` 之后：
**「内容流向哪」要问到「会不会流向消失」**。若规避动作是删除、而删除本身无人守，
那么这条闸把一个**有信号的失败**换成了一个**无信号的失败** —— 哪怕它的判据一个字都没错。

配套结论：真闭合要**两条**闸（消费性 + 存在性），而「加一个闸」被摊开成「加两条」时，
owner 那句「越做越多」的质疑就有了可算的成本。⇒ **报加闸方案时必须先算出它要几条才闭合**，
只报其中一条等于低报成本。

## 二 · 三次「闸是绿的，但它没在查我的东西」

同一轮撞了三个不同形态，值得并排放：

| # | 现象 | 根因 |
|---|---|---|
| 1 | `audit:stale-anchors` 全绿，注入的死链探针**没被抓到** | 我那种 `docs/xxx.md` 形态落在它自印的「根相对解析不到 = **边界 B1：不报**」档。闸自印「AGENTS.md 在扫描面内」是**文件级**证据，不等于我那一段的反引号进了判定面 |
| 2 | `pnpm changeset:status` 接进 hook 后，`audit:gate-ci-parity` 照样绿 | 它的 A 侧只认 `node` 直调 `.mjs` 那种形态；`pnpm` 别名会递归展开，但**展开不到 `node` 就什么都不收**。实测三种省事接法各收 0 项，两个真闸作正对照各收 1 项 |
| 3 | 我的包名量具报「2 份无包名」 | `awk '/^"/'` 只对**双引号**灵敏，那 2 份写的是单引号 |

前两条的共同形状：**闸绿 = 「它查过的都过了」，不等于「我关心的那个对象被查了」。**
第 1 条尤其贵，因为我本来就是**为了让闸替我守**才去注入探针的 —— 探针没响，
我差点把「没响」读成「路径没问题」。⇒ 造完故障没红时，先问「它在不在判定面里」，
再问「判据灵不灵」（这正是 memory `regression-pass-needs-fault-proof` 的镜像面）。

第 2 条另有一层：**最省事的接法恰好是唯一绕过监督的接法。** 这不是巧合 ——
元闸的判定面是「调用形态」，而省事的接法之所以省事，正是因为它跳过了那个形态。
⇒ 已写进 AGENTS 封路块，真要加闸时先看那一条。

## 三 · 我自己的量具又是最不可靠的那环

本轮全部失误（3 次）**零在产品代码、全在我临时写的取证工具里**，且失败形态都是
**整齐的假象**而非报错：

- 「2 份无包名」= 假阳（单引号），差点据此宣称缺陷有残余；
- fixture 里 `changeset status` 全 exit 1，看着像「它对空目录 fail closed」——
  实际是 fixture 缺 `node_modules`、**量具自己坏了**（错误信息是 `run_main_module`）。
  补 symlink 并先跑一个「好 changeset → exit 0 且印出 minor」的正对照，才拿到真读数；
- 核「`release.mjs` 无 `--yes`」时 grep 命中 1 处，看着像我说错了 ——
  实为注释 `Deliberately interactive-only: no --yes flag`，且 `process.argv` 零命中。
  **命中 ≠ 是它**，而这恰恰是 `gate-ci-parity` 头注释逐字警告过的「注释污染 grep 判据」。

⇒ 三条都在 memory `one-off-scripts-are-the-unreliable-part` 的射程内，本轮不新增规则，
只补一个**顺序**上的自查：**全红 / 全阴时先跑一个必然为真的正对照**（第 2 例救回来就靠它）。

⚠️ 另一处：我第一版「探针名零命中」的核法是 `git grep -c … | head -3` 然后读 `$?` ——
读到的是 `head` 的退出码，恒 0。**判据必须取终态事实**，管道末尾的 exit 不是 git 的 exit。

## 四 · 没做的与为什么

- **render-gate 与 `test:visual` 没跑**：`lsof` 查到 5173 被并行 session 的 vite server 占着，
  而那两份 playwright config 是 `reuseExistingServer: true` ⇒ 会静默连上别人那棵树、**照样 exit 0**。
  那种绿比红贵。⇒ 如实记为「未测 + 解锁条件」，没走临时隔离端口配方去抢跑
  （边际价值低：`release.mjs` step 1b/1c 自己强制、无 skip flag，拍板后必然再跑一次）。
- **STATUS 顶条没加镜像**：S1 顶部摘要区现 2980 B / 上限 3000，只剩 20 B；
  ⛔ 更不该挤掉同日 design-share 线的叙述。照上一轮先例只落 CHANGELOG。
- **没立 backlog entry**：两件都已拍完，给已拍定的决定立 entry 就是那个「越做越多」的形态。
  封路理由改落「改那行的人必然会看到」的地方（范式同当日 F62 搬家）。
