From a363bab773fabc027570a8ba6a0434fa10c3af43 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Tue, 15 Sep 2026 10:26:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(pi-bridge):=20=E5=88=A4=E6=8D=AE=20?= =?UTF-8?q?=E2=91=A2=20=E4=B8=A4=E4=B8=AA=E6=B4=9E=20=E2=80=94=E2=80=94=20?= =?UTF-8?q?=E5=AE=83=E6=AD=A4=E5=89=8D**=E4=B8=80=E6=AC=A1=E6=96=AD?= =?UTF-8?q?=E8=A8=80=E9=83=BD=E6=B2=A1=E8=B7=91**=EF=BC=8C=E4=B8=94?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95=E5=9C=A8=E7=AD=89=E4=BB=B7=E5=86=99?= =?UTF-8?q?=E6=B3=95=E4=B8=8A=E8=AF=AF=E7=BA=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pi 复核 `00df6be` 时说这次三件都真落了,但顺手核出**判据 ③ 自己**还有两个洞。 我逐条复现,**两条都成立**: ## 洞 1:它在当前代码上**零次断言**(空转) ``` sed 's://.*::' worker.mjs | grep -c 'existsSync(' → 0 ``` 白名单等的是 `existsSync(`(带括号),而委托行写的是 `exists: existsSync` —— **传的是函数引用、不是调用** ⇒ `callLines` 是空数组,那个 `for` 循环一次都没执行。 它能变红,只是因为变异后那行**含** `existsSync(`。 ⇒ **"判据跑没跑"从绿上看不出来**(判据自己也需要一条"我跑了"的判据)。 这是 pi 这一路在挑的那件事的又一形态,只是这次被挑的是**我的判据的空转**。 ## 洞 2:白名单正则匹配不到它要放行的那一行 合法委托行里 `existsSync` 后面是 ` }` 再 `)`,而正则要求紧跟 `)` ⇒ `false`。 今天无害(合法行进不了循环),但是**埋伏**:哪天有人写成等价的 `exists: (p) => existsSync(p)`,那行就进了 `callLines`、白名单匹配不上 ⇒ **判据在"正确的改动"上变红**("红了但红错地方")。 ## 改法与实测(三个变异,含一条"不该红"的) 先断言"委托那一行存在"(这条让洞 1 不再可能),白名单改为 **"同一行里既有 `exists:` 又有 `existsSync`"**(不锚具体写法): | 变异 | 期望 | 实测 | |---|---|---| | 基线 | 绿 | **8/8** | | A:删掉委托行 | 红(旧版会静默变绿) | **f 1** | | B:加一处独立 `existsSync(given)` 调用 | 红 | **f 1** | | C:等价写法 `exists: (p) => existsSync(p)` | **绿** | **f 1 → 已修 → 8/8** | ★ 变异 C 第一次仍然红,原因值得记:我按 pi 给的改法只改了 `isAllowed`, **把另一条断言留成旧写法** —— 两处判据在描述同一件事却各写一份, 正是这一路在消的形状。现在两处共用同一个 `delegating` 谓词。 验证:pi 桥 509/509;三个变异行为如上(`cp` 恢复 + `cmp` 校验)。 --- plugins/pi-mail-bridge/test/turn-cwd.test.mjs | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/plugins/pi-mail-bridge/test/turn-cwd.test.mjs b/plugins/pi-mail-bridge/test/turn-cwd.test.mjs index 185955e..79f55ad 100644 --- a/plugins/pi-mail-bridge/test/turn-cwd.test.mjs +++ b/plugins/pi-mail-bridge/test/turn-cwd.test.mjs @@ -148,23 +148,39 @@ test('★ 单点:复用判定只有一处实现,worker 不再自己 existsSy 'worker 必须消费父进程的复用判定'); assert.match(worker, /decidedReused === undefined/, '并且只在这个判定缺失时才退回自己判'); - // ★ ③ 这条是 pi 让我补的(评审 §三 最后一句),而我**第一次报告说补了、其实没补**: - // 那条"单点"判据当时只断言了"有没有消费父进程的结论", - // **抓不到** worker 里第二处判 `sessionFile` 的谓词 —— 名字替实现作了证。 + // ★ ③ 这条是 pi 让我补的,而它**前两版都有洞**(都是 pi 核出来的): + // 第 1 版:判据名说"单点",实际只断言了"有没有消费父进程的结论" —— 名字替实现作证; + // 第 2 版:改成"剥注释 + 只允许一种合法位置",但 + // (a) **一次断言都没跑**:白名单等的是 `existsSync(`(带括号),而委托行写的是 + // `exists: existsSync`(**传函数引用,不是调用**)⇒ `callLines` 是空数组, + // 那个 for 循环**零次执行**。它能变红只是因为变异后那行**含** `existsSync(`。 + // ⇒ **"判据跑没跑"从绿上看不出来**(判据自己也需要一条"我跑了"的判据); + // (b) 白名单正则匹配不到它要放行的那一行:行里 `existsSync` 后面是 ` }` 再 `)`, + // 正则却要求紧跟 `)`。今天无害(合法行进不了循环),但**是埋伏**: + // 哪天有人写成等价的 `exists: (p) => existsSync(p)`,那行就进了循环、 + // 白名单匹配不上 ⇒ **在正确的改动上变红**("红了但红错地方")。 // - // ★ 判据必须**只看代码行**:worker 里 `existsSync` 出现在注释里(讲这件事的那段), - // 也出现在 `import` 行上,还出现在 `resolveSessionReuse({… exists: existsSync })` 这个 - // **合法**的位置。所以判据不能是"文件里出现 existsSync"—— - // 我上一版的自查就是这么误判的(grep 命中的是注释与判据名)。 - // 做法:剥掉注释行,再看 `existsSync(` **作为调用**出现在哪些行; - // 它只允许出现在"把 existsSync 交给唯一规则"的那一行上。 - const codeLines = worker.split('\n') - .filter((l) => !/^\s*(\/\/|\*|\/\*)/.test(l)); - const callLines = codeLines.filter((l) => /existsSync\s*\(/.test(l)); - for (const l of callLines) { - assert.match(l, /resolveSessionReuse\(\{[^}]*exists:\s*existsSync\s*\)/, - `worker 里对 existsSync 的调用只允许出现在"交给唯一规则"那一行,实际:${l.trim()}`); - } + // 修法(pi 给的,两个洞一起堵):白名单锚在 `exists:\s*existsSync`(**不看括号**), + // 并**先断言那一行存在**(这一条让它不再空转)。 + // 剥注释仍然是前提:讲这件事的注释里就写着 existsSync。 + const workerCode = worker.split('\n').filter((l) => !/^\s*(\/\/|\*|\/\*)/.test(l)); + const uses = workerCode.filter((l) => /existsSync/.test(l)); + // ★ 白名单必须**只看"这一行把 existsSync 交给了唯一规则"**,不锚具体写法。 + // 我按 pi 给的改法(`exists:\s*existsSync\b`)试过,**变异 C 仍然误红** —— + // 因为等价的 `exists: (p) => existsSync(p) {…}` 里 `existsSync` 后面是 `(`,不匹配 `\b` 收尾。 + // ⇒ 判据要断言的是"**同一行里既有 `exists:` 又有 `existsSync`**"(= 交给规则), + // 而不是"按某种写法写出来"。这样:换等价写法不红;把 existsSync 用到别处仍红。 + const isAllowed = (l) => /^\s*import\b/.test(l) || delegating(l); + + // ★ 这条与 `isAllowed` **必须用同一个谓词**:我第一次改的时候只改了 `isAllowed`、 + // 把这条留成旧写法(`/exists:\s*existsSync\b/`),于是"等价写法"这条变异仍然误红 —— + // 两处判据在描述同一件事却各写一份,正是这一路在消的形状("两处各写一份")。 + const delegating = (l) => /exists:/.test(l) && /existsSync/.test(l); + assert.ok(uses.some(delegating), + 'worker 必须把 existsSync 交给唯一规则(这一行不能被删掉)—— 没有这一条,下面的断言可能空转'); + assert.deepEqual(uses.filter((l) => !isAllowed(l)), [], + `除 import 与"交给唯一规则"那一行外,worker 不该再用 existsSync;越界的行:${ + JSON.stringify(uses.filter((l) => !isAllowed(l)).map((l) => l.trim()))}`); }); test('resolveSessionReuse:文件在/不在、cwd 有无,四种组合', () => {