Skip to content

fix(desktop): strip Windows verbatim prefix from workspace paths - #8619

Merged
yiliang114 merged 7 commits into
QwenLM:mainfrom
yiliang114:fix/issue-8615-verbatim-workspace
Aug 7, 2026
Merged

fix(desktop): strip Windows verbatim prefix from workspace paths#8619
yiliang114 merged 7 commits into
QwenLM:mainfrom
yiliang114:fix/issue-8615-verbatim-workspace

Conversation

@yiliang114

@yiliang114 yiliang114 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR replaces std::fs::canonicalize with dunce::canonicalize at the two workspace-path canonicalization sites in the desktop shell: start_runtime_async in main.rs (before the workspace is persisted to desktop-state.json and handed to the runtime spawn) and resolve_workspace in runtime.rs (before the path becomes the child process cwd and the --workspace argument). It adds dunce as a direct dependency (already present in Cargo.lock transitively via tauri plugins, so no new code is compiled) and a regression test asserting resolve_workspace never returns a \\?\-prefixed path.

Why it's needed

Fixes #8615. On Windows, std::fs::canonicalize returns extended-length verbatim paths such as \\?\C:\dev\MathDesk. The desktop shell persisted that form and used it as the bundled Node runtime's cwd and --workspace argument. Node's own bootstrap (resolveMainPathrealpathSync) interprets the \\?\ prefix as a UNC path, the entry-script resolution collapses to the bare drive root C:, and the runtime crashes on startup with EISDIR: lstat 'C:' — the app cannot start at all. Because the verbatim path is persisted in desktop-state.json, every subsequent launch crashes identically, with no self-healing path. dunce::canonicalize behaves like fs::canonicalize but simplifies the result back to a plain drive-letter path whenever that is safe (it keeps the verbatim form only when the path genuinely requires it), and on non-Windows platforms it delegates to fs::canonicalize unchanged. Since start_runtime_async re-canonicalizes the persisted workspace on every launch, already-affected installs (with a \\?\ path saved by Desktop 0.1.0) recover automatically on their first launch after this fix, and the simplified path is written back to desktop-state.json.

Reviewer Test Plan

How to verify

From packages/desktop-shell, run cargo test --manifest-path src-tauri/Cargo.toml. All 23 desktop-shell tests pass, including the new runtime::tests::resolve_workspace_strips_windows_verbatim_prefix regression test:

test runtime::tests::resolve_workspace_strips_windows_verbatim_prefix ... ok

test result: ok. 23 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Expected behavior: resolve_workspace never returns a \\?\-prefixed path, and on non-Windows platforms behavior is unchanged (dunce delegates to std::fs::canonicalize). The literal crash requires Windows (\\?\ verbatim prefixes are a Windows-only path form); the regression test asserts the de-verbatimized output and is most meaningful on Windows CI.

Evidence (Before & After)

N/A from local — the literal crash requires a Windows machine, which is not available here; the cargo test output above is the local evidence, and the change has no TUI surface (Rust-side path handling only).

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux N/A

macOS: desktop-shell unit suite (23/23 passed). Windows: not tested locally; the new PR-gated windows-2022 Rust job validates both safe simplification and residual verbatim-path rejection. Linux: behavior unchanged, dunce::canonicalize delegates to std::fs::canonicalize on non-Windows.

Environment (optional)

Unit tests only (cargo test --manifest-path src-tauri/Cargo.toml in packages/desktop-shell); no app runtime needed.

Risk & Scope

  • Main risk or tradeoff: paths that still require a Windows verbatim form (including long and UNC paths) are now rejected before persistence or runtime startup with an actionable error; they remain unsupported instead of entering a restart crash loop.
  • Not validated / out of scope: the literal end-to-end crash is not reproduced locally; the new Windows CI leg covers the Rust path boundary. The Electron-based packages/desktop monorepo is untouched.
  • Breaking changes / migration notes: none. Already-affected installs self-heal on their next launch because start_runtime_async re-canonicalizes the persisted workspace.

Linked Issues

Fixes #8615

Related to #8400, #7139 (fixed in #7228), #8308 — investigated as duplicate candidates during triage; distinct mechanisms but the same Windows path-handling area.

中文说明

本 PR 做了什么

