Summary
Two tests assert behavior that depends on a capability the host does not have on Windows, so they fail with an environment error rather than an assertion about the code. Both are gated nowhere, and neither can fail in CI because ci.yml runs on ubuntu-latest only.
I am reporting them together because the shared shape is "the test needs a platform capability" — each has its own one-line fix, and they live in different packages, so feel free to split them.
1. tools::tests::bash_stderr_keeps_the_tail assumes a POSIX shell
crates/host-core/src/tools/mod.rs:2902 runs a POSIX command and asserts on its stderr:
let result = execute_tool(Some(ws.path()), None, "Bash",
&serde_json::json!({
"command": format!("seq 1 {lines} >&2; printf 'error: the real problem\n' >&2; exit 2")
}), 30_000).await;
...
assert!(stderr.trim_end().ends_with("error: the real problem"), ...);
On Windows the resolved shell is PowerShell, so the command never reaches a POSIX parser:
thread 'tools::tests::bash_stderr_keeps_the_tail' panicked at tools/mod.rs:2926:
tail retained: " : ParserError: (:) [], ParentContainsErrorRecordException\r\n + FullyQualifiedErrorId : MissingFileSpecification\r\n \r\n"
The shell is config.program from read_runner_config (tools/mod.rs:389), i.e. platform-selected — the suite already knows this, since it carries a sibling powershell_preserves_utf8_errors_quotes_cwd_and_native_exit_codes test at tools/mod.rs:4138.
Suggested fix: #[cfg(unix)] on this test, mirroring how the suite already gates symlink tests, or port the command to something both shells accept. The stderr-tail truncation logic it covers is shell-independent, so a PowerShell sibling would keep the coverage on Windows.
2. plugin-devkit test needs SeCreateSymbolicLinkPrivilege
packages/plugin-devkit/src/templates.test.ts:213:
it("rejects symlinks, which host-core refuses to copy", async () => {
const dir = await scaffolded("panel-basic");
await symlink(join(dir, "main.js"), join(dir, "link.js"));
expect((await check(dir)).errors.map((e) => e.code)).toContain("package.symlink");
});
pnpm test on Windows:
FAIL src/templates.test.ts > check > rejects symlinks, which host-core refuses to copy
Error: EPERM: operation not permitted, symlink
'C:\Users\...\Temp\pi-devkit-ESn7Tf\plugin\main.js' -> 'C:\Users\...\Temp\pi-devkit-ESn7Tf\plugin\link.js'
Creating a file symlink on Windows requires Developer Mode or elevation. Verified on the reporting host: fs.symlinkSync for a file returns EPERM, while fs.symlinkSync(..., 'junction') succeeds — and Node reports a junction through Dirent.isSymbolicLink() === true, which is the branch walkPluginDir tests (packages/plugin-devkit/src/walk.ts:104). Rust's FileType::is_symlink() sees junctions too, so copy_dir_filtered (crates/host-core/src/plugins.rs:2342) still refuses them.
The affected code path is a real install gate — a package containing a link must never install — so losing Windows coverage of it is worth avoiding.
Suggested fix: try the file symlink first and fall back to a directory junction when the platform refuses it (skipping only if both fail), so the assertion still runs on Windows. A plain skipIf on process.platform === 'win32' also works but drops the coverage on Developer-Mode machines. Note the repo already gates the Rust equivalent with #[cfg(unix)] (crates/host-core/src/db.rs:2503), which is why the TS side has no counterpart.
Environment
PI-Desktop 6e6d710e (main), Windows 10 Pro N 19045, Node 24.14.1, pnpm 11.18.0, rustc/cargo 1.98.1.
Related: #208 and #209 cover the other Windows failures in the same suites (different root causes, filed separately).
Summary
Two tests assert behavior that depends on a capability the host does not have on Windows, so they fail with an environment error rather than an assertion about the code. Both are gated nowhere, and neither can fail in CI because
ci.ymlruns onubuntu-latestonly.I am reporting them together because the shared shape is "the test needs a platform capability" — each has its own one-line fix, and they live in different packages, so feel free to split them.
1.
tools::tests::bash_stderr_keeps_the_tailassumes a POSIX shellcrates/host-core/src/tools/mod.rs:2902runs a POSIX command and asserts on its stderr:On Windows the resolved shell is PowerShell, so the command never reaches a POSIX parser:
The shell is
config.programfromread_runner_config(tools/mod.rs:389), i.e. platform-selected — the suite already knows this, since it carries a siblingpowershell_preserves_utf8_errors_quotes_cwd_and_native_exit_codestest attools/mod.rs:4138.Suggested fix:
#[cfg(unix)]on this test, mirroring how the suite already gates symlink tests, or port the command to something both shells accept. The stderr-tail truncation logic it covers is shell-independent, so a PowerShell sibling would keep the coverage on Windows.2.
plugin-devkittest needsSeCreateSymbolicLinkPrivilegepackages/plugin-devkit/src/templates.test.ts:213:pnpm teston Windows:Creating a file symlink on Windows requires Developer Mode or elevation. Verified on the reporting host:
fs.symlinkSyncfor a file returnsEPERM, whilefs.symlinkSync(..., 'junction')succeeds — and Node reports a junction throughDirent.isSymbolicLink() === true, which is the branchwalkPluginDirtests (packages/plugin-devkit/src/walk.ts:104). Rust'sFileType::is_symlink()sees junctions too, socopy_dir_filtered(crates/host-core/src/plugins.rs:2342) still refuses them.The affected code path is a real install gate — a package containing a link must never install — so losing Windows coverage of it is worth avoiding.
Suggested fix: try the file symlink first and fall back to a directory junction when the platform refuses it (skipping only if both fail), so the assertion still runs on Windows. A plain
skipIfonprocess.platform === 'win32'also works but drops the coverage on Developer-Mode machines. Note the repo already gates the Rust equivalent with#[cfg(unix)](crates/host-core/src/db.rs:2503), which is why the TS side has no counterpart.Environment
PI-Desktop
6e6d710e(main), Windows 10 Pro N 19045, Node 24.14.1, pnpm 11.18.0,rustc/cargo1.98.1.Related: #208 and #209 cover the other Windows failures in the same suites (different root causes, filed separately).