On Windows, bun run test fails 24 tests across 7 files. Every one of them fails after its assertions have passed, in fs.rmSync on the temp project:
× Resolution Module > PHP Include Resolution > resolves require_once to a file→file imports edge (#660)
→ EPERM, Permission denied: \?\C:\Users\…\Temp\codegraph-php-e2e-zmZ4mt
❯ __tests__/resolution.test.ts:3792 fs.rmSync(tempProject, { recursive: true, force: true });
Windows refuses to remove a directory that holds an open file; POSIX unlinks one happily, which is why CI does not see this. The tests are not wrong about the graph — they are wrong about the cleanup.
Two causes
1. The graph is never closed. __tests__/arkts-resolution.test.ts calls CodeGraph.initSync 7 times and close() zero times; frameworks-integration.test.ts closes in two JVM tests and not in the other three; resolution.test.ts's C++/PHP end-to-end tests additionally open a second DatabaseConnection for a raw edge query and never close that either. The SQLite handle is still open when rmSync runs. 14 tests.
2. A killed child still holds its handles. The four mcp-* suites do close the graph and do child.kill('SIGKILL') — then call rmSync synchronously in the same tick. A signalled process has not exited yet. 10 tests.
Measured, both arms, same file and command back to back: adding cg.close() to arkts-resolution.test.ts's ohpm main entry test makes it pass; removing that one line makes it fail with EPERM again.
Affected
| Tests |
File |
Cause |
| 7 |
arkts-resolution.test.ts |
unclosed graph |
| 4 |
resolution.test.ts (C++/PHP e2e) |
unclosed graph + unclosed DatabaseConnection |
| 3 |
frameworks-integration.test.ts (JVM FQN imports) |
unclosed graph |
| 3 |
mcp-initialize.test.ts |
killed child not awaited |
| 3 |
mcp-roots.test.ts |
killed child not awaited |
| 3 |
mcp-subproject-adoption.test.ts |
killed child not awaited |
| 1 |
mcp-daemon.test.ts |
killed child not awaited (fixed 50ms grace period) |
Awaiting the tracked child is necessary but not sufficient for the MCP suites: with only that, a full-suite run still failed 2 of 4,240 — a different pair each time — because the server can leave a detached daemon the suite never tracks, and mcp-daemon's concurrent-launcher test deliberately leaves one alive.
Environment
Windows 11, Node 22, vitest run from a built tree (tsc + assets + ui workspace). Before: 3,975 passing / 24 failing / 234 skipped. Not reproduced on Linux — the guess is that nobody outside a Windows host ever sees it, and the failure is pure teardown, so no behaviour of the indexer is implicated.
PR follows.
On Windows,
bun run testfails 24 tests across 7 files. Every one of them fails after its assertions have passed, infs.rmSyncon the temp project:Windows refuses to remove a directory that holds an open file; POSIX unlinks one happily, which is why CI does not see this. The tests are not wrong about the graph — they are wrong about the cleanup.
Two causes
1. The graph is never closed.
__tests__/arkts-resolution.test.tscallsCodeGraph.initSync7 times andclose()zero times;frameworks-integration.test.tscloses in two JVM tests and not in the other three;resolution.test.ts's C++/PHP end-to-end tests additionally open a secondDatabaseConnectionfor a raw edge query and never close that either. The SQLite handle is still open whenrmSyncruns. 14 tests.2. A killed child still holds its handles. The four
mcp-*suites do close the graph and dochild.kill('SIGKILL')— then callrmSyncsynchronously in the same tick. A signalled process has not exited yet. 10 tests.Measured, both arms, same file and command back to back: adding
cg.close()toarkts-resolution.test.ts'sohpm main entrytest makes it pass; removing that one line makes it fail with EPERM again.Affected
arkts-resolution.test.tsresolution.test.ts(C++/PHP e2e)DatabaseConnectionframeworks-integration.test.ts(JVM FQN imports)mcp-initialize.test.tsmcp-roots.test.tsmcp-subproject-adoption.test.tsmcp-daemon.test.tsAwaiting the tracked child is necessary but not sufficient for the MCP suites: with only that, a full-suite run still failed 2 of 4,240 — a different pair each time — because the server can leave a detached daemon the suite never tracks, and
mcp-daemon's concurrent-launcher test deliberately leaves one alive.Environment
Windows 11, Node 22,
vitest runfrom a built tree (tsc+ assets +uiworkspace). Before: 3,975 passing / 24 failing / 234 skipped. Not reproduced on Linux — the guess is that nobody outside a Windows host ever sees it, and the failure is pure teardown, so no behaviour of the indexer is implicated.PR follows.