(Found by Claude — I asked it to investigate why background Codex jobs kept accumulating on my machine while working through #412; it traced this to a separate, unrelated bug and I thought it was worth reporting on its own.)
Summary
terminateProcessTree in scripts/lib/process.mjs — what /codex:cancel uses to kill background Codex jobs — invokes taskkill through runCommand, which on Windows always sets shell: process.env.SHELL || true. That was added so spawnSync could resolve .cmd shims like npm/npx, but it also applies to taskkill, a native .exe that doesn't need it. If SHELL points at a POSIX shell (e.g. Git Bash), that shell's MSYS path-conversion rewrites the leading-slash flags before taskkill ever sees them:
taskkill /PID 29576 /T /F
→ actually invoked as: taskkill "C:/Program Files/Git/PID" 29576 /T /F
→ ERROR: Argumento u opción no válido - "C:/Program Files/Git/PID"
So taskkill fails every time in that environment, silently, and the target process is never actually killed — /codex:cancel reports success while the job keeps running.
Fix: pass shell: false explicitly for the taskkill call in terminateProcessTree, since it's a native binary and doesn't need shell resolution. Verified end-to-end (spawned a real detached process, called terminateProcessTree, confirmed it was actually gone via Get-Process), and it also fixes the plugin's own cancel stops an active background job and marks it cancelled test, which was failing on this same argument-mangling issue before the change.
(Found by Claude — I asked it to investigate why background Codex jobs kept accumulating on my machine while working through #412; it traced this to a separate, unrelated bug and I thought it was worth reporting on its own.)
Summary
terminateProcessTreeinscripts/lib/process.mjs— what/codex:canceluses to kill background Codex jobs — invokestaskkillthroughrunCommand, which on Windows always setsshell: process.env.SHELL || true. That was added sospawnSynccould resolve.cmdshims likenpm/npx, but it also applies totaskkill, a native.exethat doesn't need it. IfSHELLpoints at a POSIX shell (e.g. Git Bash), that shell's MSYS path-conversion rewrites the leading-slash flags beforetaskkillever sees them:So
taskkillfails every time in that environment, silently, and the target process is never actually killed —/codex:cancelreports success while the job keeps running.Fix: pass
shell: falseexplicitly for thetaskkillcall interminateProcessTree, since it's a native binary and doesn't need shell resolution. Verified end-to-end (spawned a real detached process, calledterminateProcessTree, confirmed it was actually gone viaGet-Process), and it also fixes the plugin's owncancel stops an active background job and marks it cancelledtest, which was failing on this same argument-mangling issue before the change.