docs(dev-tooling): 写点三处**不同的入口假设**、过宽过窄、因果无关判据、锚点、lib/ 边界 + 一条被自己纠正的观察
按 pi 的两条建议补: 1. "漏过三次"改成**列出三次的位置与各自不同的入口假设**(他指出的问题:原写法 会让读者以为"同一处错了三次"):① `writeSession` 只包 `writeFileSync`; ② `mk()` 的 `mkdtempSync` 在 try 之外;③ 兜底层放在 `main()` 而 `selfCheck()` 是导出的。 三次都概括成一句:**"我以为的入口/哪一行"决定了覆盖范围**。 2. "判据只能发现已知成因"作为实例加进同节,并配对写出**因果无关**的运行期判据 (`test/lib/run-suite.mjs` 数结果行 / 跨文件重名实测不被拦)。 新补四条(本轮新踩出来的): - **判据过宽和过窄都是坏的**(那条 import 判据自己同时踩过两边); - **判据的锚点必须与结论一一对应**(重名检查锚结果行 vs 锚"名字出现过", 噪声与效应恰好同阶 ⇒ 4 看起来还能解释); - **`lib/` 与 `test/lib/` 的边界**(部署脚本是 `cp -a` + `rm -rf test`, 没有 `EXCLUDE_DIRS` 这种变量;★ 别写成"被 src/ 直接 import"—— 直接引用数不是可达性); - **附一节"被自己的结论半路纠正的观察"**:那条 `result IS NULL` 的挂起请求看着像 授权链缺口,实际是网关按设计处理的(`PermissionWaitWindow = 10min` + 推导出的 "失效时刻")。教训写成"先查完'是否已由另一层按设计处理掉'再下结论"—— 这类误判会以"我发现了新问题"的语气传播,比沉默更贵。
This commit is contained in:
@ -171,20 +171,57 @@ bash deploy/prune-deploy-artifacts.sh --self-check # 判据自检(16 项,
|
||||
实测:从测试文件取夹具,让巨行用例(单条往临时目录写 ~12 MiB)跑了**两次**
|
||||
(测试总数 475;修完 459)。夹具放**非测试模块**(`lib/session-fixtures.mjs`),
|
||||
判据也进套件(`env-guard.test.mjs` 里那条扫描)。
|
||||
- **写点要"一处覆盖全部",且覆盖范围不取决于入口。** 同一个"ENOSPC 被翻译成环境问题"
|
||||
在这套代码里被漏掉过三次:只包了 5 个写点里的 1 个;`mkdtempSync` 在 try 之外;
|
||||
兜底层放在 `main()` 里而**被兜的函数是导出的**。判据过宽过窄都是坏的 ——
|
||||
上面"测试文件互相引用"那条判据自己就同时踩了过窄(漏动态导入)和过宽
|
||||
(把注释里的散文引用也算违规),最后只能收成"解析真引用"。
|
||||
- **写点要"一处覆盖全部",且覆盖范围不取决于入口。** 同一条"ENOSPC 被翻译成环境问题"
|
||||
在这套代码里被漏过**三次**,而且是**三个不同的入口假设**(不是同一处错了三次):
|
||||
① `test/lib/session-fixtures.mjs` 的 `writeSession`(第一版只包 `writeFileSync`,
|
||||
`mkdirSync` 在 try 之外);
|
||||
② `deploy/check-deploy-drift.mjs` 的 `mk()`(`mkdtempSync` 也**是**一个写点,却在 try 之外);
|
||||
③ 同一个文件的 `main()` —— 兜底层放在调用方,而**被兜的 `selfCheck()` 是导出的**
|
||||
⇒ 任何绕过 `main()` 的调用者拿不到翻译。
|
||||
三次都可以概括成一句话:**"我以为的入口/哪一行"决定了覆盖范围**。
|
||||
- **判据过宽和过窄都是坏的。** "测试文件不许互相 import"那条判据自己同时踩过两边:
|
||||
过窄(只匹配静态 from,漏掉动态 `import()`)、过宽("文件里出现别的测试文件名"
|
||||
把**注释里的散文引用**也算违规,还被自己注释里的示例字面量点亮)。
|
||||
最终形状只能是"解析真引用"。
|
||||
- **因果特定的判据 + 因果无关的判据要配对。** "测试文件互相 import"只能发现**已知成因**;
|
||||
同族的另一种成因它看不见 —— 实测**跨文件同名用例不会被 runner 拦**
|
||||
(两个文件各写一个同名用例 ⇒ `# tests 2 / # pass 2 / # fail 0`,零警告)。
|
||||
所以补一条不挑成因的运行期判据:`test/lib/run-suite.mjs` 从**同一次运行的 TAP**
|
||||
里数结果行,重名即红。
|
||||
- **判据的锚点必须与结论一一对应。** 同一条重名检查,锚 `^(ok|not ok) <n> - <名字>`
|
||||
(结果行)是对的;锚"名字出现过"是错的 —— TAP 里名字既出现在 `# Subtest:` 头、
|
||||
又出现在结果行,**效应 2 倍、噪声也 2 倍且恰好同值**,于是"4"看起来还能解释;
|
||||
若行种类是 3,就会把"两次"读成"三次"。**别让噪声与效应同阶。**
|
||||
- **`lib/` 与 `test/lib/` 的边界**(2026-09-14):部署脚本是 `cp -a "$SRC/." "$STAGING/"`
|
||||
加一条 `rm -rf "$STAGING/test"`(**没有** `EXCLUDE_DIRS` 这种变量)⇒ **`lib/` 整份进快照**。
|
||||
于是规则必须是可判定的:`lib/` = 从**生产入口**可达的模块;只被测试引用的放 `test/lib/`。
|
||||
判据在 `test/lib/reach.mjs`(真走 import 闭包,含按路径 fork 的子进程入口)
|
||||
与 `test/layout-boundaries.test.mjs`。
|
||||
★ 别写成"被 `src/` **直接** import":实测 22 个 `lib/` 模块里 4 个 `src` 直接引用数为 0
|
||||
(`addressing.js` 被 `lib/inbox-format.js` 引、`user-question.js` 走前缀动态 import、
|
||||
另两个谁都不用)—— **直接引用数不是可达性**。
|
||||
|
||||
### 索引(实证在各自文件头注释里,此处不复述)
|
||||
|
||||
- `plugins/pi-mail-bridge/lib/tmp-space.mjs` —— 测量层:`bavail × bsize`、
|
||||
- `plugins/pi-mail-bridge/test/lib/tmp-space.mjs` —— 测量层:`bavail × bsize`、
|
||||
**`0` 是"真的没有"而不是"不知道"**(只有 `null` 才是不知道)。
|
||||
- `plugins/pi-mail-bridge/lib/env-error.mjs` —— ENOSPC ⇒ 人话;为什么它是纯函数、
|
||||
- `plugins/pi-mail-bridge/test/lib/env-error.mjs` —— ENOSPC ⇒ 人话;为什么它是纯函数、
|
||||
为什么**不与** `deploy/` 的实现合并(`deploy/` 的独立性比去重值钱)。
|
||||
- `plugins/pi-mail-bridge/test/lib/reach.mjs` —— `lib/` 与 `test/lib/` 的边界判据。
|
||||
- `deploy/check-deploy-drift.mjs` —— 现场比树(判据自己算,不靠手抄常量)+
|
||||
判据自检的两侧验证 + 为什么它自带兜底层。
|
||||
- `deploy/check-deploy-drift.mjs` 的运行输出本身**就是**部署状态的判据来源;
|
||||
**不要**把它抄成一份哈希清单往外发 —— 抄出来的那一刻就开始过期
|
||||
(2026-09-14 发给 `jianf` 的清单在两次提交后就作废了)。
|
||||
|
||||
### 附:一条被自己的结论"半路纠正"的观察(留作提醒)
|
||||
|
||||
2026-09-14 排查时看到 `permission_requests` 里有一条 `result IS NULL` 的挂起请求
|
||||
(11:56 创建,已挂 3h54m),当时的推断是"worker 被重启/超时杀掉 ⇒ 停在授权上没人解除
|
||||
⇒ 这是那个 bug 的同族缺口"。
|
||||
**实际是正常状态**:网关的等待窗口是 `PermissionWaitWindow = 10 * time.Minute`
|
||||
(`server/internal/models/permission_mode.go`),请求 12:06 就失效了,
|
||||
界面靠 `AttachPermissionDeadline` 推导出的**时刻**(不是布尔快照)自己判断过期。
|
||||
**教训**:看到与已知 bug 形状相同的现象时,先把"它是否已经由另一层按设计处理掉了"
|
||||
查完再下结论 —— 否则会把一个正常状态写成缺陷,而这类误判会以"我发现了新问题"的
|
||||
语气传播出去,比沉默更贵。
|
||||
|
||||
Reference in New Issue
Block a user