Daemon lifecycle

Updated Jun 22, 2026

weaverd backgrounds itself using daemonize-me and manages runtime artefacts under the same directory as the Unix socket (for example $XDG_RUNTIME_DIR/weaver). Launching the daemon creates a lock file (weaverd.lock), a PID file (weaverd.pid), and a health snapshot (weaverd.health). PID and health files are written atomically, so observers never see a partially written payload. Attempts to start a second copy while one is running fail fast with an "already running" error that reports the existing PID. When the original launch is still initializing and has not yet published a PID, the second invocation now reports "launch already in progress" instead of removing the lock. If the daemon exited uncleanly, the new instance removes the stale files before continuing.

The daemon now binds a socket listener as part of startup. It binds to the configured --daemon-socket endpoint and accepts multiple client connections concurrently. On Unix targets, stale socket files are removed only after confirming no listener responds, while actively used sockets cause the daemon to fail fast with a clear error. The listener removes the Unix socket file on shutdown to avoid lingering bind failures.

The daemon implements a JSONL request dispatch loop that reads CommandRequest messages from connected clients, routes them to the appropriate domain handler, and streams DaemonMessage responses back. Request parsing validates the JSONL structure and rejects malformed input with structured error messages. Domain routing supports observe, act, and verify commands. Unknown domains or operations return structured errors with exit status 1.

The observe get-definition, observe get-card, and observe graph-slice operations are fully implemented. get-definition accepts --uri and --position, infers the language from the file extension, initializes the appropriate language server, and returns definition locations as JSON. get-card accepts the same location arguments plus --detail, reads the target file locally, and returns a Tree-sitter-backed symbol card for supported Rust, Python, and TypeScript files. graph-slice accepts the same location arguments plus traversal, detail, and budget options, and returns a stable same-file graph-slice envelope. Missing or malformed arguments return structured error messages with exit status 1. Operations outside the implemented observe subcommands, and outside the implemented act and verify flows, may return "not yet implemented" responses while backend wiring is being completed.

The health snapshot is a single-line JSON document describing the current state, enabling operators and automation to poll readiness without speaking the daemon protocol. Example:

{"status":"ready","pid":12345,"timestamp":1713356400}

The status transitions through starting, ready, and stopping before the files are removed on shutdown. Sending SIGTERM, SIGINT, SIGQUIT, or SIGHUP prompts the daemon to log the request and complete its shutdown sequence within a ten-second budget. For interactive debugging or CI jobs, set WEAVER_FOREGROUND=1 to keep the daemon attached to the terminal while preserving the same lock, PID, and health semantics.