fix(desktop): hide the Windows runtime terminal and align reduced-motion startup - #9064
Conversation
…ion startup On Windows the bundled runtime (node.exe, a console application) was spawned without CREATE_NO_WINDOW, so the desktop (a GUI application) allocated a visible terminal window for it; closing that window stopped the runtime with exit code 1 (#9043). Route the spawn through a platform-specific helper that sets CREATE_NO_WINDOW via the command-group builder (group_spawn would overwrite creation flags set directly on the command). Add a Windows-only regression test that asserts the spawned child receives no console window. The reduced-motion bootstrap fallback showed the logo left-aligned inside the shell while the newly visible status text was centered in a wider card, so the two did not share a horizontal center. Stack and center both inside the shell and drop the leftover card chrome, mirroring the simplified startup view from #8988, and extend the bootstrap release check to guard the centering rules.
|
Re-run — this pass reviews the current head
Moving on to code review. 🔍 中文说明Re-run —— 本轮审查当前 head
进入代码审查。🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRe-reviewed at the current head. The previous pass covered My independent proposal for #9043, written before reading the diff: carry Verified beyond the diff:
No blockers. One optional nit: the new comments at the two Files changed (9 of 9)
Testing evidence (this PR's own CI — unattended run, no PR code executed here)All green on the exact head
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Sandboxed verification: a 中文说明代码审查(在当前 head 上复核):上一轮覆盖 在读 diff 之前我的独立方案:通过 command-group builder 为运行时子进程携带 额外核实:
未发现阻塞问题。一条可选提示: 测试证据(非值守运行,未在此执行 PR 代码):精确 head 沙箱验证: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — the #9043 fix is correct, minimal, and pinned by a test that runs on a real Windows runner; the follow-up hardening sweep is conventional and inert off-Windows. Named nits keep this short of a 5: two new Stepping back at the new head: the desktop-shell half is exactly what I'd have done in its place — carry Every change in the diff is accounted for, CI is green on the exact head including the only check that could settle the spawn claim (a Windows runner), and both review threads landed resolved. The single open item is cosmetic and cannot be settled from here — the reduced-motion rendering on a packaged Windows build — and the author flags the same gap honestly. Approving, pinned to the reviewed commit. ✅ 中文说明信心:4/5 —— #9043 的修复正确、最小化,且由真实 Windows runner 上运行的测试钉住;后续加固扫描符合既有约定、在非 Windows 上为空操作。未达 5 分的原因(均已点名): 在新 head 上回头看:desktop-shell 部分与我的独立方案完全一致——通过 group builder 携带 diff 中每一处改动都有明确归属,CI 在精确 head 上全绿,包括唯一能确认启动参数的检查(Windows runner),两条评审线均已收敛。唯一悬而未决的是外观项、且无法在此确认——打包 Windows 构建上的减弱动效渲染——作者也已如实注明同样的缺口。 批准,固定在本轮审查的提交上。✅ — Qwen Code · qwen3.8-max Reviewed at |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
|
The exact-head Ubuntu test job failed at the aggregate test command without an actionable PR-local failure in the log; desktop Ubuntu and Windows lanes passed. I reran the failed job rather than changing product code. Review and rerun verification are pending. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
Not explored to full depth (tool budget reached): "PR #9064 fixes Desktop startup problems from issue #9043:…": none — all planned checks completed within budget.; "PR #9064 fixes Desktop startup problems from issue #9043:…": could not compile or execute the Rust code — no cargo/rustc installed on this runner and no Windows target available; the #[cfg(windows)] path was verified by…; "PR #9064 fixes Desktop startup problems from issue #9043:…": did not fetch the failing Test (ubuntu-latest, Node 22.x) run logs to identify the failing step; assessed as unrelated to this diff without root cause.; "PR #9064 fixes Desktop startup problems from issue #9043:…": did not exhaustively enumerate every child_process spawn site in all packages for missing windowsHide — only the principal runtime paths (shell exec, MCP SD….
Test Plan (not a blocker): scripts/test-release.js — no such file or directory.
中文说明
已审查。 建议见行内评论。
未探索到全部深度(达到工具调用预算):"PR #9064 fixes Desktop startup problems from issue #9043:…":none — all planned checks completed within budget.;"PR #9064 fixes Desktop startup problems from issue #9043:…":could not compile or execute the Rust code — no cargo/rustc installed on this runner and no Windows target available; the #[cfg(windows)] path was verified by…;"PR #9064 fixes Desktop startup problems from issue #9043:…":did not fetch the failing Test (ubuntu-latest, Node 22.x) run logs to identify the failing step; assessed as unrelated to this diff without root cause.;"PR #9064 fixes Desktop startup problems from issue #9043:…":did not exhaustively enumerate every child_process spawn site in all packages for missing windowsHide — only the principal runtime paths (shell exec, MCP SD…。
Test Plan(非阻断):scripts/test-release.js — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.21.11)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "You are review agent reverse-audit — Reverse audit agent…": none — finished within the tool budget (~14 calls).; "PR #9064 fixes the Desktop startup problems reported in…": none — all checks completed within budget.; "PR #9064 fixes the Desktop startup problems reported in…": none — all checks above completed within budget.; "PR #9064 fixes the Desktop startup problems reported in…": none — all checks above completed within budget..
Test Plan (not a blocker): scripts/test-release.js — no such file or directory.
中文说明
未探索到全部深度(达到工具调用预算):"You are review agent reverse-audit — Reverse audit agent…":none — finished within the tool budget (~14 calls).;"PR #9064 fixes the Desktop startup problems reported in…":none — all checks completed within budget.;"PR #9064 fixes the Desktop startup problems reported in…":none — all checks above completed within budget.;"PR #9064 fixes the Desktop startup problems reported in…":none — all checks above completed within budget.。
Test Plan(非阻断):scripts/test-release.js — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.21.11)
doudouOUC
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): PR #9064 fixes Desktop startup problems from issue #9043:...: None. All checks completed within budget..
[Critical] R1-c1: Making the runtime daemon console-less (via CREATE_NO_WINDOW) converts previously-invisible daemon-side spawns into visible ones. Pre-PR, console-app children of the daemon inherited its console (one shared window — the #9043 bug); post-PR the daemon has no console and creation flags are not inherited, so every console-app child spawned without windowsHide/CREATE_NO_WINDOW allocates its own visible console window on Windows. Verified unguarded daemon-reachable sites in packages/core/src/tools/shell.ts: getGitHeadSync execFileSync('git', ['rev-parse', 'HEAD']) (:3874), the attribution :4108), git show (runGitCount git rev-list --count (:3812), :3840), the getGitHead (git notes add write (:4193), and both :3102, ~:3252 — no options object at all). These are reachable from taskkill spawns (qwen serve sessions: the attribution flow fires on every attributable git commit, and taskkill fires on Windows background-shell kill. Failure scenario: a Windows desktop user whose agent runs an attributable git commit sees console windows flash where none appeared before (~5 allocations per commit), and background-shell kills flash taskkill windows — contradicting this PR's stated #9043 goal, and introduced by this PR's own flag change. Fix: add windowsHide: true (or windowsHide: isWindows) to those shell.ts spawn sites, matching the existing pattern in `packages/core/src/services/shellExecutionService.ts:798.
中文说明
未探索到全部深度(达到工具调用预算):PR #9064 fixes Desktop startup problems from issue #9043:...:None. All checks completed within budget.。
[Critical] R1-c1: Making the runtime daemon console-less (via CREATE_NO_WINDOW) converts previously-invisible daemon-side spawns into visible ones. Pre-PR, console-app children of the daemon inherited its console (one shared window — the #9043 bug); post-PR the daemon has no console and creation flags are not inherited, so every console-app child spawned without windowsHide/CREATE_NO_WINDOW allocates its own visible console window on Windows. Verified unguarded daemon-reachable sites in packages/core/src/tools/shell.ts: getGitHeadSync execFileSync('git', ['rev-parse', 'HEAD']) (:3874), the attribution :4108), git show (runGitCount git rev-list --count (:3812), :3840), the getGitHead (git notes add write (:4193), and both :3102, ~:3252 — no options object at all). These are reachable from taskkill spawns (qwen serve sessions: the attribution flow fires on every attributable git commit, and taskkill fires on Windows background-shell kill. Failure scenario: a Windows desktop user whose agent runs an attributable git commit sees console windows flash where none appeared before (~5 allocations per commit), and background-shell kills flash taskkill windows — contradicting this PR's stated #9043 goal, and introduced by this PR's own flag change. Fix: add windowsHide: true (or windowsHide: isWindows) to those shell.ts spawn sites, matching the existing pattern in `packages/core/src/services/shellExecutionService.ts:798.
— deepseek-v4-flash via Qwen Code /review (v0.21.10)
yiliang114
left a comment
There was a problem hiding this comment.
Verified the unresolved Critical against this head — it still stands.
Checked packages/core/src/tools/shell.ts at this head: zero windowsHide occurrences, and the unguarded console-app spawns are all present — taskkill at ~:3102 and ~:3252 (no options object at all), execFileSync('git', ['rev-parse', 'HEAD']) at ~:3874, and the attribution git show at ~:4108. Once the daemon runs console-less, creation flags don't inherit, so each of these allocates its own visible console window on Windows — the attribution flow fires on every attributable commit. The MCP stdio leg is indeed covered by the SDK's own windowsHide.
The desktop-shell half itself is solid: command.group().creation_flags(CREATE_NO_WINDOW).spawn() is the documented pattern for command_group, non-Windows keeps group_spawn() untouched, and the PowerShell GetConsoleWindow() probe asserts the actual contract. Agree with the thread that the post-PR state is still a net improvement over the release baseline, so a fast-follow adding windowsHide at those shell.ts sites (matching shellExecutionService.ts) is a reasonable landing path — but the residual windows are introduced by this flag change, so they should be tracked before merge.
With the bundled runtime spawned console-less, console-app children of the daemon no longer inherit a console and each allocates a visible window. Pass windowsHide at the git attribution, git notes, and taskkill spawn sites in the shell tool, matching shellExecutionService. Also loosen the reduced-motion bootstrap assertion so it no longer hard-codes rule order inside the media block.
|
Follow-up fix pushed in 9ef0043 closes the remaining no-console gaps on the desktop path:
Focused verification: ACP spawn tests 41/41, shell execution service tests 135/135, desktop release helper checks, Cargo check, Prettier, and diff check all pass. No local |
…call site The Rust test exercises spawn_runtime_group directly; nothing guarded that DesktopRuntime::start still routes through it. Assert the call site in the release gate so a refactor reverting to group_spawn() fails the suite.
|
@qwen-code /review --timeout=360 |
|
Qwen Code review request accepted. Review is queued in workflow run. |
doudouOUC
left a comment
There was a problem hiding this comment.
Reviewed at 58a6acd0 — approving. The Rust half is correct for a non-obvious reason, and the two things I was going to raise (local_control.rs, and pinning the production call site) are already fixed in 9ef0043b/58a6acd0. I do still have one correction to the premise the newer commits were built on.
Verified
- The builder detour is necessary and sufficient.
command-group'sCommandGroupBuilder::spawn(src/stdlib/windows.rs) doesself.command.creation_flags(self.creation_flags | CREATE_SUSPENDED)— it ORs, soCREATE_NO_WINDOWsurvives alongside the suspend-and-resume + job-object grouping. And becausegroup_spawn()goes through the same path withcreation_flags = 0, flags set directly on theCommandreally are overwritten, exactly as the comment says. The crate's ownexamples/with_flags.rsis this precise pattern, described as "setting creation flags on windows to hide the console window". - CI actually ran the new test.
Desktop Shell (windows-2022)→ "Compile and test the desktop crate" was green, soruntime_child_gets_no_windows_consolepassed on a real Windows runner. That is the only way this could be checked. local_control.rsis the right addition.start_sleep_inhibitor()spawns from the Tauri GUI process, which genuinely has no console, so thepowershell.exewake-lock loop really did get a fresh console with a visible window for as long as Local Control was on — same user-visible symptom as #9043, and closing it killed the inhibitor. The#[cfg(target_os = "windows")]block with the localCommandExtimport keeps the non-Windows build untouched.- The
shellExecutionService.tssweep is mechanical and safe. All fivetaskkillsites now take the sharedWINDOWS_TASKKILL_OPTIONS; nothing else about the cleanup contracts moves, and the existing #5873 assertions were updated to the new arity in both directions (positive and negative), so the "reap must tree-kill on cancel" / "must not downgrade to a shell-only kill" guards still bite. - The CSS change is where the release gate says it is.
body[data-state='starting'] .brand { justify-content: center; }sits inside@media (prefers-reduced-motion: reduce), and the.statusrule it is asserted alongside already carriestext-align: centerthere. - Clean tree at this head:
shellExecutionService.test.ts135 passed,tools/shell.test.ts294 passed,acp-bridge/spawnChannel.test.ts41 passed,node scripts/test-release.jspasses (including the newspawn_runtime_group(&mut command)gate on the production call site).
Correction — the premise behind the TS sweep looks wrong
R1-c1 argued that once the daemon is console-less, "creation flags are not inherited, so every console-app child spawned without windowsHide allocates its own visible console window". CREATE_NO_WINDOW does not make a process console-less: per the Win32 process-creation-flags docs it is a third console-allocation mode alongside CREATE_NEW_CONSOLE/DETACHED_PROCESS — the child gets a new console whose window is never created. Inheriting the parent's console is the default when no console flag is passed, so taskkill/git/the ACP channel spawned from the daemon inherit that windowless console and show nothing. (Consistent with the new Rust test: GetConsoleWindow() returns NULL precisely because no window was ever created.) The genuinely console-less parent is the Tauri GUI process — which is exactly why local_control.rs was the real bug and the daemon-side spawns were not.
I am not asking you to revert any of it. Every added windowsHide: true matches a convention already present at ~15 sites in packages/core, is inert off-Windows, and does close the gap for a qwen serve started some other way. But it is worth recording that the TS half of this PR — now spanning packages/core/src/tools/shell.ts, packages/core/src/services/shellExecutionService.ts and packages/acp-bridge/src/spawnChannel.ts — is hardening rather than part of the #9043 fix, so it grew on a claim that does not hold. If any of it starts costing review rounds, it can be split out without weakening the #9043 fix at all.
Minor
The reduced-motion gate extracts the block with /@media \(prefers-reduced-motion: reduce\) \{([\s\S]*?)(?:@media|<\/style>)/. The next @media in bootstrap/index.html is the max-width: 640px block ~150 lines later, so the captured span runs well past the reduced-motion block's closing brace: the two centering rules only have to appear somewhere in that span, and moving them out of the media block keeps the gate green. It still catches outright deletion, so this is a weak-oracle note, not a defect.
中文说明
在 58a6acd0 上完成审查,同意合并。Rust 部分的正确性依赖一个不显然的细节,我已对照 crate 源码确认;我原本要提的两点(local_control.rs、以及固定生产调用点)已在 9ef0043b/58a6acd0 中修好。但对新提交所依据的前提仍有一处更正。
已验证
- 走 builder 传标志是必要且充分的:
command-group的CommandGroupBuilder::spawn执行creation_flags(self.creation_flags | CREATE_SUSPENDED)——是按位或,所以CREATE_NO_WINDOW与挂起/恢复、job object 分组共存;而group_spawn()走同一路径且 builder 标志为 0,因此直接设在Command上的标志确实会被覆盖,注释描述准确。crate 自带的examples/with_flags.rs就是这个写法,用途正是"在 Windows 上隐藏控制台窗口"。 - CI 确实跑了新测试:
Desktop Shell (windows-2022)的 "Compile and test the desktop crate" 为绿,说明runtime_child_gets_no_windows_console在真实 Windows runner 上通过——这也是唯一可行的验证方式。 local_control.rs这处补得对:start_sleep_inhibitor()是从完全没有控制台的 Tauri GUI 进程启动的,所以那个powershell.exe保持唤醒的循环确实会获得一个带可见窗口的新控制台,并且只要 Local Control 开着就一直存在,关掉窗口即杀死 inhibitor——症状与 #9043 完全一致。#[cfg(target_os = "windows")]块内局部use CommandExt的写法不影响非 Windows 构建。shellExecutionService.ts的扫描式改动机械且安全:五处taskkill统一走WINDOWS_TASKKILL_OPTIONS,清理契约本身没有变化,且既有 #5873 断言在正向与反向两侧都同步更新了参数形态,因此"取消时必须树杀"/"不得降级为仅杀 shell"两条守卫依然有效。- CSS 改动位置与发布门禁一致:新规则确实在
@media (prefers-reduced-motion: reduce)内,与之一同断言的.status规则本来就带text-align: center。 - 在该 head 的干净树上:
shellExecutionService.test.ts135 passed,tools/shell.test.ts294 passed,acp-bridge/spawnChannel.test.ts41 passed,node scripts/test-release.js通过(含新增的生产调用点spawn_runtime_group(&mut command)断言)。
更正:TS 侧扫描所依据的前提似乎不成立
R1-c1 认为守护进程变成"无控制台"后,"创建标志不会继承,因此每个未带 windowsHide 的控制台子进程都会分配自己的可见控制台窗口"。但 CREATE_NO_WINDOW 并不是让进程"没有控制台":按 Win32 进程创建标志文档,它是与 CREATE_NEW_CONSOLE/DETACHED_PROCESS 并列的第三种控制台分配模式——子进程会获得一个新的、但从未创建窗口的控制台。而不传任何控制台标志时的默认行为就是继承父进程的控制台,所以从守护进程派生的 taskkill/git/ACP 通道继承的是那个无窗口控制台,不会显示任何东西。(这与新的 Rust 测试一致:GetConsoleWindow() 返回 NULL 正是因为从未创建窗口。)真正"没有控制台"的父进程是 Tauri GUI 进程——这也正好说明 local_control.rs 才是真 bug,而守护进程侧的 spawn 并不是。
我不建议回退任何一处:新增的每个 windowsHide: true 都与 packages/core 中已有约 15 处的既有约定一致,非 Windows 上是空操作,也确实覆盖了 qwen serve 以其他方式启动的情形。但值得记录:本 PR 的 TS 部分——现已覆盖 packages/core/src/tools/shell.ts、packages/core/src/services/shellExecutionService.ts 与 packages/acp-bridge/src/spawnChannel.ts——属于加固而非 #9043 修复的组成部分,而它是基于一个不成立的判断扩张起来的。如果这部分开始消耗额外的评审轮次,可以拆出去,对 #9043 的修复没有任何削弱。
次要
减弱动效门禁用 /@media \(prefers-reduced-motion: reduce\) \{([\s\S]*?)(?:@media|<\/style>)/ 提取区块,而 bootstrap/index.html 中下一个 @media 是约 150 行之后的 max-width: 640px,所以捕获范围远超该媒体查询的闭合括号:两条居中规则只需出现在这段范围内的任意位置即可通过,把它们移出媒体查询门禁仍然是绿的。它仍能拦住整体删除,所以这只是"弱预言"提示,不算缺陷。
|
@qwen-code /review --timeout=360 |
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 485 passed · 0 failed · 485 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:485 通过 · 0 失败 · 485 总计 Verification reportPR 9064 verification —
|
| Cell | Environment | Oracle | Result |
|---|---|---|---|
| C0a head control | merge tree, packages/core |
shellExecutionService.test.ts |
green 135/135 (predicted) |
| C0b head control | packages/acp-bridge |
spawnChannel.test.ts |
green 41/41 (predicted) |
| C0c head control | packages/core |
shell.test.ts |
green 294/294 (predicted) |
M1 spawnChannel.ts minus windowsHide: true |
mutant | spawnChannel.test.ts |
red at AssertionError: expected undefined to be true in "scrubs daemon-only env vars from the spawned ACP child" (predicted) |
M2 WINDOWS_TASKKILL_OPTIONS → { windowsHide: false } |
mutant | shellExecutionService.test.ts |
red, 9 tests fail on the #5873 taskkill toHaveBeenCalledWith(..., HIDDEN_WINDOW) assertions (predicted) |
M3 shell.ts git-attribution site minus windowsHide |
mutant | shell.test.ts |
green 294/294 — survivor (see Finding 1) |
| restore check | — | git status --porcelain on mutated paths |
clean |
M1/M2 prove the new tests are load-bearing: they fail against the un-fixed source at exactly the assertions the PR added, with expected-vs-received values in the message.
Cell table 2 — release gate test-release.js (oracle: gate exit code + assertion message; witness 02-ab-release-gate-cells.png)
| Cell | Tree under head's gate script | Result |
|---|---|---|
| R0 head control | head files (scratch copy) | green, "Desktop release helper checks passed." (predicted) |
R1 base index.html (A/B control) |
pre-PR CSS | red at "reduced-motion startup view must keep the logo and status text on the same horizontal center" (predicted) |
R2 base runtime.rs (A/B control) |
pre-PR call site | red at "DesktopRuntime::start must spawn the runtime through the hidden-console helper" (predicted) |
R3 mutant: let mut child = command.group_spawn() |
reverted call site | red at the call-site pin (predicted) — the exact regression commit 5 claims to guard |
R4 mutant: drop the .brand { justify-content: center } rule |
head minus new rule | red at the centering assertion (predicted) |
R0 calibrates the scratch harness (it reproduces the main-tree green), so R1–R4 reds are attributable to the mutated inputs, not the harness.
Cell table 3 — Rust mechanism, static verification against the pinned dependency
No Rust toolchain exists in this container (command -v cargo rustc → none), so the cfg(windows) test could not run. Instead the mechanism claim was verified against the exact command-group 5.0.1 crate the build uses (downloaded from static.crates.io; tarball sha256 a68fa787… equals the Cargo.lock checksum):
| Claim | Evidence from crate source | Holds |
|---|---|---|
group_spawn() overwrites creation flags set directly on the Command |
group_spawn() = group().spawn(); builder starts with creation_flags: 0; spawn() does self.command.creation_flags(self.creation_flags | CREATE_SUSPENDED) (std CommandExt::creation_flags replaces) |
yes |
command.group().creation_flags(CREATE_NO_WINDOW).spawn() is the correct pattern |
effective flags become CREATE_NO_WINDOW | CREATE_SUSPENDED |
yes |
| job-object grouping + suspend/resume unchanged | assign_child() assigns to the job object then resume_threads() (winres.rs:148) on every group spawn |
yes |
CREATE_NO_WINDOW = 0x08000000 |
matches the documented constant; used identically in runtime.rs and local_control.rs |
yes |
| non-Windows behavior unchanged | #[cfg(not(windows))] arm is literally command.group_spawn(), identical to base; cfg arms exhaustive |
yes |
| all start/restart/recover paths use the helper | restart_runtime → start_runtime_async → DesktopRuntime::start (the single spawn site, pinned by R2/R3); Rust production spawn sweep = runtime child + local-control sleep inhibitor, both now hidden |
yes |
Corrections
- PR body vs. code (description drift, not a code defect): the body states the Local Control sleep inhibitor "is left unchanged", but the fourth commit (
9ef0043b) addsCREATE_NO_WINDOWto exactly that spawn (local_control.rs:646). The code change is in the same direction as the PR's purpose and is correct for a plainCommandspawn (no group builder to overwrite it). The description is stale, not the code.
Findings
- Suggestion — the seven
windowsHidesites inshell.tsare unpinned by any test. The PR added assertions tospawnChannel.test.tsandshellExecutionService.test.ts(both proven load-bearing above) but none forshell.ts; mutant M3 (git-attribution site reverted) survived 294/294. The behavior is correct in code and matches the repo's established convention, so this is completeness reporting, not a merge condition. A follow-up could spy theexecFile/spawnoptions the wayspawnChannel.test.tsdoes. - Suggestion, pre-existing — bare
taskkillname in the twoshell.tstaskkill sites (shell.ts:3102,3253), whileshellExecutionService.tsdeliberately uses the absoluteSystem32\taskkill.exepath as binary-planting hardening (难绷逆天BUG:用一次工具开一个powershell 并且不再关闭 直到OOM #5873). The PR touched these lines (options arg only) and inherited the bare name; the pre-existing cause and the PR's contribution are named separately on purpose — this is not introduced by the PR. - Nit — the release-gate pins are textual and order-blind. The
runtime.rspin fails closed on a benign reformat (safe direction). The CSS gate pins rule content inside the media block but not the cascade order: the status-card chrome removal depends on the visually-hiddenbody[data-state='starting'] .statusrule (border: 0; padding: 0) preceding the reduced-motion block at equal specificity; a refactor that reorders them would silently re-hide or re-chrome the reduced-motion status while the gate stays green. Same completeness class as Finding 1.
Not covered
cargo teston windows-2022 / ubuntu-22.04, including the newruntime_child_gets_no_windows_consoletest and its vacuity proof: no Rust toolchain in this container (verifiedcommand -v cargo rustcempty). Compensated by cell table 3 (pinned-crate source verification) and cells R2/R3 (call-site pin). The PR's own CI runs the Rust suite.- Packaged-Windows visual confirmation of both the hidden console and the reduced-motion centering — the PR itself declares this unvalidated; the CSS was verified by cascade trace (logo and status are the only visible children of
.shell, both centered within the same shrink-wrapped width). - Per-commit attribution: shallow depth-2 checkout (
git rev-parse --is-shallow-repository= true;rev-list HEAD^1..HEAD^2yields 1 commit vs. 5 in the metadata snapshot). The aggregateHEAD^1..HEADdiff is what was verified. - Real-browser rendering of the bootstrap view (no browser in container); the
test-release.jsDOM harness exercises state transitions, not layout.
Methodology
Environment: node:22-bookworm CI verify container, merge-ref checkout (HEAD = b5c169e0, base tip HEAD^1, PR head HEAD^2), npm ci + npm run build pre-provided. Harnesses live in harness/ab-vitest.mjs (in-place mutation with byte-level restore, verified by git status) and harness/ab-release-gate.mjs (per-cell scratch trees: copied packages/desktop-shell + symlinked .github, gate run from inside). Raw per-cell logs in logs/; evidence PNGs rendered by scripts/verify-capture.mjs. Assertion count: 470 vitest tests at head (135+294+41) + 7 vitest-cell expectations + 5 release-gate-cell expectations + 1 head release-gate run + 1 prettier --check + 1 tsc --noEmit = 485; fail counts only unexpected outcomes (0). Repo typecheck (tsc --noEmit) exit 0.
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM on re-run at this head — the desktop-shell fix is correct and pinned by the Windows-runner probe, the follow-up hardening sweep is complete for the flagged sites and inert off-Windows, and CI is green on the exact commit. See the staged comments for the full notes. ✅
|
Released in v0.21.12. |


What this PR does
This fixes both startup problems from #9043 in Qwen Code Desktop (the Tauri shell in
packages/desktop-shell). First, the bundled runtime child is now spawned withCREATE_NO_WINDOWon Windows, so starting, restarting, or recovering the runtime no longer opens a visible Windows Terminal/console window, and closing unrelated windows can no longer kill the runtime. Second, the bootstrap view's reduced-motion fallback (used when Windows animation effects are disabled) now stacks the logo and the "Starting Qwen Code" status in one centered column and drops the leftover status-card chrome, so both share the same horizontal center as the simplified normal-motion startup view from #8988.Why it's needed
The desktop app is a GUI-subsystem Windows process, but the bundled runtime (
node.exerunningqwen serve) is a console application. Creating a console child withoutCREATE_NO_WINDOWmakes Windows allocate a visible console window for it; closing that window terminates the runtime, and Desktop then reportsQwen Code stopped: exit code: 1. The spawn usescommand_group::CommandGroup::group_spawn(), which replaces any creation flags set directly on theCommandwith the group builder's own flags ORed withCREATE_SUSPENDED, so the flag must be passed through the builder (command.group().creation_flags(...).spawn()) — this is the exact pattern the crate documents for hiding console windows. The reduced-motion branch of the startup view was left behind when #8988 simplified the normal loading screen: the logo stayed left-aligned in the shell while the newly visible status text sat centered inside a wider card, so the two did not share a horizontal center.Reviewer Test Plan
How to verify
On Windows, build or install the Desktop app and launch it with the default workspace: no separate terminal/console window should appear while the bundled runtime starts, and the app should reach the Web Shell. Trigger a runtime failure and use Retry, or choose another workspace — both restart paths go through the same spawn helper, so no console window should appear there either. With Windows animation effects disabled (Settings > Accessibility > Visual effects, or OS-level
prefers-reduced-motion), the startup view should show the logo with "Starting Qwen Code" directly beneath it, both on the same horizontal center, with no leftover status-card border or padding. In CI,cargo testforpackages/desktop-shell/src-tauriruns onwindows-2022and includes the newruntime_child_gets_no_windows_consoletest, which spawns a child through the same helper and asserts the child has no attached console window (GetConsoleWindow() == 0). Locally,node scripts/test-release.jspasses, including the new assertion guarding the reduced-motion centering rules, andprettier --checkis clean for the changed files.Evidence (Before & After)
Before: the issue screenshots in #9043 show the visible Windows Terminal window for the runtime and the misaligned reduced-motion logo/status. After: no local Windows machine was available to produce matching screenshots; the behavior is covered by the new Windows CI test for the hidden console and the bootstrap release check for the centering, and needs a visual confirmation on a Windows build.
Tested on
Environment (optional)
Linux:
node scripts/test-release.js(desktop release/bootstrap regression checks) and Prettier on the changed files. Rust compile/tests were not run locally (no Rust toolchain or WebKitGTK headers in this environment); the desktop-shell CI job runscargo teston ubuntu-22.04 and windows-2022.Risk & Scope
CREATE_NO_WINDOWthrough the command-group builder; the crate still applies its job-object grouping andCREATE_SUSPENDED+ thread resume, so kill-on-stop and process-group behavior are unchanged. Other platforms keepgroup_spawn()exactly as before.powershell.exeon Windows withoutCREATE_NO_WINDOW; that is a separate code path not reported in Windows Desktop opens a visible runtime Terminal and misaligns the loading state #9043 and is left unchanged.Linked Issues
Fixes #9043
中文说明
这个 PR 做了什么
修复 #9043 中 Qwen Code Desktop(
packages/desktop-shell的 Tauri 外壳)的两个启动问题。第一,Windows 上捆绑运行时子进程现在带CREATE_NO_WINDOW标志启动,启动、重启或恢复运行时不会再弹出可见的 Windows Terminal/控制台窗口,关闭无关窗口也不会再杀死运行时。第二,启动视图的减弱动效回退分支(Windows 关闭动画效果时使用)现在把 logo 和 "Starting Qwen Code" 状态堆叠在同一列并整体居中,同时去掉了残留的状态卡片边框和内边距,使两者与 #8988 简化后的常规启动视图保持同一水平中心。为什么需要
桌面应用是 GUI 子系统的 Windows 进程,而捆绑运行时(运行
qwen serve的node.exe)是控制台应用。创建控制台子进程时不带CREATE_NO_WINDOW,Windows 会为它分配一个可见的控制台窗口;关闭该窗口会终止运行时,Desktop 随后报Qwen Code stopped: exit code: 1。启动代码使用command_group::CommandGroup::group_spawn(),它会用 group builder 自身的标志(或上CREATE_SUSPENDED)覆盖直接设置在Command上的创建标志,所以标志必须通过 builder 传入(command.group().creation_flags(...).spawn())——这正是该 crate 文档中隐藏控制台窗口的推荐写法。减弱动效分支在 #8988 简化常规加载界面时被遗漏:logo 仍在外壳中左对齐,而新出现的状态文字居中在更宽的卡片里,两者没有共享同一水平中心。评审测试计划
如何验证
在 Windows 上构建或安装 Desktop 应用并用默认工作区启动:运行时启动期间不应出现单独的终端/控制台窗口,应用应能进入 Web Shell。触发一次运行时失败并使用 Retry,或选择其他工作区——两条重启路径都走同一个启动辅助函数,同样不应出现控制台窗口。在 Windows 关闭动画效果(设置 > 辅助功能 > 视觉效果,或系统级
prefers-reduced-motion)时,启动视图应显示 logo,其正下方为 "Starting Qwen Code",两者处于同一水平中心,且没有残留的状态卡片边框或内边距。CI 中packages/desktop-shell/src-tauri的cargo test在windows-2022上运行,包含新增的runtime_child_gets_no_windows_console测试:它通过同一辅助函数启动子进程,并断言子进程没有附加的控制台窗口(GetConsoleWindow() == 0)。本地node scripts/test-release.js通过(含新增的减弱动效居中规则断言),改动文件的prettier --check也通过。证据(修改前后)
修改前:#9043 的截图展示了可见的 Windows Terminal 窗口和未对齐的减弱动效 logo/状态。修改后:本地没有 Windows 机器可以产出对应截图;行为由新增的 Windows CI 测试(隐藏控制台)和 bootstrap 发布检查(居中规则)覆盖,需要在 Windows 构建上做一次视觉确认。
测试环境
环境(可选)
Linux:
node scripts/test-release.js(桌面发布/bootstrap 回归检查)以及对改动文件的 Prettier 检查。本地未运行 Rust 编译/测试(该环境没有 Rust 工具链和 WebKitGTK 头文件);desktop-shell 的 CI 任务会在 ubuntu-22.04 和 windows-2022 上运行cargo test。风险与范围
CREATE_NO_WINDOW;该 crate 仍然应用其 job object 分组和CREATE_SUSPENDED+ 线程恢复,因此停止时杀进程和进程组行为不变。其他平台完全保持原来的group_spawn()。CREATE_NO_WINDOW启动powershell.exe;那是 Windows Desktop opens a visible runtime Terminal and misaligns the loading state #9043 未报告的独立代码路径,本次不改动。关联 Issue
Fixes #9043