Ninth review round; each item reproduced before fixing.
The inner WSL install ran install.sh without /usr/lib/wsl/lib on PATH,
so its GPU detection (which checked PATH and /usr/bin only) could pick
CPU torch wheels on the exact Spark/N1X path this PR exists for, failing
the later torch.cuda probe. The forwarded env now appends
/usr/lib/wsl/lib to PATH (appended, so a PATH nvidia-smi still wins),
and install.sh's _has_usable_nvidia_gpu and torch-index _smi resolution
gained the same location fallback for direct WSL runs.
Three WSL failure paths in install.ps1 (WSL-not-installed deferral, the
download sentinel, and the final torch.cuda failure) set LASTEXITCODE
and returned, bypassing the round-eight Exit-InstallFailure fix, so
powershell -Command automation using the published pipe form still saw
success on those failures. All three now route through
Exit-InstallFailure, which restores the rolled-aside venv and fails the
process in every invocation mode.
The uninstall.ps1 WSL cleanup removed /root/.unsloth before killing and
matched only full argv, so cmake/nvcc children of a live CUDA build
(relative argv after the provisioner cds into the tree) survived the rm
and recreated files. The cleanup now signals each matched PID's whole
process group (guarded against the shell's own pgid, direct children
via pkill -P as fallback) before any rm; the /proc cmdline greps are
unaffected by kill order since they read process state, not files.
The round-eight same-group fallback called pkill -P without a guard;
under this script's set -e a matched provisioner with no children at
that instant (TERM pass already reaped them) aborted the whole
uninstaller before any cleanup. Now || true, like the kill beside it.
Reproduced in a dash sandbox with set -e: a childless matched PID
previously killed the harness, now dies cleanly while setsid-group and
same-group scenarios keep passing.
Verified: bash -n on both shell scripts, sh -n on the extracted WSL
clean snippet, PowerShell AST parse on both ps1 files, the three-
scenario kill sandbox, gpu-detection and installer-index pytest suites
pass, and the sh battery matches the branch baseline.