将 desktop shell 中两处 workspace 路径规范化从 std::fs::canonicalize 替换为 dunce::canonicalizemain.rsstart_runtime_async(在 workspace 持久化到 desktop-state.json 并传给 runtime 启动逻辑之前)和 runtime.rsresolve_workspace(在该路径成为子进程 cwd 和 --workspace 参数之前)。新增 dunce 直接依赖(它已通过 tauri 插件存在于 Cargo.lock 传递依赖中,不会新增编译代码),并新增回归测试,断言 resolve_workspace 不会返回带 \\?\ 前缀的路径。

为什么需要

修复 #8615。在 Windows 上,std::fs::canonicalize 返回扩展长度 verbatim 路径(如 \\?\C:\dev\MathDesk)。desktop shell 将该形式持久化,并用作内置 Node runtime 的 cwd 和 --workspace 参数。Node 自身引导过程(resolveMainPathrealpathSync)将 \\?\ 前缀解释为 UNC 路径,入口脚本解析坍缩到裸盘符根 C:,runtime 启动时以 EISDIR: lstat 'C:' 崩溃——应用完全无法启动。由于 verbatim 路径已持久化到 desktop-state.json,之后每次启动都会同样崩溃,无法自愈。dunce::canonicalize 行为与 fs::canonicalize 相同,但会在安全时将结果简化为普通盘符路径(仅在路径确实需要时才保留 verbatim 形式),在非 Windows 平台上直接委托给 fs::canonicalize,行为不变。由于 start_runtime_async 每次启动都会对已持久化的 workspace 重新规范化,受影响的用户(Desktop 0.1.0 已保存 \\?\ 路径)在应用本修复后的首次启动即可自动恢复,且简化后的路径会被写回 desktop-state.json

评审者测试计划

如何验证

packages/desktop-shell 下运行 cargo test --manifest-path src-tauri/Cargo.toml。desktop-shell 全部 23 个测试通过,包括新增的 runtime::tests::resolve_workspace_strips_windows_verbatim_prefix 回归测试(输出见英文部分)。预期行为:resolve_workspace 永不返回 \\?\ 前缀路径;非 Windows 平台行为不变。崩溃本身需要 Windows 环境(\\?\ verbatim 前缀是 Windows 独有路径形式);回归测试断言去 verbatim 化的输出,在 Windows CI 上最有意义。

证据(修复前后)

本地 N/A——字面崩溃需要 Windows 机器,本地不具备;上方 cargo test 输出即本地证据。该改动无 TUI 界面变化(仅 Rust 侧路径处理)。

测试平台

OS 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux N/A

macOS:desktop-shell 单测套件(23/23 通过)。Windows:本地未做字面复现;新增的 PR 门控 windows-2022 Rust 任务会验证安全去前缀和残留 verbatim 路径拒绝。Linux:行为不变,dunce::canonicalize 在非 Windows 上委托给 std::fs::canonicalize

环境(可选)

仅单元测试(packages/desktop-shellcargo test --manifest-path src-tauri/Cargo.toml),无需应用运行时。

风险与范围

  • 主要风险或权衡:仍然需要 Windows verbatim 形式的路径(包括长路径和 UNC)现在会在持久化或 runtime 启动前返回可操作错误;它们仍不受支持,但不会再进入重启崩溃循环。
  • 未验证 / 超出范围:本地未做 Windows 字面端到端崩溃复现;新增 Windows CI 会覆盖 Rust 路径边界。未触碰基于 Electron 的 packages/desktop monorepo。
  • 破坏性变更 / 迁移说明:无。已受影响的用户在下次启动时自动恢复,因为 start_runtime_async 会对已持久化的 workspace 重新规范化。

关联 Issue

Fixes #8615

Related to #8400#7139(已在 #7228 修复)、#8308——triage 阶段作为重复候选排查过;机制不同,但同属 Windows 路径处理领域。

On Windows, std::fs::canonicalize returns extended-length verbatim
paths (\\?\C:\dev\MathDesk). The desktop shell persisted that form to
desktop-state.json and passed it as the bundled Node runtime's cwd and
--workspace argument, and Node's bootstrap (resolveMainPath ->
realpathSync) crashed with EISDIR: lstat 'C:', so Desktop 0.1.0 failed
to start at all. Because the verbatim path is persisted, every
subsequent launch crashed the same way.

