Summary
On Windows, LIFEOS/TOOLS/Inference.ts cannot spawn the Claude CLI at all. Every inference
call dies before reaching the model, so the memory reviewer has never completed a single
successful run on a fresh Windows install.
Environment
- Windows 11, LifeOS 7.40.4
bun 1.4.0, Node 22.9
- Claude Code CLI installed via
npm i -g @anthropic-ai/claude-code (2.1.258)
What happens
resolveClaudeBin() returns the path from Bun.which("claude"), which on Windows is npm's
shim claude.cmd. That path is handed to spawn imported from node:child_process,
which refuses to execute a .cmd without a shell and throws:
error: spawn C:\Users\<user>\AppData\Roaming\npm\claude.cmd EINVAL
syscall: "spawn"
errno: -4071
code: "EINVAL"
Node tightened this in the CVE-2024-27980 fix: .cmd and .bat require shell: true.
Observed downstream effect
MemoryHealthCheck reports critical with reviewer-latest-timed-out and
priorSuccesses: 0. The reviewer run directory contains prompt.system.md,
prompt.user.md and transcript.txt but no response.raw.txt, because inference returns
before writing one. The failure is silent from the user's perspective: the reviewer simply
never produces memory.
Worth noting Bun.spawn handles .cmd correctly; it is specifically the node:child_process
import that fails. So the bug is invisible to anyone testing the spawn path under Bun's own API.
Suggested fix
Use shell: true on win32 when the resolved binary is a .cmd/.bat. cmd.exe does not
auto-quote, so arguments containing spaces and empty-string arguments must be quoted, or
the deliberate --tools '' and --setting-sources '' arguments silently vanish and change
the meaning of the invocation.
const claudeBin = resolveClaudeBin();
const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/i.test(claudeBin);
const q = (a: string): string => (a === '' || /\s/.test(a) ? `"${a}"` : a);
const proc = spawn(
needsShell ? q(claudeBin) : claudeBin,
needsShell ? args.map(q) : args,
{ env, stdio: ['pipe', 'pipe', 'pipe'], ...(needsShell ? { shell: true, windowsHide: true } : {}) },
);
Relationship to #2003
Fixing this uncovers #2003 as the next failure: once the spawn succeeds, cmd.exe's ~8191
character command-line limit rejects the reviewer's ~9 KB system prompt. The two are
sequential, not alternatives. Both must be fixed for the memory loop to work on Windows.
Verified locally: after applying the patch above plus the #2003 threshold change, a reviewer
run returns ok: true, parse_ok: true, with items dispatched and zero failures.
Summary
On Windows,
LIFEOS/TOOLS/Inference.tscannot spawn the Claude CLI at all. Every inferencecall dies before reaching the model, so the memory reviewer has never completed a single
successful run on a fresh Windows install.
Environment
bun1.4.0, Node 22.9npm i -g @anthropic-ai/claude-code(2.1.258)What happens
resolveClaudeBin()returns the path fromBun.which("claude"), which on Windows is npm'sshim
claude.cmd. That path is handed tospawnimported fromnode:child_process,which refuses to execute a
.cmdwithout a shell and throws:Node tightened this in the CVE-2024-27980 fix:
.cmdand.batrequireshell: true.Observed downstream effect
MemoryHealthCheckreportscriticalwithreviewer-latest-timed-outandpriorSuccesses: 0. The reviewer run directory containsprompt.system.md,prompt.user.mdandtranscript.txtbut noresponse.raw.txt, because inference returnsbefore writing one. The failure is silent from the user's perspective: the reviewer simply
never produces memory.
Worth noting
Bun.spawnhandles.cmdcorrectly; it is specifically thenode:child_processimport that fails. So the bug is invisible to anyone testing the spawn path under Bun's own API.
Suggested fix
Use
shell: trueon win32 when the resolved binary is a.cmd/.bat. cmd.exe does notauto-quote, so arguments containing spaces and empty-string arguments must be quoted, or
the deliberate
--tools ''and--setting-sources ''arguments silently vanish and changethe meaning of the invocation.
Relationship to #2003
Fixing this uncovers #2003 as the next failure: once the spawn succeeds, cmd.exe's ~8191
character command-line limit rejects the reviewer's ~9 KB system prompt. The two are
sequential, not alternatives. Both must be fixed for the memory loop to work on Windows.
Verified locally: after applying the patch above plus the #2003 threshold change, a reviewer
run returns
ok: true,parse_ok: true, with items dispatched and zero failures.