Summary
cargo test -p host-core --locked is red on Windows because 11 test expectations spell the expected path with std Path::canonicalize() while the code under test spells its results with the crate's own simple_canonicalize(). On Windows the two disagree, so the assertions fail there and nowhere else.
Root cause
crates/host-core/src/workspace.rs defines:
/// Canonicalize a path, stripping the Windows extended-length prefix (`\?\`)
/// when the result is a simple drive-letter path (e.g. `C:\...`).
fn simple_canonicalize(path: &Path) -> std::io::Result<PathBuf> {
let canonical = path.canonicalize()?;
#[cfg(windows)]
{ /* strip `\?\X:\...` -> `X:\...` */ }
Ok(canonical)
}
The stripping is deliberate — the doc comment explains that shell APIs such as ShellExecuteW reject \?\. Every path the resolver returns (resolve_in_workspace, resolve_tool_path, resolve_external_path, via resolve_with_existing_ancestor) carries that spelling.
The tests, however, build their expected value with Path::canonicalize() / .canonicalize() directly, which keeps \?\ on Windows. Path equality and strip_prefix compare components, and VerbatimDisk('C') != Disk('C'), so the two never match on Windows. On Linux/macOS both spellings are identical, which is why CI — ubuntu-latest only — has never caught this.
Failing tests (Windows, cargo 1.98.1, after #207 which fixes the unrelated relative_display case)
workspace::tests::allows_new_nested_path (workspace.rs:326)
workspace::tests::tool_path_relative_resolves_to_workspace (workspace.rs:394)
workspace::tests::tool_path_absolute_scratch_resolves_to_scratch (workspace.rs:405)
workspace::tests::approved_external_path_resolves_and_keeps_absolute_identity (workspace.rs:450)
workspace::tests::relative_escape_can_be_resolved_only_after_external_approval (workspace.rs:468)
rpc::tests::project_create_returns_an_id_without_switching_workspace (rpc/mod.rs:3778)
rpc::tests::builtin_delegate_without_permission_scope_inherits_auto_for_external_glob (rpc/mod.rs:6049)
sessions::tests::create_session_returns_canonical_project_path (sessions.rs:3228)
tools::tests::approved_external_paths_work_for_read_and_search_tools (tools/mod.rs:3297)
mcp_servers::tests::project_server_is_copied_and_state_is_pruned_after_removal (mcp_servers.rs:624)
user_skills::tests::imports_one_file_into_the_selected_agents_directory (user_skills.rs:543)
Representative assertion output:
thread 'workspace::tests::tool_path_relative_resolves_to_workspace' panicked at workspace.rs:394:
assertion failed: p.starts_with(ws.path().canonicalize().unwrap())
Why the production code is right and the tests are wrong
workspace::path_is_within already handles Windows explicitly (case-insensitive component comparison), and db.rs / sessions.rs normalize separators for protocol paths. So simple_canonicalize is the intended canonical spelling, and the expectations are the side that is out of step.
Note some of these tests are not simply "expected == std canonicalize":
mcp_servers.rs:624 and user_skills.rs:543 compare against a raw dir.path().join(...) that was never canonicalized at all, so they also fail whenever the temp root's raw spelling (e.g. an 8.3 short name such as DAJIAO~1) differs from its canonical spelling.
Proposed fix
Have the expectations canonicalize with the same helper the code uses — simple_canonicalize — rather than Path::canonicalize(). That makes the assertion compare like with like on every platform and keeps the Windows-specific behavior covered instead of skipped.
If the maintainers would rather not widen the helper's visibility for tests, the alternative is to #[cfg(unix)]-gate the affected assertions, but that drops Windows coverage of a containment boundary, so the first option seems preferable.
Environment
PI-Desktop 6e6d710e (main), Windows 10 Pro N 19045, rustc 1.98.1, cargo 1.98.1. CI (ci.yml) runs cargo test -p host-core --locked on ubuntu-latest only.
I am happy to send a PR for this if the direction is agreed — it is a test-expectation change with no production behavior change.
Summary
cargo test -p host-core --lockedis red on Windows because 11 test expectations spell the expected path with stdPath::canonicalize()while the code under test spells its results with the crate's ownsimple_canonicalize(). On Windows the two disagree, so the assertions fail there and nowhere else.Root cause
crates/host-core/src/workspace.rsdefines:The stripping is deliberate — the doc comment explains that shell APIs such as
ShellExecuteWreject\?\. Every path the resolver returns (resolve_in_workspace,resolve_tool_path,resolve_external_path, viaresolve_with_existing_ancestor) carries that spelling.The tests, however, build their expected value with
Path::canonicalize()/.canonicalize()directly, which keeps\?\on Windows.Pathequality andstrip_prefixcompare components, andVerbatimDisk('C') != Disk('C'), so the two never match on Windows. On Linux/macOS both spellings are identical, which is why CI —ubuntu-latestonly — has never caught this.Failing tests (Windows, cargo 1.98.1, after #207 which fixes the unrelated
relative_displaycase)Representative assertion output:
Why the production code is right and the tests are wrong
workspace::path_is_withinalready handles Windows explicitly (case-insensitive component comparison), anddb.rs/sessions.rsnormalize separators for protocol paths. Sosimple_canonicalizeis the intended canonical spelling, and the expectations are the side that is out of step.Note some of these tests are not simply "expected == std canonicalize":
mcp_servers.rs:624anduser_skills.rs:543compare against a rawdir.path().join(...)that was never canonicalized at all, so they also fail whenever the temp root's raw spelling (e.g. an 8.3 short name such asDAJIAO~1) differs from its canonical spelling.Proposed fix
Have the expectations canonicalize with the same helper the code uses —
simple_canonicalize— rather thanPath::canonicalize(). That makes the assertion compare like with like on every platform and keeps the Windows-specific behavior covered instead of skipped.If the maintainers would rather not widen the helper's visibility for tests, the alternative is to
#[cfg(unix)]-gate the affected assertions, but that drops Windows coverage of a containment boundary, so the first option seems preferable.Environment
PI-Desktop
6e6d710e(main), Windows 10 Pro N 19045,rustc 1.98.1,cargo 1.98.1. CI (ci.yml) runscargo test -p host-core --lockedonubuntu-latestonly.I am happy to send a PR for this if the direction is agreed — it is a test-expectation change with no production behavior change.