Use dunce::canonicalize at both canonicalization sites
(start_runtime_async, resolve_workspace). It behaves like
fs::canonicalize but simplifies the result to a plain drive-letter path
whenever that is safe, and delegates to fs::canonicalize unchanged on
non-Windows platforms. Since start_runtime_async re-canonicalizes the
persisted workspace on every launch, affected installs recover on their
first launch after this fix, and the simplified form is written back to
desktop-state.json.

dunce is already in Cargo.lock transitively via the tauri plugins, so
no new code is compiled. Adds a regression test asserting
resolve_workspace never returns a \\?-prefixed path.

Fixes QwenLM#8615
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fast turnaround on #8615, @yiliang114 — the root-cause trace matches what the issue triage found, and dunce at the two canonicalization sites is the direction we'd expect. Before this can go to code review, the PR body needs to follow the PR template — right now it's free-form (## What / ## Why / ## Verification) and missing:

  • Reviewer Test Plan → Tested on: the important one here. This is a Windows-only crash; please fill the OS matrix and say plainly whether you built and ran the change on Windows. The Verification section reads as "cargo test only, the crash itself not reproduced" — if that's right, state it explicitly.
  • How to verify / Evidence (Before & After): what should a reviewer run or look at — cargo test --manifest-path src-tauri/Cargo.toml output at minimum; for the startup crash itself, either a Windows repro or an explicit "not reproduced, needs a Windows machine".
  • Risk & Scope: e.g. dunce::canonicalize keeps the \\?\ prefix when the path genuinely requires it (very long paths, unusual components) — that residual case is worth spelling out, plus a "not validated" line.
  • Linked Issues: Fixes #8615 is already in the body (auto-close wiring works), just move it under the heading.

Why this matters more than usual here: PR CI runs the desktop-shell cargo suite only on ubuntu-22.04, where the new regression test passes with or without the fix (Linux canonicalization never produces \\?\ paths), and the windows-latest / macos-latest jobs only run in the merge queue. So the Tested-on evidence for the platform this bug lives on has to come from the PR body itself.

Once the body is updated, re-run with @qwen-code /triage and it moves to code review.

中文说明

感谢对 #8615 的快速响应 —— 根因追踪与 issue triage 的结论一致,在两处 canonicalization 位置使用 dunce 正是预期方向。进入代码审查前,PR 描述需要先按 PR 模板 补全——目前是自由结构(## What / ## Why / ## Verification),缺少:

  • Reviewer Test Plan → Tested on:本 PR 最关键的一项。这是 Windows 专属崩溃,请填写 OS 矩阵,并明确说明是否在 Windows 上构建并运行过本改动。Verification 部分读起来是"只跑了 cargo test、崩溃本身未复现"——如果确实如此,请直接写明。
  • How to verify / Evidence (Before & After):reviewer 应该运行或查看什么——至少给出 cargo test --manifest-path src-tauri/Cargo.toml 的输出;启动崩溃本身要么提供 Windows 复现,要么明确写"未复现,需要 Windows 环境"。
  • Risk & Scope:例如 dunce::canonicalize 在路径确实需要 verbatim 形式时(超长路径、特殊组成部分)仍会保留 \\?\ 前缀——这一残留场景值得写明,并补充"未验证"项。
  • Linked IssuesFixes #8615 已在正文中(自动关闭已生效),移到该标题下即可。

为什么这次格外重要:PR CI 仅在 ubuntu-22.04 上运行 desktop-shell 的 cargo 测试套件,而新增回归测试在 Linux 上有无修复都会通过(Linux 的 canonicalization 从不产生 \\?\ 路径),windows-latest / macos-latest 任务只在 merge queue 中运行。因此这个 bug 所在平台的测试证据必须来自 PR 描述本身。

描述补全后,用 @qwen-code /triage 重新运行,即可进入代码审查。

Qwen Code · qwen3.8-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI failing: Test (ubuntu-latest, Node 22.x). Reviewed.

中文说明

⚠️ 已从批准降级为评论:CI failing: Test (ubuntu-latest, Node 22.x)。 已审查。

— qwen3.8-max via Qwen Code /review (v0.21.6)

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Refreshed this branch with the latest main in merge commit a8167c89515.

The previous Ubuntu failure was a timeout in clipboardUtils.test.ts, outside this PR's desktop-shell changes, so no product code was changed. Verified the refreshed branch with cargo test --locked: 23/23 tests passed, including the Windows verbatim-prefix regression test.

