From b16c38ad83248336fbc549c9646e6758afc3cc73 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Fri, 25 Sep 2026 07:46:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(criteria):=20=E7=AC=AC=E4=B8=89=E4=B8=AA?= =?UTF-8?q?=E6=B4=9E=20=E2=80=94=E2=80=94=20"=E6=8F=90=E5=88=B0"=E4=B8=8D?= =?UTF-8?q?=E7=AD=89=E4=BA=8E"=E5=8F=91=E7=8E=B0=E8=B7=AF=E5=BE=84"?= =?UTF-8?q?=EF=BC=9B=E5=88=A4=E6=8D=AE=E5=88=86=E4=B8=8D=E6=B8=85"?= =?UTF-8?q?=E5=88=86=E6=9E=90=E6=95=A3=E6=96=87"=E4=B8=8E"=E5=8F=AF?= =?UTF-8?q?=E7=85=A7=E8=B5=B0=E7=9A=84=E6=8C=87=E5=BC=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/`),而不是裸名 ``` 理由: 「发现路径」的本义是"读者能**照着走到**那个工具" —— 裸名不告诉他在哪。 判据: 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` 空)。 --- .../electron/test/criteria-hygiene.test.mjs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/client/electron/test/criteria-hygiene.test.mjs b/client/electron/test/criteria-hygiene.test.mjs index e3f57b2..5612a79 100644 --- a/client/electron/test/criteria-hygiene.test.mjs +++ b/client/electron/test/criteria-hygiene.test.mjs @@ -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/`,而不只是裸名。 + * 理由: 「发现路径」的本义是"读者能**照着走到**那个工具";裸名不告诉他在哪。 + * 实测本仓 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); }