.maestro/playbooks/2026-03-12-CM-Issues-PRs/2026-03-12-Issues-PRs-Triage/TRIAGE-04-Worker-Service-Reliability.md
These bugs cause the worker service to silently fail, report incorrect health status, or get killed by process managers. They're high severity because users think claude-mem is working when it's actually dead — observations are silently lost.
Issues addressed: #1323, #1231, #1245 Prerequisite: Phases 01-03 should be complete.
Fix race condition in database initialization on session-init hook (#1323). The hook fires before initializeBackground() completes in src/services/worker-service.ts, causing "Database not initialized" errors:
src/services/worker-service.ts — find the initializeBackground() method (around line 377) and the readiness tracking (initializationCompleteFlag, resolveInitialization)src/services/worker/http/routes/SessionRoutes.tsawait this.workerService.initializationComplete before proceeding with database operations. The promise already exists (line ~182) — it resolves when DB + search are readyinitializationComplete across the routessrc/services/worker/http/middleware.ts), ensure it covers the session-init endpointRetry-After: 1 header if not initialized, so the hook retries/sessions/* guard middleware in worker-service.ts matching the existing /api/* guard pattern. Legacy session routes now wait for initializationComplete with 30s timeout, returning 503 if DB isn't ready. 5 tests added in tests/worker/http/initialization-guard.test.ts. All 1154 tests pass. Commit 726afd12. Fix stale PID file causing false "worker running" detection (#1231). ProcessManager.readPidFile() returns a PID that no longer corresponds to a running worker, causing the startup logic to skip spawning a new one:
src/services/infrastructure/ProcessManager.ts — find readPidFile() (around line 124-165) and isProcessAlive() (around line 698)isProcessAlive(pid) AND a health check to /api/health. A process can be alive (PID exists) but not be the worker (different process reused the PID)ensureWorkerRunning() in hook-command.ts), add this validation sequence:
isProcessAlive(pid) → if dead, remove stale PID file, spawn new workerworker-service.ts: (1) Daemon startup guard now validates PID liveness AND health check via isPortInUse() — if PID alive but health fails, removes stale PID file instead of refusing to start. (2) ensureWorkerStarted() now removes residual PID file when health check fails but cleanStalePidFile() kept it (PID reuse case). 7 tests added in tests/infrastructure/stale-pid-detection.test.ts. All 1033 tests pass. Commit 840a7500. Fix systemd SIGKILL from fork-then-exit pattern (#1245). Under systemd, the worker-service.cjs start subcommand forks a background process and exits, but systemd's default KillMode=control-group kills all processes in the cgroup including the forked worker:
plugin/scripts/worker-service.cjs to understand the start subcommand's fork patternworker-service.cjs, when running under systemd (detect via INVOCATION_ID env var or NOTIFY_SOCKET), do NOT fork — run the worker in the foreground so systemd tracks the correct PIDsystemd mode: if process.env.INVOCATION_ID is set, skip the fork-and-exit logic and run directlyType=exec or Type=simple in their service file, not Type=forkingisRunningUnderSystemd() to ProcessManager.ts that detects systemd via INVOCATION_ID env var. In worker-service.ts main(), when start command runs under systemd, it redirects to --daemon (foreground) mode — reusing all existing guard checks (PID file validation, port-in-use check, unhandled error handlers). 3 tests added in tests/infrastructure/systemd-foreground.test.ts. All infrastructure tests pass. Build synced. Commit 5fd91ec0.Run tests and verify worker lifecycle:
npm test — all tests must passnpm run build-and-sync/api/health response.