CI is rerunning. No screenshot was added because this is path normalization and test-only evidence without a meaningful UI state.

中文说明

已通过 merge commit a8167c89515 合入最新 main。此前 Ubuntu 失败来自本 PR 范围外的 clipboardUtils.test.ts 超时,因此没有修改产品代码。刷新后执行 cargo test --locked,23/23 测试通过,包括 Windows verbatim prefix 回归测试。CI 正在重新运行。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

中文说明

已审查——无阻断问题。 建议见行内评论。

— qwen3.8-max via Qwen Code /review (v0.21.6)

Comment thread packages/desktop-shell/src-tauri/src/runtime.rs
Comment thread packages/desktop-shell/src-tauri/src/runtime.rs Outdated
Comment thread packages/desktop-shell/src-tauri/src/main.rs Outdated
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Resolved the three active suggestion threads with no code changes in this pass.\n\n- A PR-gated Windows Rust job is a valid follow-up, but adding a new platform CI leg expands workflow scope beyond this path-normalization fix. The current workflow does not provide that Windows signal.\n- Residual Windows long-path and UNC handling remains the explicitly disclosed limitation. Rejecting those inputs would change compatibility and failure behavior, so it needs a maintainer decision.\n- The proposed shared canonicalization helper was not extracted because it is a refactor rather than a correctness requirement for this PR.\n\nCurrent PR checks are green. The focused Rust suite previously passed 23/23.\n\n

\n中文摘要\n\n本轮无代码修改。Windows Rust CI 属于合理后续,但新增平台矩阵会扩大本次路径修复范围;当前工作流并没有该 Windows 验证。Windows 长路径和 UNC 仍是 PR 已披露的限制,直接拒绝会改变兼容性与失败语义,需要维护者决策。共享 canonicalization helper 属于重构建议,本轮不采纳。当前 PR 检查为绿色,之前的定向 Rust 测试为 23/23。\n\n

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Implemented the maintainer-gated follow-up in 38a11e74ccd.

  • The PR-gated desktop-shell job now runs cargo test on both ubuntu-22.04 and windows-latest; Node release checks remain Linux-only.
  • start_runtime_async and DesktopRuntime::start now share resolve_workspace, so residual Windows verbatim paths are rejected before persistence and again before runtime arguments/cwd are created.
  • Added a Windows-only regression covering verbatim disk, UNC, and generic verbatim prefixes.
  • Verified locally: desktop-shell tests 23/23, workflow YAML parsing, actionlint, Prettier, and diff check.
  • Updated the PR body to describe the new failure boundary and Windows CI signal.

No screenshot: this is Rust path handling and CI behavior with no visual state.

中文摘要

已在 38a11e74ccd 落地后续决策:desktop-shell PR CI 现在同时运行 Ubuntu 和 Windows Rust 测试;两条 workspace 消费路径共用 resolve_workspace,残留的 Windows verbatim disk、UNC 或 generic prefix 会在持久化和 runtime 启动前返回可操作错误。新增 Windows-only 回归测试。本地 desktop-shell 23/23、YAML 解析、actionlint、Prettier 和 diff 检查通过;PR 描述已同步更新。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

已审查。 建议见行内评论。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

— qwen3.8-max via Qwen Code /review (v0.21.6)

Comment thread packages/desktop-shell/src-tauri/src/runtime.rs Outdated
Comment thread packages/desktop-shell/src-tauri/src/runtime.rs
Comment thread .github/workflows/ci.yml Outdated
@yiliang114

Copy link
Copy Markdown
Collaborator Author

All three active review threads are addressed in 9a60c06 and resolved:

  • Replaced the misleading shorter-path-only diagnostic with guidance covering unsupported Windows extended-length forms, including UNC shares, paths over 260 characters, and trailing dot/space components.
  • Added a Windows-only filesystem regression that sends a real long extended-length path through resolve_workspace; deleting the runtime-spawn guard now fails the test.
  • Pinned the PR desktop-shell Windows runner to windows-2022, matching the existing hosted gate, and updated the PR body.

Verification: Linux cargo test 23/23, rustfmt on the changed Rust file, Prettier, actionlint, diff check, and two final reviews (C=0 / S=0). The pushed matrix will execute the Windows-only regression.

@qwen-code /triage

@yiliang114 yiliang114 added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 6, 2026

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

