Skip to content

feat(daemon): auto-respawn via per-project watchdog (#116) - #118

Closed
ardelperal wants to merge 1 commit into
mainfrom
fix/daemon-auto-respawn
Closed

feat(daemon): auto-respawn via per-project watchdog (#116)#118
ardelperal wants to merge 1 commit into
mainfrom
fix/daemon-auto-respawn

Conversation

@ardelperal

Copy link
Copy Markdown
Owner

Summary

The shared MCP daemon auto-respawns when killed while the proxy is still up. After Stop-Process -Id (or any other death cause) the proxy per-project daemon watchdog detects the dead daemon via its .codegraph-vba/daemon.pid lockfile and respawns it within 30s — file edits are picked up automatically instead of needing a manual codegraph-vba sync.

What is in

  • src/mcp/daemon-watchdog.ts (new) — DaemonWatchdog class. Constructor takes an injectable spawnFn so tests can stub the detached spawn without ESM-fragile vi.mock("child_process") patches. hasLiveDaemon reads the lockfile via decodeLockInfo (the lockfile is the authoritative pointer; listDaemons filters dead entries from its return value, so it cannot be used to detect a dead daemon).
  • src/mcp/index.ts — MCPServer (proxy mode) instantiates a watchdog per project root, starts it, and tears it down on stop. Watchdog intervals are unrefed so they never keep the proxy alive.

Why a watchdog (vs. spawn-on-demand at MCP request time)

The issue repro is Stop-Process -Id while the MCP keeps serving — file edits are missed until the user manually runs codegraph-vba sync. The watchdog closes that gap in under 30s.

Acceptance

  • npx vitest run tests/daemon-watchdog.test.ts — 9/9 passed
  • npx vitest run tests/daemon-registry.test.ts — 6/6 (no regression)
  • npx tsc --noEmit clean
  • git diff main...HEAD -- src/db/schema.sql empty
  • Conventional commit, no Co-Authored-By
  • CHANGELOG entry under [Unreleased] → Fixes

Closes #116.

When the shared MCP daemon is killed (Stop-Process, kill -9, crash) while the proxy host is still running, the incremental file-watcher was lost until the user manually ran 'codegraph-vba sync'. This change adds a per-project daemon liveness watchdog inside the proxy that polls the daemon's .codegraph-vba/daemon.pid lockfile every 30s and respawns the daemon (via the same detached-spawn recipe the launcher uses) when the pid points at a dead process.

Architecture:

- src/mcp/daemon-watchdog.ts (new): DaemonWatchdog class. Constructor takes an injectable spawnFn so tests can stub the detached spawn without ESM-fragile vi.mock('child_process') patches. hasLiveDaemon(root) reads the lockfile via decodeLockInfo (the lockfile is the authoritative pointer; listDaemons filters dead entries from its return value, so it cannot be used to detect a dead daemon -- that was the trap in v1).

- src/mcp/index.ts: MCPServer (proxy mode) instantiates a watchdog per project root, starts it, and tears it down on stop(). Watchdog intervals are unref-ed so they never keep the proxy alive.

Why a watchdog (vs. spawn-on-demand at MCP request time): the issue's repro is Stop-Process -Id <pid> while the MCP keeps serving -- file edits are missed until the user manually runs 'codegraph-vba sync'. The watchdog closes that gap in <30s.

Tests (15/15 green across the daemon tests):

- __tests__/daemon-watchdog.test.ts (9 tests): covers hasLiveDaemon for the 3 lockfile states, checkAndRespawn (live=no-op, dead=respawn, no-.codegraph=no-op), tick -> respawn integration (single + double), start/stop behavior.

- __tests__/daemon-registry.test.ts: unchanged, still 6/6.

Closes #116.
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.

Daemon does not auto-respawn after Stop-Process — leaves project without incremental file-watcher

1 participant