feat(daemon): cross-platform Windows support via named-pipe IPC#15
Merged
Conversation
…or change) Replace the Unix-only UnixListener/UnixStream with a sync interprocess local_socket abstraction (transport.rs): nonblocking-accept + poll loop instead of the self-connect poke, Stream::split() for the reader/writer halves, rustix for getppid + signal-0 liveness, and a write-then-publish lock that never deletes an empty in-flight pidfile. Public API unchanged; Unix behavior byte-identical (golden equivalence + daemon lifecycle green). Bumps MSRV declaration 1.70 -> 1.75 (interprocess 2.x requirement; toolchain is stable).
…load Add x86_64-pc-windows-msvc (native build on windows-latest) to the release matrix, package as .zip via Compress-Archive, and extend the upload-assets glob to dist/*.tar.gz + dist/*.zip so the Windows asset is not silently dropped.
Add Windows to the prebuilt install matrix, rewrite the stale Windows-unsupported / daemon-does-not-work paragraphs (daemon now works via named pipes on Windows), note the self-update .zip caveat, bump MSRV 1.70 -> 1.75 in both READMEs, and add docs/design/windows-daemon.md.
Fill the Windows arms of the cross-platform daemon: bare `codegraph-<hash>` namespaced pipe name (interprocess GenericNamespaced), current_ppid()->0 + OpenProcess/GetExitCodeProcess liveness via windows-sys, and a cfg(windows)-skipped ppid branch in supervision_lost_reason so a Some(parent_pid) never self-kills the daemon. Unix stays byte-identical (golden equivalence + daemon lifecycle green); Windows runtime is gated on the windows-latest CI job.
…cking) Add a windows-latest job (clippy + test --workspace) and a linux `cargo check -p codegraph-daemon --target x86_64-pc-windows-msvc` cross-check; wire windows into the ci-success gate (needs + failure check) so a red Windows run blocks the PR via the required CI Success status check.
Add parallel-safe regression tests pinning the C1 behavior changes: lock contention yields exactly one winner, stop() terminates the accept loop and clears the pidfile, dead-pid locks recover, and an empty in-flight pidfile is never deleted by a reader (audit BLOCKER #6). temp_project uses a process-global AtomicU64 counter so parallel tests never collide on a coarse-clock nanos value.
codegraph-bench is a publish=false dev/benchmark harness using Unix-only APIs (posix_fadvise, process_group, AsRawFd). The windows-latest clippy/test --workspace run tried to compile it and failed. Exclude it on Windows only; the Linux test job keeps --workspace so bench + golden equivalence stay covered. The release windows build is unaffected (cargo build -p codegraph-rs skips dev-deps).
The `file` param is only read inside the #[cfg(unix)] set_mode block; on Windows that block compiles out, so -D warnings tripped unused-variables. Add #[cfg_attr(not(unix), allow(unused_variables))] (chmod is a no-op on Windows). Surfaced by the new windows-latest CI job.
codegraph-bench is a dev-dependency of codegraph-cli, so --all-targets pulls it into the Windows build graph (--exclude can't prevent that). cfg-gate the Unix-only bits: process_group/kill_process_group (metrics) and posix_fadvise/AsRawFd page-cache eviction (runner); on Windows these are portable no-ops (child.kill() for cleanup, eviction skipped). Move libc to [target.'cfg(unix)'.dependencies]. Unix behavior + golden oracle byte-identical.
The node-id/content-hash invariant SHA-256s raw fixture bytes (include_str!). On Windows, git autocrlf checks out the LF fixtures as CRLF, changing the bytes -> hash mismatch -> golden byte-stability breaks (caught by the windows-latest CI job: content hash mismatch for src/app.ts). Force eol=lf for crates/codegraph-bench/fixtures, reference/golden, and text source types; mark the binary .db golden as binary. Renormalize is a no-op on Linux (already LF), so Unix hashes stay byte-identical while Windows now checks out LF too.
…n Windows sharing violation
The codegraph-mcp golden test callees_matches_golden panicked on
windows-latest CI with Os{code:32} ERROR_SHARING_VIOLATION at the
fs::copy of the golden DB into a temp .codegraph/codegraph.db. Root cause
is a test-harness race, not a logic or golden-data bug:
- setup_mini_project() and index_fixture() keyed their temp dir on
process::id() + SystemTime nanos. Windows' coarse SystemTime lets
parallel test threads collide on identical nanos, so two tests share
one temp .codegraph/codegraph.db and one test's open SQLite handle
races the other's copy/remove (mandatory file locking on Windows).
Fix (test-harness only; no extraction/resolution/store/node-id/golden
change):
- Add a process-wide AtomicU64 TEMP_SEQ and append it to both temp dir
names, the same collision-proof pattern codegraph-daemon's
regressions.rs already uses. A colliding path is now impossible
regardless of clock granularity.
- Wrap the golden-db and fixture copies in copy_with_retry(), which
retries only on raw OS error 32 with a short sleep and otherwise
panics with the original per-call-site context. Error 32 never occurs
on Unix, so the first fs::copy always succeeds there and Unix behavior
is byte-identical.
Also harden .gitattributes: the binary *.db / *.db-shm / *.db-wal /
*.sqlite / *.sqlite3 lines now carry '-text !eol' so the catch-all
'* text=auto eol=lf' can never leave a dangling eol=lf on a binary
SQLite DB. The golden .db bytes are unchanged.
tildify() stripped the home prefix with a hardcoded forward slash
(strip_prefix(&format!("{home}/"))) on the lossy path string. On
Windows path components use backslash separators, so the "{home}/"
literal never matched and the full absolute path was returned instead
of "~/foo.json".
Use the separator-agnostic Path::strip_prefix(&ctx.home), which matches
on path components, then normalize the displayed tail to POSIX-style
forward slashes so the rendered "~/..." is byte-identical on every
platform (upstream index.ts:437 emits POSIX-style ~/). On Unix there
are no backslashes to replace, so output is unchanged.
…sqlite3
The bench oracle and pipeline shelled out to the external `sqlite3` CLI
(`Command::new("sqlite3").arg(db).arg(".schema")`) to capture the schema
for canonicalization. The windows-latest CI runner has no `sqlite3` binary
on PATH (Linux/macOS runners ship it), so the spawn failed with io::Error
"program not found", panicking 6/7 tests in sync_incremental.rs on Windows.
Replace both `sqlite_schema` functions with an in-process rusqlite query
over sqlite_master (`SELECT name, type, sql ... ORDER BY rowid`), feeding
the result through the existing `normalize_schema`. The SQLite shell appends
a `/* name(cols) */` comment after each CREATE VIRTUAL TABLE; that comment
is reproduced from pragma_table_info so the normalized schema string is
byte-identical to the old CLI output and the committed golden equivalence
is preserved. rusqlite (bundled SQLite) is already a bench dependency, so no
new crate is added and the external runtime dependency is removed entirely.
…helling out to sqlite3
The schema_parity test shelled out to the external sqlite3 CLI via
Command::new("sqlite3").arg(db).arg(".schema"). The windows-latest CI
runner has no sqlite3 on PATH, so the spawn failed with NotFound
"program not found" and the test panicked on unwrap.
Replace the shell-out with an in-process rusqlite dump of sqlite_master
(SELECT name, type, sql ... ORDER BY rowid). To stay byte-identical to
the committed golden schema, reproduce the /* name(cols) */ column-list
comment that sqlite3 .schema appends after each CREATE VIRTUAL TABLE,
sourcing the columns from pragma_table_info. rusqlite is already a
dev-dependency; no new dependency is added and normalize_schema is
unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Make
codegraphbuild and run on Windows by giving the per-project daemon a cross-platform IPC transport (Unix domain socket on Unix, named pipe on Windows) backed by theinterprocesscrate — matching upstream colby's Windows support. Zero change to extraction / golden output.Ships as one PR (6 logical commits):
refactor(daemon)— abstract IPC transport overinterprocess(Unix byte-identical: nonblocking-accept + poll,Stream::split(), write-then-publish lock, rustix process introspection). MSRV declaration 1.70 → 1.75 (interprocess 2.4 requirement; toolchain is already stable).test(daemon)— parallel-safe regression tests: lock contention one-winner, shutdown-flag stops accept loop, stale-lock recovery, empty in-flight pidfile never deleted (audit BLOCKER fix(installer): never overwrite JSONC agent configs on parse failure #6).feat(daemon)— Windows named-pipe (GenericNamespaced) +windows-sysprocess liveness;supervision_lost_reasonskips the ppid branch on Windows (no startup self-kill).ci— windows-latest job (clippy + test) + daemon windows cross-check, wired intoCI Successas a merge blocker.build(release)—x86_64-pc-windows-msvctarget,.zippackaging,dist/*.zipadded to the upload-assets glob (was tar.gz-only).docs— Windows install matrix + caveats (EN + zh-CN), MSRV 1.75, design doc, upstream ledger.Verification
-D warnings, guardrail, golden equivalence byte-identical (4/4), golden_mcp 21/21, daemon regressions 10/10 parallel.Reviewer note — pre-existing flaky tests (NOT introduced here)
Two tests flake intermittently only under peak full-workspace parallel load; both exist on
mainand this PR makes zero changes to their crates:codegraph-mcp::golden_mcp::tools_list_matches_upstream_names_and_schemascodegraph-watch::sync::tests::unchanged_file_hash_is_not_reindexedThey pass cleanly serial / per-crate. If CI reds on either, re-run — it is unrelated to this change. (Tracked for a separate follow-up.)