Skip to content

test(desktop): fix stale windows-child-process needles (not a wiring regression) - #114

Merged
karlligamesvc-spec merged 1 commit into
mainfrom
fix/windows-childproc-hide
Jul 13, 2026
Merged

karlligamesvc-spec merged 1 commit into
mainfrom
fix/windows-childproc-hide

Conversation

@karlligamesvc-spec

Copy link
Copy Markdown
Owner

定谳:测试 stale,不是真回归

windows-child-process.test.cjs 的「hidden Windows consoles」契约测试在 origin/main(含 v0.16.11)上红,与近期桌面 PR 无交集。已复现 + 用两个历史点实测定位根因:测试断言写死了单行 execFileSync(...)/spawn(...) 调用格式,后来 5 处调用被(与本测试无关的)重排版改成多行,断言随之失配 —— 但实际的 hiddenWindowsChildOptions(...) 保护在这 5 处全部原样保留,Windows 用户不受影响。

失败断言原文

error: 'missing call site: execFileSync(pyExe'
code: 'ERR_ASSERTION'

即 requireHiddenChildOptions(source, 'execFileSync(pyExe') 在 main.cjs 里 source.indexOf('execFileSync(pyExe') 返回 -1。

引入 commit

d62979a6f34f64f2ed840f159aac66e24d7cad78("feat(desktop): composer status stack, live subagent windows, editable prompts NousResearch#44630",2026-06-12)—— 与本测试完全无关的一个功能 PR,顺带把 5 处 execFileSync/spawn 调用从单行重排成多行:

-    const child = spawn(resolveGitBinary(), IS_WINDOWS ? [...] : args, hiddenWindowsChildOptions({
+    const child = spawn(
+      resolveGitBinary(),
+      IS_WINDOWS ? [...] : args,
+      hiddenWindowsChildOptions({

(pyExe / resolveGitBinary / 两处 backend.command,backend.args / uninstall spawn,共 5 处;hiddenWindowsChildOptions(...) 包装在每一处都原样保留,只是换行位置变了)

定谳证据链

  1. 复现:worktree 跑 node --test electron/windows-child-process.test.cjs → 2 pass / 1 fail,断言原文如上。
  2. 历史点实测(非猜测):
    • d62979a6f^(重排版前一个 commit)detached checkout → node --test 3/3 全绿。
    • d62979a6f(重排版 commit 本身)detached checkout → 复现同一失败(missing call site: execFileSync(pyExe)。
      坐实这就是引入点。
  3. node --test 的 fail-fast 掩盖了真实范围:requireHiddenChildOptions 里的 assert.notEqual 一失败就抛出,同一个 test() 内后续的 needle 检查全部不会执行。直接用 String.prototype.indexOf 逐条重放全部 9 个 needle 发现:实际有 5 个 needle 失配(execFileSync(pyExe、spawn(resolveGitBinary()、spawn(backend.command, backend.args、hermesProcess = spawn(backend.command, backend.args、spawn(py, [...'uninstall'...]),node --test 的报错只暴露了遍历顺序里第一个。
  4. 保护面逐条核对:读 main.cjs 现状,5 处失配调用全部仍然把第三参数包在 hiddenWindowsChildOptions({...}) 里(已用 Read/grep 逐处核对源码行号:~1946/~2092/~7258/~7481/~9655)。hiddenWindowsChildOptions 定义本身(line 246-251)未变:!IS_WINDOWS || 已显式设置 windowsHide 时透传,否则补 windowsHide:true。即运行时行为没有任何变化。
  5. 负向验证(证明修复后的断言不是"永远绿"的空测试):临时把 pyExe 调用点的 hiddenWindowsChildOptions(...) 包装剥掉,重跑测试 → 立刻红,报错变成 expected execFileSync(pyExe to wrap child-process options with hiddenWindowsChildOptions;还原后确认改动干净(git status 只剩测试文件的合法 diff)。

修复

requireHiddenChildOptions 改成对「source 和 needle 都先去除全部空白字符」后再 indexOf 匹配 —— 断言追的是调用点的结构(标识符相邻顺序),不再依赖行内换行这种排版细节。这样下次 prettier/重排版再把某个多参数调用改成多行也不会重新致盲。顺手把唯一硬编码了换行的 'reg' needle("execFileSync(\n 'reg'")简化回普通字符串,统一用新机制,不再需要手写转义。

Windows 用户可见影响评估

无影响。9 个契约调用点(pyExe / reg / git / taskkill / command,args / curl / backend.command,args ×2 / uninstall)在当前 main.cjs 上全部正确包了 hiddenWindowsChildOptions(...);bootstrap-runner.cjs 的 PowerShell runner 调用点同样完好。这是纯测试断言脆弱性问题,不是子进程黑框闪烁的功能回归,v0.16.11 已发的桌面版本没有引入新的 Windows 控制台可见性问题。

验证

  • node --test electron/windows-child-process.test.cjs:3/3 全绿(此前 2 pass/1 fail)。
  • npm run test:desktop:platforms 完整清单(37 个测试文件,含本文件):606 tests / 605 pass / 1 skip(与本改动无关的既有 skip)/ 0 fail。
  • tsc --noEmit:本改动仅涉及 apps/desktop/electron/*.test.cjs;apps/desktop/tsconfig.json 的 include 只有 ["src", "../shared/src"] 且 allowJs:false,electron/ 目录结构性地不在 TS 项目范围内,该文件改动不可能影响 typecheck 结果(未额外跑 npm install 去执行一个必然无关的检查)。

范围说明

排查过程中额外发现 3 处未被本测试覆盖、且当前确实没有 hiddenWindowsChildOptions 包装的 Windows 可达 spawn 调用(extractBundleArchive 的 tar.exe 解压、runBundledTool、本地 coding-agent daemon 的 runLocalAgentJob)—— 这与本次红测试的根因无关(这 3 处从一开始就不在测试覆盖范围内,不是 d62979a 引入的),是否需要修复是一个独立问题,不在本 PR 范围内,已另行反馈给 PM 评估。

🤖 Generated with Claude Code

The "desktop background child processes opt into hidden Windows
consoles" contract test has been silently broken since 2026-06-12
(d62979a, an unrelated feature commit that reformatted 5 spawn/
execFileSync call sites from single-line to multi-line). node --test
only ever reported 1 failure because assert throws on the first
failing needle and aborts the rest of that test() body — in reality
5 of 9 needles no longer matched.

Root cause is test brittleness, not a wiring regression: every
affected call site (pyExe python-version probe, git spawn, the two
backend.command/args spawns, the uninstall spawn) still correctly
wraps its options with hiddenWindowsChildOptions(...), so
windowsHide:true is still applied on Windows. No user-visible
console-flash regression.

Fix: match needles against whitespace-collapsed source/needle text
instead of exact substrings, so the assertion tracks call-site shape
(identifier adjacency) rather than incidental line-wrapping. Verified
the new matcher still catches a real regression (temporarily strayed
the pyExe call's hiddenWindowsChildOptions wrapping locally -> test
failed as expected; reverted).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@karlligamesvc-spec
karlligamesvc-spec merged commit b934ced into main Jul 13, 2026
22 checks passed
@karlligamesvc-spec
karlligamesvc-spec deleted the fix/windows-childproc-hide branch July 13, 2026 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant