codegraph index told you to run `codegraph daemon stop`, which the CLI
rejects ("too many arguments"), whenever it could not verify the process
named in .codegraph/daemon.pid. In that state `codegraph daemon` lists
nothing, because it lists only daemons that answer their socket. And
`codegraph unlock` kept an old-format lock whose process was alive (the
rule from issue #1834), so nothing the CLI offered got past it. The
writer-lock message gave the same wrong advice.
Root cause: a live pid in the lock was the only evidence, and a pid
proves nothing once the OS gives it to another program. Every CodeGraph
daemon since the first one (v0.9.5) runs
`<runtime> ... codegraph.js serve --mcp --path <root>`, so the holder's
command line settles the question.
Fix:
- New src/mcp/process-identity.ts reads a process's command line and
executable name from the OS: Win32_Process through PowerShell (tasklist
as fallback) on Windows, /proc on Linux, ps elsewhere. Each lookup is
capped at 5 s and never throws. A holder is a CodeGraph MCP server,
provably something else, or unknown, and unknown is never evidence.
- stopDaemonAt treats an unverified live holder that is provably not a
CodeGraph server like a dead one. Its lock is cleared, under the same
writer-slot claim, and the process is never signalled.
clearStaleDaemonArtifacts (codegraph unlock) does the same for an
old-format lock. A holder that may be a daemon keeps its lock, as
before (issues #1553 and #1834).
- daemonStopFailureMessage builds index's error from what remains: the
pid, what it runs, and a command that clears it (taskkill /PID N /F or
kill N), or which lock file to delete when only that will do.
- writerLockHeldMessage names `codegraph daemon` instead of the missing
`codegraph daemon stop`.
Validation (Windows 11):
- New daemon-lock-holder.test.ts (11 tests) and a daemon-registry test.
With main's daemon-registry.ts, writer-lock.ts and codegraph.ts put
back, 11 tests fail, including all 8 CLI cases and the writer-lock
wording; with the fix all pass. Four existing tests used generic
processes to stand in for a daemon that doesn't answer. Those stand-ins
now carry a CodeGraph server's command line, and pass on main and here.
- Real CLI with a private HOME, 4 cases: an old-format lock naming a live
PING.EXE; an old-format lock naming a live Node process that isn't
CodeGraph; a current-format lock naming that kind of Node process with
a pipe nobody listens on; an old-format lock naming a Node process whose
command line reads `codegraph.js serve --mcp`. On main all 4 index runs
exit 1 with the old advice, `daemon stop` is rejected, `daemon` lists
nothing, and unlock clears only the third. With the fix the first 3
rebuild (exit 0), each holder stays alive, and unlock clears the first
too. The 4th exits 1, naming the pid and `taskkill /PID <pid> /F`;
after that command, index exits 0.
Contributor PR #2373 (@danusha2345) changes the two strings only; I read
it for guidance.
Fixes#2770
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: safely stop daemon before reindex
* fix(index): coordinate rebuilds with running MCP daemons (#1325)
Stop verified project daemons and confirm termination before recreating SQLite.
Fence daemon and proxy reopening throughout rebuilds while preserving foreign and newer locks.
Add Windows lifecycle coverage and cross-platform shutdown regressions.
Co-authored-by: Harry B ✦ <drewavesx0@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(mcp): fence project opens during index rebuilds after rebase
Carry the #1325 rebuild fence through the upstream explicit-project lifecycle, read-only engine opens, synchronous initialization retries, and direct writer acquisition. Preserve shared project leases, read-only fallback behavior, freshness handling, and direct-mode query pools.
Cover watched, unwatched, and read-only engines blocking during a foreign rebuild and recovering after the fence is released.
* fix(mcp): answer a running index rebuild as guidance, not a tool error (#1325)
A projectPath call during `codegraph index` hit assertNoRebuild's plain Error
and returned isError; it is an expected, temporary condition, so it now uses a
RebuildInProgressError that the tool handler answers success-shaped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Harry B ✦ <drewavesx0@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Follow-up to #1065/#1066. Those stopped a *new* index from scanning an
ignored gitlink corpus, but a project that had already built the multi-GB
graph before upgrading still couldn't recover: `codegraph index` printed
only "Indexing project" and was then SIGKILLed (137) by the #850 watchdog
~60s later, before scanning even started.
Root cause is not the scanner. `index` cleared the old graph with a
synchronous `DELETE FROM nodes/edges/files`. `nodes` carries an FTS5
`AFTER DELETE` trigger, so deleting ~1.6M rows fires ~1.6M FTS
delete-markers — O(rows), and it grows the WAL further before it can
finish. A deterministic probe puts the DELETE-clear at 20.4s on 1.5M
synthetic nodes (WAL 1.16->2.14GB); at the report's denser ~2.6KB/node WAL
that crosses the 60s main-thread watchdog. `open()` was never the wedge.
A full re-index is documented as "same result as a fresh init", so make it
one: discard the database files and re-initialize, instead of opening the
old DB and DELETE-ing every row.
- db: add removeDatabaseFiles(dbPath) — unlinks codegraph.db + its
-wal/-shm sidecars (O(1) regardless of size; sidecars best-effort).
- index: add CodeGraph.recreate(projectRoot) — discards the files and
returns a fresh, empty instance. Never opens or migrates the poisoned
DB. POSIX unlinks an open file fine (a live daemon heals via
reopenIfReplaced, #925); a Windows file lock becomes an actionable
"stop the daemon / remove .codegraph" error.
- cli: `codegraph index` now calls recreate() instead of open()+clear();
both clear() calls dropped. The public clear() API is unchanged.
This also reclaims the disk the bloated db/-wal were holding.
Validated: deterministic probe (DELETE O(rows) vs recreate O(1)); an
end-to-end run through the built binary recovering a real 800K-node /
419MB poisoned DB in 0.3s with no wedge and the correct small graph; new
unit + CLI regression tests; existing #874 index tests still green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`codegraph index` ran extraction against the already-populated DB without
clearing it first. On an unchanged tree every file's content hash still
matched, so the orchestrator skipped re-inserting all of them and the run
reported its delta (after - before = 0) as "0 nodes, 0 edges" — which read as
if `index` had wiped the graph. `init` only ever differed because it runs on a
freshly created, empty DB.
Clear the existing graph before re-indexing so `index` rebuilds from scratch
and reports the same complete result as a fresh `init`. `--force` keeps its
role as the home-dir/root-path override; `sync` stays the incremental path.
Adds an end-to-end regression test driving the built binary (init -> index),
asserting the graph stays populated and the summary is never "0 nodes, 0 edges".
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>