From 8edd6cfafd710567ea5ac3c57c90a615fc50f8e6 Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Mon, 25 May 2026 20:36:54 -0500 Subject: [PATCH] docs(claude): document cross-platform validation (macOS host, Linux via Docker, Windows VM) Rename the Windows section to "Cross-platform validation": macOS is the dev machine / default npm test target; on Linux use Docker (node:22-bookworm, `docker run --rm --init` so process-lifecycle tests don't false-fail on un-reaped zombies; count inotify via /proc//fdinfo); Windows VM kept. Also record the pre-existing mcp-initialize/mcp-roots EPERM failures on Windows. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a1131bf..8c73cc7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -170,7 +170,19 @@ Tests create temp dirs with `fs.mkdtempSync` and clean up in `afterEach`. They w Behavior that differs by platform (path resolution, drive letters, `SENSITIVE_PATHS`, `%APPDATA%` config dirs, CRLF) must be gated, not assumed. Use `it.runIf(process.platform === 'win32')(...)` for Windows-only assertions and `it.runIf(process.platform !== 'win32')(...)` for POSIX-only ones — e.g. `/etc` is sensitive on POSIX but resolves to `C:\etc` (non-existent) on Windows, so an ungated `/etc` assertion fails on Windows. Validate the Windows side for real (see below); don't merge a Windows-gated test you haven't seen run. -## Windows validation (Parallels + SSH) +## Cross-platform validation + +The dev machine — and the default `npm test` target — is **macOS**, so local runs cover the macOS path. The other two platforms aren't here; when a change is platform-sensitive (file watching, sockets / named pipes, path & symlink handling, process lifecycle, inotify budget) validate them for real rather than guessing. + +### Linux (Docker) + +When asked to test or validate on Linux, use **Docker** — there's no Linux box, but Docker runs on the macOS host. Build a throwaway image from the repo and run the suite inside it: + +- `FROM node:22-bookworm`; `COPY` the repo with a `.dockerignore` excluding `node_modules`/`dist`/`.git`/`.codegraph`; `RUN npm ci && npm run build`. Don't reuse the Mac `node_modules` — `esbuild`/`rollup` ship platform-specific binaries. +- Run with **`docker run --rm --init`**. The `--init` is load-bearing for any process-lifecycle test (daemon reaping, the #277 PPID watchdog, idle-timeout): without a zombie-reaping PID 1, a SIGKILL'd/exited process lingers as a zombie and `process.kill(pid, 0)` still reports it *alive*, so exit-detection assertions false-fail even though the process did exit. +- Linux is where the inotify watch budget actually bites: count a process's watches via `/proc//fdinfo/*` (sum `^inotify ` lines on the fd whose `readlink` is `anon_inode:inotify`). + +### Windows (Parallels VM + SSH) For any Windows-specific PR, bug, or implementation, validate it on the real Windows VM rather than guessing. Connection details live in the gitignored **`.parallels`** file at the repo root (VM name, guest IP, SSH user/key). `prlctl exec` needs Parallels Pro and is unavailable, so SSH is the bridge. @@ -184,7 +196,7 @@ For any Windows-specific PR, bug, or implementation, validate it on the real Win - Clone fresh into a **Windows-local** path (`C:\dev\codegraph`) and `npm ci` there — never run npm against the shared Mac repo, since `esbuild`/`rollup` ship platform-specific binaries. - Guest toolchain (winget): Node LTS, Git, and the **VC++ ARM64 redistributable** (required by `@rollup/rollup-win32-arm64-msvc`, which vitest pulls in). - Fetch a contributor PR head straight from their fork to dodge `pull//head` lag: `git fetch ` then `git checkout -f FETCH_HEAD`. -- Known pre-existing Windows failure: `security.test.ts > Session marker symlink resistance > does not follow a pre-planted symlink` (symlink creation needs privileges on Windows). Unrelated to current work; don't let it mask new regressions. +- Known pre-existing Windows failures (they reproduce on `main`, unrelated to your change — confirm against `origin/main` before blaming your PR, and don't let them mask new regressions): `security.test.ts > Session marker symlink resistance > does not follow a pre-planted symlink` (symlink creation needs privileges on Windows); and the `mcp-initialize.test.ts` / `mcp-roots.test.ts` suites, which fail in `afterEach` with `EPERM` removing the temp dir because a spawned `serve --mcp` (its `--liftoff-only` re-exec grandchild) still holds the cwd / SQLite file open — a Windows file-locking quirk, not a logic bug. ## Releases