Skip to content

fix(cli): never run daemon/catch-up when launched at $HOME or filesystem root#68

Merged
sunerpy merged 2 commits into
mainfrom
fix/daemon-home-root
Jun 25, 2026
Merged

fix(cli): never run daemon/catch-up when launched at $HOME or filesystem root#68
sunerpy merged 2 commits into
mainfrom
fix/daemon-home-root

Conversation

@sunerpy

@sunerpy sunerpy commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes two symptoms a user saw in ps (on v0.15.3): an IDE (Kiro) launching codegraph serve --mcp with CWD=$HOME spawned a daemon serve --mcp --path /config/. that pegged a CPU at 99%, indexing the entire home tree.

Root cause

cmd_serve defaults the project to the process CWD when no --path is given. Kiro launches the stdio server with the workspace's FIRST root folder as CWD, which for this user resolves to $HOME. With a $HOME/.codegraph present, the server entered SpawnOrProxy → spawned a daemon AND ran spawn_catch_upsync_project_once($HOME), indexing the whole home tree. v0.15.2's home guard only gated the WATCHER; the daemon spawn and catch-up were never guarded.

Fix

  • Promote the home/filesystem-root detection in codegraph-watch into a public too_broad_root_reason() + TooBroadRoot, the single source of truth (the watcher's home_or_too_broad_root_reason now delegates to the same classify_too_broad_root, keeping its wording → watcher behavior unchanged).
  • cmd_serve consults it BEFORE selecting a serve mode: when the resolved root is exactly $HOME or /, it serves tools off any existing index via serve_direct_no_services (NO daemon, NO watcher, NO catch-up) and logs one clear line. Never enters SpawnOrProxy/BeDaemon.
  • serve_direct gates spawn_catch_up behind should_run_daemon_services (the 99% CPU source).
  • A real project nested under $HOME (e.g. ~/projects/myapp) is UNAFFECTED — full daemon + catch-up. Only an EXACT $HOME/filesystem-root match is too broad; /config/. canonicalizes to equal $HOME.

Clarifying the "two daemons" question

Daemons are shared PER PROJECT ROOT (same root → same socket → shared). The two daemons in the user's ps differed only because Kiro used the wrong root ($HOME). With this fix a $HOME launch runs daemon-less; a real project still spawns ONE daemon shared by all its windows' proxies (verified: 1 daemon + 2 proxies).

Verification

  • cargo test -p codegraph-watch -p codegraph-rs: pass (3 new tests + watcher behavior unchanged)
  • cargo clippy --workspace --all-targets -- -D warnings: exit 0
  • cargo fmt --all --check: exit 0
  • bash scripts/guardrail.sh: exit 0
  • git status --porcelain reference/golden/: clean
  • E2E (real processes): CWD=$HOME → no daemon, no 99% CPU, disable log shown; real project → 1 shared daemon + 2 proxies.

Scope

No new EdgeKind/NodeKind, no new dependency, no hand version bump (Cargo.lock 0.15.2→0.15.3 sync), confined to codegraph-cli / codegraph-watch + docs.

Deferred follow-up (documented, NOT in this release)

The deeper "Kiro auto-discovers its real workspace" fix is deferred: Kiro does NOT push rootUri/workspaceFolders in initialize (confirmed) but DOES support the server-driven roots/list flow — implementing that (+ deferring daemon spawn until the workspace is resolved) is a larger transport change for its own release. Until then a $HOME-launched window runs daemon-less but safe; pin --path to opt into a shared per-project daemon.

Final Verification Wave: F1 (plan compliance), F2 (code quality), F3 (hands-on QA), F4 (scope fidelity) — all APPROVE.

sunerpy added 2 commits June 26, 2026 02:04
…tem root

An IDE (e.g. Kiro) launches 'serve --mcp' with no --path and CWD=$HOME, so the
project defaulted to the home directory: the server spawned a daemon AND ran a
catch-up sync that indexed the ENTIRE home tree, pegging a CPU at 99%
(serve --mcp --path /config/. observed). The v0.15.2 home guard only gated the
watcher; spawn_catch_up / sync_project_once were never guarded.

Promote the home/filesystem-root detection in codegraph-watch into a public
too_broad_root_reason() + TooBroadRoot, reused as the single source of truth by
both the watcher guard (wording unchanged) and a new CLI
should_run_daemon_services(). cmd_serve now, before selecting a serve mode,
refuses a too-broad root: it serves tools off any existing index via
serve_direct_no_services() but starts NO daemon, watcher, or catch-up, logging
one clear line. serve_direct also gates spawn_catch_up behind the same predicate.

Only an EXACT $HOME or filesystem-root match is too broad; a real project nested
under $HOME (e.g. ~/workspace/.../codegraph-rust) is unaffected and keeps its
daemon + catch-up. Canonicalization makes /config/. compare equal to $HOME.
Covered by policy tests (home/root flagged, nested project not, $HOME/. normalized)
and a CLI should_run_daemon_services test. Cargo.lock is the 0.15.2->0.15.3 sync.
Extend the existing home/filesystem-root note: when the resolved root is exactly
$HOME or /, the server now disables the daemon and catch-up sync in addition to
the watcher (an IDE launching serve --mcp from $HOME would otherwise index the
whole home tree and peg a CPU at 99%). Tools still answer off any existing index;
a real project nested under $HOME is unaffected; pin --path to get per-project
services.
@sunerpy sunerpy merged commit cf34500 into main Jun 25, 2026
4 checks passed
@sunerpy sunerpy deleted the fix/daemon-home-root branch June 25, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant