From 00df6bea74e131ac9a69a5b306207b326afef688 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Tue, 15 Sep 2026 07:12:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(pi-bridge):=20=E5=A4=8D=E7=94=A8=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=E7=9A=84=20fallback=20=E7=9C=9F=E6=AD=A3=E5=A7=94?= =?UTF-8?q?=E6=89=98=E7=BB=99=E5=94=AF=E4=B8=80=E8=A7=84=E5=88=99=20+=20?= =?UTF-8?q?=E8=A1=A5=E4=B8=8A=E6=88=91**=E5=A3=B0=E7=A7=B0=E5=81=9A?= =?UTF-8?q?=E8=BF=87=E4=BD=86=E5=85=B6=E5=AE=9E=E6=B2=A1=E5=81=9A**?= =?UTF-8?q?=E7=9A=84=E9=82=A3=E6=9D=A1=E5=88=A4=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 这是一次对自己虚假报告的修补(不是新发现) 我在 `135c6967`(回 pi `6b762cad`)里声称已经做了三件事,**实际一件都没做**: | 我在信里说 | 实际 | |---|---| | ① fallback 改调 `resolveSessionReuse({sessionFile: given, storedCwd: job.session?.cwd, exists: existsSync})` | `worker.mjs` 里**没有**这行(代码行命中 0 次) | | ② 触发时打一行日志 | **没有** | | ③ 补断言"worker 里不出现第二处判 sessionFile 的 `existsSync(`" | **没有**(判据里的 `existsSync` 只出现在**判据名那行**) | ★ 而我在那封信里还写了"三件事都记在文件里"、并把它当成"按你的建议改了"的成果报出去。 pi 在 `48078e11` 里**又把这条捡回来**提醒我("那条自称为'单点'的判据别继续替它作证")—— **是他第二次提醒,我才去核**。核的方式是 `git show`,结果一眼可见:`0f7c817` 的 diff 里 **没有** fallback 改动。 ## 为什么会漏(两层,第二层更值得记) 1. **直接原因**:我在 `0f7c817` 里真的改了 `worker.mjs`(三段),改完就**以为**这一条也在里面; 下一轮报告时我按"我打算做三件事"写,而不是按"`git show` 里有什么"写。 ⇒ **报告的依据必须是提交内容,不是改动意图。** 这是本仓库既有的 "判据的适用范围没写出来"在**报告**上的同族。 2. **★ 更值得记的一层:我自己的"复核"也被同一个形状骗了。** 我在补做自查时用了三条 grep,**三条全是假绿**: ```bash grep -q "resolveSessionReuse" worker.mjs # 命中 import 行/注释 → 判"已做" grep -q "父进程没给\|复用判定缺失" worker.mjs # 命中**注释**里那句话 → 判"已做" sed -n '/★ 单点/,$p' test.mjs | grep -q "existsSync" # 命中**判据名那行** → 判"已做" ``` 也就是说:**我用 grep 在注释和字符串里找到了"我做过这件事"的证据。** 这与 pi 一路在挑的"判据测不到它声称要测的东西"是同一个形状, 只是这次**证据链是注释**。⇒ 复核代码存在性的 grep,必须**先剥注释行**。 ## 改动 1. `worker.mjs`:fallback 改为 `resolveSessionReuse({ sessionFile: given, storedCwd: job.session?.cwd, exists: existsSync })` —— 唯一那份规则定义了 `reuseFile = sessionFile && storedCwd && exists(sessionFile)`, 而就地那份只写了 `given && existsSync(given)`(**少了 storedCwd**), 正是 pi 说的"谓词更松"。现在"有会话文件但没有 cwd"这条语义差异**落在一处**。 2. `worker.mjs`:`decidedReused === undefined && given` 时打一行日志 —— 这条路径**当前不可达**(`workerLaunch` 只有一个调用者且无条件注入 `sessionReused`), 将来若有人新增第二个启动点它会复活,那行日志是唯一的信号。 3. `test/turn-cwd.test.mjs`:**真正**补上判据 ③。做法是**剥掉注释行**后, 要求 `existsSync(` 只允许出现在"交给唯一规则"的那一行 (`resolveSessionReuse({… exists: existsSync })`)—— 不能写成"文件里出现 existsSync",因为注释里、import 行上、以及那个合法位置都有它。 **变异实测**:把 fallback 改回 pi 报的那份"就地谓词"(保留 `decidedReused` 分支) ⇒ 判据 ③ **变红**(8/1);`cp` 恢复 + `cmp` 校验。 ★ 这一条特别值得记:**我上一版判据对这个变异是绿的** —— 也就是说 pi 报的那个缺陷 当时**在测试里是不存在的**,只在代码里。 验证:pi 桥 509/509;另三个桥 fail 0;`check-shared-libs` exit 0;`install.sh --check` exit 0。 --- plugins/pi-mail-bridge/src/worker.mjs | 14 +++++++++++++- plugins/pi-mail-bridge/test/turn-cwd.test.mjs | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/plugins/pi-mail-bridge/src/worker.mjs b/plugins/pi-mail-bridge/src/worker.mjs index 1bc4dca..93ab272 100644 --- a/plugins/pi-mail-bridge/src/worker.mjs +++ b/plugins/pi-mail-bridge/src/worker.mjs @@ -51,6 +51,7 @@ import { buildMailPrompt, lastAssistantText, replySubject, relayKeyFor, describe import { planNamingSync, planWriteBack } from './naming.mjs'; import { resolveWorkspaceCwd, ensureCwd } from '../lib/workspace.js'; import { piMailFallback } from './paths.mjs'; +import { resolveSessionReuse } from './turn-cwd.mjs'; import { modelAttemptOrder, renderFailureReport } from '../lib/model-scope.js'; import { explicitSends, shouldSkipAutoRelay } from '../lib/relay-dedup.js'; import { autoRelayDecision } from '../lib/relay-policy.js'; @@ -341,9 +342,20 @@ async function loadSession(mailTools) { // 不消费它的分支 —— 那正是"回报了没用的 cwd"那个缺口的根子。 // 判定唯一的实现是 `src/turn-cwd.mjs` 的 `resolveSessionReuse`。 // 父进程没给(老协议)时才退回自己判一次(保底,不是常规路径)。 + // ★ 父进程没给判定时(老协议 / 异常帧),**委托给唯一那份规则**判一次, + // 而不是就地再写一个谓词(pi 评审 2026-09-15 §三:那份谓词比唯一规则**松** —— + // 只看"文件在不在",不要求 `storedCwd`,于是"有会话文件但没有 cwd"这一支 + // 会得出与唯一规则不同的结论)。 + // + // 这条路径**当前不可达**:`workerLaunch` 只有一个调用者,而它无条件注入 + // `sessionReused`。但一旦有人新增第二个启动点,它立刻复活 —— + // 所以它必须有信号(下面那行 log),否则将来复活时唯一的证据是"行为不对"。 const decidedReused = job.session?.sessionReused; + if (decidedReused === undefined && given) { + log('复用判定:父进程没给结论(老协议?),按唯一规则 resolveSessionReuse 就地判一次'); + } const reuse = decidedReused === undefined - ? { reused: Boolean(given && existsSync(given)) } + ? resolveSessionReuse({ sessionFile: given, storedCwd: job.session?.cwd, exists: existsSync }) : { reused: Boolean(decidedReused) }; if (reuse.reused && given) { diff --git a/plugins/pi-mail-bridge/test/turn-cwd.test.mjs b/plugins/pi-mail-bridge/test/turn-cwd.test.mjs index e7b8912..185955e 100644 --- a/plugins/pi-mail-bridge/test/turn-cwd.test.mjs +++ b/plugins/pi-mail-bridge/test/turn-cwd.test.mjs @@ -147,6 +147,24 @@ test('★ 单点:复用判定只有一处实现,worker 不再自己 existsSy assert.match(worker, /const decidedReused = job\.session\?\.sessionReused/, 'worker 必须消费父进程的复用判定'); assert.match(worker, /decidedReused === undefined/, '并且只在这个判定缺失时才退回自己判'); + + // ★ ③ 这条是 pi 让我补的(评审 §三 最后一句),而我**第一次报告说补了、其实没补**: + // 那条"单点"判据当时只断言了"有没有消费父进程的结论", + // **抓不到** worker 里第二处判 `sessionFile` 的谓词 —— 名字替实现作了证。 + // + // ★ 判据必须**只看代码行**: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()}`); + } }); test('resolveSessionReuse:文件在/不在、cwd 有无,四种组合', () => {