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` 空)。
This commit is contained in:
2026-09-25 07:46:41 +08:00
parent fc54a816e8
commit b16c38ad83

View File

@ -770,6 +770,24 @@ test('★ 非门禁工具(deploy/*.sh)必须有发现路径(至少一处
* `basename(p) === t` 会**把它也跳过** ⇒ 那处引用白算 ⇒ **假红**(实测: 唯一引用在
* 同名他文件里时,按名字的版本误报孤儿;按身份的版本判绿 ✓)。
* ⇒ 结论(比 pi 的建议严一格,且不丢本排除): **按身份比对被测工具自己的路径**。
*
* ★★★ 而**第三个**洞是我自己实测出来的(pi `cf5d9b18` 的 ⑯″ 正好指向它,我复现):
* 原判据只问「这个名字出现过吗」(`prose(p).includes(t)`)⇒
* **"提到"就算"发现路径"** ⇒ 于是**我自己的分析散文**(本仓 `docs/API.md`,5420 行逐轮记录)
* 里随口提一句,就能让一个真孤儿判绿。
* 实测(构造):
* 造 `deploy/zz-prose-only.sh`(无任何文档专节)+ 只在 `docs/API.md` 追加一行
* "分析随笔:zz-prose-only.sh 这次只是个例子" ⇒ 判据 **9/9 全绿**(孤儿没被报出)★ 假绿
* ⇒ 根因: 判据**分不清**「有人会照着这条信息找到它」(发现路径)与
* 「我在讨论里提过它」(提到)。而**分析日志恰恰全是后者**。
* ★ 修法: 要求引用是**可定位的** —— 必须带路径 `deploy/<t>`,而不只是裸名。
* 理由: 「发现路径」的本义是"读者能**照着走到**那个工具";裸名不告诉他在哪。
* 实测本仓 5/5 个非门禁工具的现有发现路径**本来就都是路径限定的**
* (`docs/DEV-TOOLING.md` 的专节标题与表格、`deploy/redeploy-gateway.sh` 注释、
* `deploy/prune-deploy-artifacts.sh` 注释、`docs/DEBTS.json` 的 where)
* ⇒ 所以这条收紧**不误伤任何现存工具**,而恰好挡住"散文提到"。
* ⚠️ 注意它**不是**"排除 `docs/API.md` 这个文件"—— 那是按文件名裁豁免(给逃逸指路)。
* 判据落在**引用的形态**上,任何文件里的路径限定引用都算数。
*/
const SELF_FILE = resolve(fileURLToPath(import.meta.url));
/** 反空真: 两处排除必须真的指向不同对象,合并了就会退化成"删掉一个"。 */
@ -778,13 +796,24 @@ test('★ 非门禁工具(deploy/*.sh)必须有发现路径(至少一处
'两处排除指向了同一个文件 ⇒ 本判据的排除逻辑坏了(观察者 == 被测工具?)。',
);
/*
* ★ 判据自身的判定器 + **它的自检**(照本仓"每条判据都要能被判据守"的惯例)。
* 为什么必须自检: 这条判据的**判据**就是"算不算发现路径",它一旦退回裸名匹配,
* 症状是**假绿**(孤儿不报)—— 而那正是它存在的理由,所以不能只靠注释声明。
*/
const isDiscoveryRef = (text, tool) => text.includes(`deploy/${tool}`);
assert.equal(isDiscoveryRef('见 deploy/archive-stale-sessions.sh', 'archive-stale-sessions.sh'), true,
'路径限定引用必须算作发现路径');
assert.equal(isDiscoveryRef('见 archive-stale-sessions.sh', 'archive-stale-sessions.sh'), false,
'裸名(散文提到)**不得**算作发现路径 —— 它不告诉读者那个工具在哪');
const orphans = [];
for (const t of tools) {
let refs = 0;
for (const p of SCAN) {
// 排除**工具自己的文件**(按身份,不按名字 —— 按名字会漏掉同名他文件里的引用)
if (resolve(p) === resolve(join(DEPLOY, t))) continue;
if (prose(p).includes(t)) refs++;
if (isDiscoveryRef(prose(p), t)) refs++;
}
if (refs === 0) orphans.push(t);
}