已审查。 建议见行内评论。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

— qwen3.8-max via Qwen Code /review (v0.21.6)

Comment thread packages/desktop-shell/src-tauri/src/main.rs

@doudouOUC doudouOUC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI failing: web-shell E2E Smoke (ubuntu-latest, Node 22.x). Reviewed. Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

⚠️ 已从批准降级为评论:CI failing: web-shell E2E Smoke (ubuntu-latest, Node 22.x)。 已审查。 未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

— kimi-k3 via Qwen Code /review (v0.21.6)

@doudouOUC

Copy link
Copy Markdown
Collaborator

Review Summary — PR #8619

PR: fix(desktop): strip Windows verbatim prefix from workspace paths — replaces std::fs::canonicalize with dunce::canonicalize at two workspace-path sites in the desktop shell, fixing a Windows-only Node runtime crash (#8615).

Diff: 5 files, +83/-26, 204 source lines

Findings: 0

All 14 review agents (Issue Fidelity, Correctness, Security, Reuse, Altitude, Consistency, Performance, Test Coverage, 3 adversarial personas, Removed-behavior, Cross-file tracer, Build & Test) returned zero findings. Two reverse audit rounds both returned dry with substantive evidence.

Key validations:

  • Both canonicalization sites correctly converted; no remaining std::fs::canonicalize calls
  • ensure_supported_workspace_path guard correctly catches residual verbatim paths dunce can't strip
  • CI matrix properly adds windows-2022 with correct Linux-only step guards
  • Three new tests are mutation-resistant on the platform where the bug manifests
  • No security, performance, or abstraction concerns

Verdict: Comment (downgraded from Approve)

CI is failing on web-shell E2E Smoke (ubuntu-latest, Node 22.x) — unrelated to this PR's desktop-shell Rust changes. The PR itself is clean with zero findings.

Cost

217 model calls · 13.3M input (96% cached) · 280k output · 38 min wall


Reviewed with qwen-code-review-plugin (review-only mode)

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the autofix/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 1 finishedview run. See this round's report below.

中文说明

AutoFix 第 1 轮已完成 —— 查看运行。本轮报告见下方。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下:

Autofix review round — PR #8619

Addressed the new round-4 suggestion and re-verified the earlier findings. One commit: 16726db905 refactor(desktop): resolve workspace once per runtime start. No base merge was performed (--conflict false).

Feedback dispositions

  • [rc:3730565369] Double workspace resolution (round 4, Suggestion) — Fixed. DesktopRuntime::start no longer re-resolves the workspace; the idempotent second resolve_workspace call is removed and resolution/rejection now has exactly one owner: start_runtime_async, which resolves before settings.set_workspace persists the path and before the runtime's cwd/--workspace arguments are built. This is the reviewer's first (code-shrinking) option; the function now documents that callers pass a workspace already resolved by resolve_workspace. Verified the sole call site (main.rs) passes the resolved path, and src-tauri is a binary crate with no other callers of DesktopRuntime::start. Behavior on the only call path is unchanged; the duplicated dunce::canonicalize + verbatim guard + is_dir per start is gone, and the resolution policy can no longer sit on two sites bracketing the persistence write.
  • [rc:3727995152] Windows leg for the PR-gated desktop-shell job — Already fixed, re-verified. The desktop_shell job runs a ['ubuntu-22.04', 'windows-2022'] matrix and both legs are green on the current head (see checks).
  • [rc:3727995162] Residual \\?\ paths kept by dunce — Already fixed, re-verified. resolve_workspace rejects any canonical result that still carries a verbatim prefix via ensure_supported_workspace_path, before persistence and before spawn; the comment states dunce strips the prefix only when safe.
  • [rc:3727995171] Duplicated canonicalize block in main.rs — Already fixed, re-verified. start_runtime_async routes through the shared pub(crate) resolve_workspace and maps its error into emit_runtime_failure.
  • [rc:3729396638] Rejection message attributed everything to path length — Already fixed, re-verified. The message now names the unsupported Windows extended-length form and covers UNC shares, >260-char paths, and trailing dot/space components.
  • [rc:3729396667] Guard wiring had no through-resolve_workspace coverage — Already fixed, re-verified. The Windows-only regression creates a real extended-length directory (>270 UTF-16 units) via its \\?\ path and drives it through resolve_workspace; deleting the guard now fails the test.
  • [rc:3729396672] Floating windows-latest alias — Already fixed, re-verified. The matrix pins windows-2022, matching the existing hosted Windows gate.
  • Triage stage-1a review (CHANGES_REQUESTED, PR body template) — Needs PR-author action, not a code change. The request is to restructure the PR body per the template (Reviewer Test Plan → Tested on with the OS matrix and a plain statement about Windows testing, How to verify / Evidence, Risk & Scope incl. the residual verbatim cases, Linked Issues). This automation path operates on the checkout only and cannot edit the PR body; flagged here so it is not silently dropped.
  • Downgrade reviews citing CI failures (Test (ubuntu-latest, Node 22.x), web-shell E2E Smoke) — No action needed. Both checks are green on the current head.

Verification

Commands actually run this round:

  • npm run build — passed
  • npm run typecheck — passed (tsc --noEmit across core, sdk, web-shell, webui, external-context)
  • npm run lint — passed (eslint . --ext .ts,.tsx && eslint integration-tests)
  • Focused Vitest for touched packages — N/A: the only touched package is desktop-shell, which has no Vitest suite (its test script is the cargo suite)
  • cargo test --manifest-path src-tauri/Cargo.toml — not runnable on this self-hosted runner (no Rust toolchain installed; checked PATH, ~/.cargo, /opt, /usr/local). This suite is covered by the PR-gated Desktop Shell (ubuntu-22.04) and Desktop Shell (windows-2022) matrix jobs, both green on the previous head; the pushed commit re-runs them. The change is a mechanical removal of one call plus two &-adjustments whose typing was checked by inspection (workspace: &Path into runtime_arguments(&Path) and current_dir(impl AsRef<Path>)); no test calls DesktopRuntime::start.
  • Settings schema regeneration — not required (no settings source touched)
中文说明

Autofix 审查轮次 — PR #8619

本轮处理了第 4 轮新增建议,并复核了此前的各项发现。提交一个 commit:16726db905 refactor(desktop): resolve workspace once per runtime start。未执行 base 合并(--conflict false)。

反馈处理结论

  • [rc:3730565369] workspace 被重复解析(第 4 轮,建议)— 已修复。 DesktopRuntime::start 不再重复解析 workspace;幂等的第二次 resolve_workspace 调用已移除,解析/拒绝策略现在只有一个归属:start_runtime_async —— 它在 settings.set_workspace 持久化路径之前、以及在构造 runtime 的 cwd/--workspace 参数之前完成解析。这采用了 reviewer 给出的第一个(缩减代码的)方案;函数内现以注释注明调用方传入的是已由 resolve_workspace 解析好的路径。已核实唯一调用点(main.rs)传入的是已解析路径,且 src-tauri 是纯二进制 crate,DesktopRuntime::start 没有其他调用方。唯一调用路径上的行为不变;每次启动重复执行的 dunce::canonicalize + verbatim 守卫 + is_dir 被消除,解析策略也不再可能分布在持久化写入两侧的两个位置。
  • [rc:3727995152] 为 PR 门控的 desktop-shell 任务增加 Windows 分支 — 此前已修复,本轮复核确认。 desktop_shell 任务现运行 ['ubuntu-22.04', 'windows-2022'] 矩阵,两个分支在当前 head 上均为绿色(见 checks)。
  • [rc:3727995162] dunce 保留的残留 \\?\ 路径 — 此前已修复,本轮复核确认。 resolve_workspace 通过 ensure_supported_workspace_path 拒绝仍带 verbatim 前缀的规范化结果,且发生在持久化与启动之前;注释已说明 dunce 仅在安全时才去除前缀。
  • [rc:3727995171] main.rs 中重复的规范化代码块 — 此前已修复,本轮复核确认。 start_runtime_async 改为调用共享的 pub(crate) resolve_workspace,并将其错误映射到 emit_runtime_failure
  • [rc:3729396638] 拒绝消息把一切都归因于路径长度 — 此前已修复,本轮复核确认。 消息现明确指出不受支持的 Windows 扩展长度形式,并覆盖 UNC 共享、超过 260 字符的路径、以点/空格结尾的组件。
  • [rc:3729396667] 守卫接线缺少经过 resolve_workspace 的测试覆盖 — 此前已修复,本轮复核确认。 Windows-only 回归测试通过 \\?\ 路径创建真实的超长扩展长度目录(>270 个 UTF-16 单元),并让其经过 resolve_workspace;删除守卫后该测试会失败。
  • [rc:3729396672] 浮动的 windows-latest 别名 — 此前已修复,本轮复核确认。 矩阵固定为 windows-2022,与现有 hosted Windows 门控一致。
  • Triage stage-1a 审查(CHANGES_REQUESTED,PR 描述模板)— 需要 PR 作者处理,非代码改动。 该请求是按模板重构 PR 描述(Reviewer Test Plan → Tested on:填写 OS 矩阵并明确说明是否在 Windows 上测试过;How to verify / Evidence;Risk & Scope:含残留 verbatim 场景;Linked Issues)。本自动化流程仅操作 checkout,无法编辑 PR 描述;在此明确标注以免被遗漏。
  • 因 CI 失败而降级的审查(Test (ubuntu-latest, Node 22.x)web-shell E2E Smoke)— 无需处理。 这两项检查在当前 head 上均为绿色。

Verification(验证)

本轮实际执行的命令:

  • npm run build — 通过
  • npm run typecheck — 通过(core、sdk、web-shell、webui、external-context 均执行 tsc --noEmit
  • npm run lint — 通过(eslint . --ext .ts,.tsx && eslint integration-tests
  • 针对改动包的定向 Vitest — 不适用:唯一改动的包是 desktop-shell,它没有 Vitest 套件(其 test 脚本即 cargo 测试套件)
  • cargo test --manifest-path src-tauri/Cargo.toml — 本自托管 runner 上无法运行(未安装 Rust 工具链;已检查 PATH、~/.cargo/opt/usr/local)。该套件由 PR 门控的 Desktop Shell (ubuntu-22.04)Desktop Shell (windows-2022) 矩阵任务覆盖,两者在上一个 head 上均为绿色;推送的 commit 会重新运行它们。本次改动是机械性地移除一个调用并调整两处 &,其类型已经人工核对(workspace: &Path 传入 runtime_arguments(&Path)current_dir(impl AsRef<Path>));没有任何测试调用 DesktopRuntime::start
  • settings schema 重新生成 — 不需要(未改动任何 settings 源)

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

未发现问题。LGTM!✅

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

— qwen3.8-max via Qwen Code /review (v0.21.7)

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

中文说明

未发现问题。LGTM!✅

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/ci.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

— qwen3.8-max via Qwen Code /review (v0.21.7)

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Reviewed the remaining thread against the Windows verbatim-path fix scope. The duplicate canonicalization point is a cleanup/refactor follow-up, not required for this PR, so I am resolving it without code changes.

@yiliang114
yiliang114 enabled auto-merge August 7, 2026 16:20
@wenshao

wenshao commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

🔍 Maintainer local verification — verdict: merge-ready

24/24 scripted assertions passed, 0 unexpected failures. Verified head: 1f7830e2 (base 63a8ed43, both from QwenLM/qwen-code). Ran on macOS arm64, rust 1.97.1 — advisory local round following the /verify-pr methodology, not a CI gate.

中文摘要

结论:可合并——24/24 项脚本化断言全部通过。

  • A/B 对比:base 22 测试、head 23 测试全绿;resolve_workspace 对 10 种路径形态(普通目录、符号链接、...、尾斜杠、字面 \\?\ 名字的目录、非 UTF-8 错误路径、文件而非目录、不存在路径、相对路径)在 base/head 输出逐字节一致——"非 Windows 平台行为不变"成立(图 1)。
  • mutation 矩阵:回退核心 hunk(duncefs::canonicalize)后 macOS 上 23/23 依旧全绿——证明新回归测试在非 Windows 平台无法失败(空洞),真正的锚定是两个 #[cfg(windows)] 测试 + 本 PR 新增的 windows-2022 CI leg(已实测 25/25 通过,含全部 3 个新测试,图 2、图 3)。正向对照 mutation 杀死恰好 2 个既有测试,证明套件有效。
  • 次要声明全部核实dunce 1.0.5 在 base 的 Cargo.lock 已是传递依赖(lock 仅新增一条依赖边);dunce 源码确认非 Windows 委托 fs::canonicalize;全 crate 仅一个 canonicalize 点,三个 workspace 入口都先 resolve 再持久化再启动("已受损安装下次启动自愈"成立);trial merge 到当前 main 零冲突且 diff 与 PR 完全一致;actionlint 无告警。
  • 注意:PR 描述的 "23 tests" 是 macOS/Linux 数字,Windows 为 25;字面崩溃与长路径/UNC 拒绝需 Windows,本地无法复现,以 Windows CI leg 实测为准。

Central claim & A/B

Claim: dunce::canonicalize in resolve_workspace strips the Windows \\?\ prefix before persistence and before the path becomes the runtime cwd / --workspace arg, while non-Windows behavior is unchanged. The literal crash is Windows-only (honestly scoped in the PR body); locally I proved the non-Windows half plus vacuity, and re-checked the Windows half via the PR's own new CI leg.

cell suite resolve_workspace probe (10 path shapes)
base 63a8ed43 22 passed / 0 failed output set O
head 1f7830e2 23 passed / 0 failed byte-identical to O (PID-normalized diff)

Probe shapes: plain dir, symlink→dir, ., .., trailing slash, a directory literally named \\?\C:\fake (legal on APFS — not rejected, resolves identically on both sides, so the Component::Prefix guard cannot misfire off Windows), non-UTF-8 name (error branch), file-not-dir (error branch), nonexistent (error branch), relative path.

A/B suite counts and 10-shape probe

Vacuity / mutation matrix (head, macOS)

mutation result expectation reads as
M0 unmutated control 23/23 green green suite healthy
M1 revert duncefs::canonicalize 23/23 green green new regression test is vacuous off-Windows — the prefix can never occur there
M2 drop ensure_supported_workspace_path call 23/23 green green pinned only by the two #[cfg(windows)] tests
M3 positive control (flip loopback check) 2 failed 2 failed suite + harness can fail

M1 makes the PR body's "most meaningful on Windows CI" precise: the real pinning is the Windows leg this PR adds. Verified live — Desktop Shell (windows-2022) ran at head 1f7830e, 25/25 passed including all three new tests by name:

Windows CI leg: 25 tests, 3 new verbatim tests green

Mutation matrix

Secondary claims — all verified

  • dunce already transitive, no new code compiled: base Cargo.lock already pins dunce 1.0.5; the lock diff adds exactly one dependency edge, no package entries.
  • Non-Windows delegation: confirmed in the vendored source (dunce-1.0.5/src/lib.rs: #[cfg(not(windows))] { fs::canonicalize(path) }).
  • Single resolve point + self-heal: exactly one canonicalize call site remains; all three workspace entries (launch restore, folder picker, restart) funnel through start_runtime_async → resolve → set_workspace → spawn, so a \\?\ path persisted by 0.1.0 is rewritten on next launch as claimed.
  • ci.yml: actionlint clean; cargo test step runs on both matrix legs, Linux-only steps correctly gated on runner.os.
  • Trial merge into current main (edb42039): 0 conflicts, diff stat identical to the PR (5 files, +86/−29); main has not touched these files since the base.

Notes (non-blocking)

  1. Cross-platform regression test is vacuous off-Windows (M1) — acceptable because the same PR adds the Windows leg that pins it.
  2. PR body's "23 tests" is the macOS/Linux count; Windows runs 25. Body already scopes this correctly.
  3. Not-a-directory error at the start_runtime_async site now reads "Desktop workspace is not a directory: …" (was "Workspace is not a directory: …" inline on base) — matches the string base's runtime path already produced; cosmetic.

Not covered

Methodology

Two detached worktrees at the exact baseRefOid/headRefOid (no HEAD^1 assumptions), shared CARGO_TARGET_DIR; the probe is an identical #[cfg(test)] module appended to both trees' runtime.rs calling the crate-internal resolve_workspace, oracle = PID-normalized diff; mutations applied/restored by script with encoded expectations (exited 0); CI evidence via gh pr checks / gh run view --log. Full artifacts (harnesses, raw logs, PNGs): tmp/pr8619-verify-20260807-235644/ on the verifying machine; report + verdict.txt + assertions.json included there.

@yiliang114
yiliang114 added this pull request to the merge queue Aug 7, 2026
Merged via the queue into QwenLM:main with commit 106b53f Aug 7, 2026
50 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Desktop 0.1.0 / Windows] Bundled runtime crashes on startup: EISDIR lstat 'C:' when opening workspace

5 participants