.maestro/playbooks/2026-03-29-Issues-Triage-3-29-2026/Phase-03-Windows-Platform-Hardening.md
This phase addresses the second-largest issue cluster: 12 Windows-specific bugs covering hook hangs, zombie ports/sockets, PowerShell quoting failures, CRLF shebang breakage, and process cleanup issues. Windows users represent a significant user segment, and these bugs make claude-mem essentially unusable on Windows for many users. The fixes are mostly platform-conditional code paths that need proper timeout handling, escaping, and process lifecycle management.
Fix Windows hook timeout and hang issues. Hooks can hang indefinitely on Windows because AbortSignal.timeout() crashes Bun with a libuv assertion and Promise.race fallbacks sometimes leak timers. To fix:
src/shared/worker-utils.ts to find all HTTP request functions (fetchWithTimeout, workerHttpRequest, etc.) and how they handle timeouts on Windowssrc/shared/hook-constants.ts for WINDOWS_MULTIPLIER and timeout valuesplugin/scripts/bun-runner.js (lines 124-149) for the stdin buffering timeout logicfetch() call in hook code paths for proper timeout handling. Ensure ALL use Promise.race with setTimeout (not AbortSignal.timeout) on Windowsbun-runner.js: if the child Bun process hasn't exited within 30 seconds (configurable), kill it and exit with code 0 to prevent terminal tab accumulationsetTimeout references are cleaned up via clearTimeout on success pathsFix Windows zombie port cleanup timing. The worker's server shutdown leaves ports bound for several seconds on Windows due to TIME_WAIT, causing "EADDRINUSE" on restart. To fix:
src/services/server/Server.ts for the shutdown sequence and existing Windows delay logic (around lines 115-127, currently 500ms + 500ms = 1000ms total)src/services/infrastructure/GracefulShutdown.ts for the full shutdown flowsrc/services/infrastructure/ProcessManager.ts waitForPortFree() functionwaitForPortFree(), increase the Windows timeout from 3 seconds to 6 seconds (use the existing getPlatformTimeout() with 2.0x multiplier)Server.ts listen(): if EADDRINUSE on Windows, wait 2 seconds and retry up to 3 times before failing Fix PowerShell quoting and escaping in process spawning. Windows daemon spawning uses PowerShell Start-Process which has different quoting rules than Unix shells. To fix:
src/services/infrastructure/ProcessManager.ts lines 624-700 for the spawnDaemon() Windows implementationGet-CimInstance process enumeration code (around lines 185-212)execAsync() and spawnSync() calls that construct PowerShell commands:
\\\\ escaping for JSON serialization, but NOT for PowerShell direct invocation$HOME) must be escaped or wrapped in single quotes to prevent PowerShell variable expansionsrc/services/integrations/CursorHooksInstaller.ts lines 322-333 for hook command generation — the escapedBunPath.replace(/\\/g, '\\\\') pattern is correct for JSON but verify it doesn't double-escape when read backescapeForPowerShell(path: string) utility in src/utils/ if one doesn't exist, and use it consistently Fix CRLF shebang issues for Windows Git checkouts. When Git on Windows checks out files with autocrlf=true, shebangs get \r\n line endings which break script execution. To fix:
.gitattributes file exists at the repo root. If not, create one*.sh text eol=lf
*.js text eol=lf
plugin/scripts/*.js text eol=lf
plugin/scripts/*.cjs text eol=lf
install/public/*.sh text eol=lf
plugin/scripts/bun-runner.js line 1 — if it has a shebang, verify it uses LFplugin/hooks/hooks.json — JSON files should also use LF to prevent parsing issues with \r in string values Fix Windows process enumeration edge cases in ProcessManager.ts:
src/services/infrastructure/ProcessManager.ts for getChildProcesses() (around line 185) and cleanupOrphanedProcesses() (around line 314)Get-CimInstance Win32_Process -Filter with WQL LIKE clauses. Edge cases to handle:
wmic process if Get-CimInstance returns no resultsGet-CimInstance can fail with "Access denied" for system processes — wrap in try-catch at the PowerShell leveltaskkill /PID /T /F command can fail silently if the process tree has already exited — check exit code and suppress the errorisProcessAlive(pid: number) utility that works cross-platform: process.kill(pid, 0) on Unix, tasklist /FI "PID eq ${pid}" on WindowsWrite tests for Windows-specific fixes:
*.test.ts)escapeForPowerShell(): paths with spaces, dollar signs, backslashes, Unicode charactersEADDRINUSE error, verify retry with delay, verify success after port freedRun build and verify:
npm run build-and-sync.gitattributes is properly committed$ in PowerShell strings, missing timeout fallbacks)