Repository navigation
Description
Context
Follow-up to #2129 ("eprintln! diagnostics are silently discarded on Windows"). PR #2136 fixed the daemon-child-process half of that issue (routed nodespaced's stdout/stderr to ~/.nodespace/logs/nodespaced.log/nodespaced-error.log instead of Stdio::null()), but was written and reviewed entirely on macOS with no Windows machine, VM, or cross-compilation toolchain available — see the PR description and the comment on #2129 for the full verification-gap writeup. This issue tracks the two things that genuinely need a real Windows environment.
Also folds in Windows validation for #2138's fix (kill_running_daemon's taskkill image name is now edition-aware — see below) since it's the same file's Windows code path.
1. Live-validate PR #2136 (and #2140) on a real Windows box
What was verified (macOS only): native cargo check/test/clippy --all-targets/fmt on the whole nodespace-app crate, plus an isolated scratch-crate cargo check/clippy --target x86_64-pc-windows-msvc of just the new #[cfg(windows)] functions (compiles + clippy-clean against the real target, but that's a partial crate with no tauri/ring in its dependency tree — it proves the Windows API usage type-checks, nothing about runtime behavior).
What still needs a real Windows machine:
- Full crate builds for
x86_64-pc-windows-msvc(a fullcargo check --target x86_64-pc-windows-msvcon this macOS machine fails onring's C build needing a Windows SDK/MSVC toolchain — unrelated to the PR's change, but it means the whole crate has never actually been type-checked here, only the isolated new functions). -
spawn_daemon_windowsactually creates and appends tonodespaced.log/nodespaced-error.logunder~/.nodespace/logs/when the daemon starts. - The file handles opened via
OpenOptions::new().create(true).append(true)correctly inherit into theDETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP-flagged child — confirm no handle-inheritance gotcha (Windows handles needbInheritHandle = TRUEat creation; verify Rust's stdCommand/Stdio::from(File)sets this up correctly for this specific flag combination). - Restarting the daemon (binary update / crash recovery, via
kill_running_daemon'staskkill /F /IM <edition-correct-name>.exe, fixed in desktop-app: Windows kill_running_daemon targets edition-correct binary name #2140 to usedaemon_binary_name()instead of a hardcodednodespaced.exeliteral) reopens the log files without a file-locking conflict, and appends rather than truncates across the restart. Also confirm the taskkill actually matches and kills the process for both editions (communitynodespaced.exeand Pronodespaced-pro.exe) — desktop-app: Windows kill_running_daemon targets edition-correct binary name #2140 fixed the edition-selection logic but the.exe-suffixed image name itself has never been confirmed against a real running process. - End-to-end: trigger a malformed-schema
fields-parse failure (the original nodespace-types::SchemaNode::from_node silently swallows a schema field parse failure to an empty Vec #2107/nodespace-types: surface SchemaNode field-parse failures via eprintln (closes #2107) #2128 diagnostic) and confirm the message actually lands innodespaced-error.log. - New (surfaced during desktop-app: Windows kill_running_daemon targets edition-correct binary name #2140's adversarial review): confirm the installed Windows daemon binary (
~/.nodespace/bin/<daemon_binary_name()>, extracted byextract_sidecar_if_changed) actually carries a.exeextension on disk —extract_sidecar_if_changed/ensure_daemon_runningconstruct the install path asbin_dir.join(daemon_binary_name())with nocfg(windows)extension handling, so if the extracted file is extension-less, neitherspawn_daemon_windows'sCommand::new(daemon_bin)norkill_running_daemon'staskkill /IM <name>.exewould work as intended. Also check for a possible mismatch betweenresolve_sidecar_path's Windows resource lookup (binaries/{name}-{triple}, no.exe) and what CI actually names the built Windows sidecar artifact (.github/workflows/release.ymlproducesbinaries/nodespaced-{triple}.exe, with.exe) — if the lookup doesn't find the resource,extract_sidecar_if_changedwould fail before the daemon is ever installed at all, which would be a more severe, earlier-stage bug than anything tracked above.
2. Fix the desktop-app GUI process's own console-less stderr
Separate, unaddressed mechanism: packages/desktop-app/src-tauri/src/commands/schemas.rs's two direct SchemaNode::from_node calls (get_all_schemas/get_schema_definition) run inside the Tauri GUI process itself, which builds with #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] in main.rs — a release build has no console at all, so any eprintln! there is discarded regardless of PR #2136's daemon-side fix. This isn't a child-process stdio problem (nothing to redirect via Stdio); it's the GUI process's own stderr handle.
Candidate approaches (need Windows-side design + validation, not just code):
- OS-level
SetStdHandle(STD_ERROR_HANDLE, ...)redirect to a log file at GUI process startup (Windows FFI inmain.rs) — meaningfully higher blast radius than the daemon fix since it touches app startup for the whole GUI process; needs runtime verification that it doesn't interfere with WebView2/Tauri's own expectations. - Restructure
nodespace_types::SchemaNode::from_nodeto return the fields-parse diagnostic in itsResult/output instead of printing internally, so callers can route it through whatever logging mechanism is appropriate for their process — rejected by PR nodespace-types: surface SchemaNode field-parse failures via eprintln (closes #2107) #2128's own rationale (chosen specifically to avoid touching call-site signatures across three callers uniformly), so this would need to revisit that decision explicitly if pursued.
Why Blocked
Both halves require either a real Windows machine/VM or at minimum cross/cargo-xwin + a full Windows SDK to get real compile+run verification — neither is available in this sandboxed macOS environment. Tracking as Blocked on the project board until Windows access is available (see MAC HANDOVER QUEUE precedent for the equivalent macOS-GUI-blocked pattern).
Activity
mstomar125 commentedon Sep 14, 2026
Starting work on this issue. Baseline: worktree created from main @ 6fdd9f7, bun install clean, full test suite green (241 files / 4955 tests passed). Confirmed already assigned to mstomar125 and board status already 'In Progress'. Plan: (1) investigate the sidecar .exe-extension question first per issue priority, (2) real Windows validation of PR #2136/#2140's daemon stderr fix using the standalone-harness technique, (3) design + implement + Windows-validate a fix for the GUI process's own console-less stderr in schemas.rs.
mstomar125 commentedon Sep 14, 2026
Resolved in #2614 — full real-Windows verification for both parts, using SSH access to a real Windows box (no libclang/LLVM there, so the full
nodespace-app/nodespacedbinaries still can't build; verified via the same standalone-harness technique used for #1452/#1498/#1519, extending it here to vendor the realnodespace-typescrate and faithfully re-implement the specificdaemon_setup.rs/main.rsfunctions under test).Sidecar
.exe-extension question (checked first, per the issue) — real bug found and fixed.extract_sidecar_if_changed's install destination wasbin_dir.join(name)with the baredaemon_binary_name(), never routed throughbundled_sidecar_name(which correctly appends.exeon Windows for the source-lookup half of the same function) — so the daemon binary extracted to~/.nodespace/bin/on Windows carried no.exeextension. Confirmed live:dirshows a file literally namednodespaced. This is not the CI/release-pipeline mismatch the issue speculated about (release.ymland Tauri's bundler are both correct); it's local to this repo's own extraction code, so it's fixed directly rather than filed separately.Real-Windows proof of the practical impact — the daemon still starts (
Command::newresolves an extensionless literal path fine), butkill_running_daemon's restart-timetaskkill /F /IM nodespaced.exesilently never matches it:A control run with the extension present isolates the root cause — same taskkill command matches correctly once the destination naming is fixed. Same result confirmed for the Pro edition name. Fixed via a new
sidecar_install_path()helper used as the single source of truth by both the install destination and the daemon-bin/taskkill path, with two new regression tests.Rest of the Part-1 checklist — confirmed working as-is on real hardware: log file creation/append under
~/.nodespace/logs/, file-handle inheritance into aDETACHED_PROCESS | CREATE_NEW_PROCESS_GROUPchild, append-not-truncate across a simulated restart, and the realSchemaNode::from_nodefields-parse diagnostic landing correctly innodespaced-error.logend-to-end.Part 2 (GUI process's own console-less stderr): implemented
SetStdHandle(STD_ERROR_HANDLE/STD_OUTPUT_HANDLE, <log file>)at GUI startup inmain.rs, gated to release-on-Windows. Reconsidered theSchemaNode::from_nodeResult-restructuring alternative and kept #2128's original rejection — it has a third call site reachable from every non-GUI entry point, none of which have this bug, so threading a diagnostic through that shared API is a much bigger blast radius than a self-containedmain.rs/daemon_setup.rsfix.The genuinely uncertain part — whether
SetStdHandleactually affects Rust's owneprintln!/io::stderr()machinery, or whether Rust caches a stale "no console" state from before the call — was verified for real with a standalonewindows_subsystem = "windows"harness launched viacmd /c start /WAIT(genuinely no inherited stdio, unlike a direct SSH invocation which inherits sshd's own pipes and doesn't reproduce the bug): output before the redirect is confirmed lost, output after it — including the realSchemaNode::from_nodediagnostic on a genuinely malformed schema — lands correctly in the log file.Full evidence, code, and the honest statement of the remaining verification gap (the real
nodespace-app/nodespacedbinaries themselves still can't build on this Windows box) are in the PR description.🤖 Generated with Claude Code