#2475
Foreground daemon emits no lifecycle logging (empty launchd StandardOutPath)
Foreground daemon emits no lifecycle logging (empty launchd StandardOutPath)
Environment
- MemPalace 3.8.0 (
mempalace --version->MemPalace 3.8.0; package metadata andmempalace.version.__version__agree) - STILL PRESENT ON LATEST: v3.9.0 (2026-08-31) is the current release, and
mempalace/daemon.pyis byte-identical between v3.8.0 and v3.9.0 — thelog_messageno-op and theserve_foreverpath are unchanged there. - Platform: macOS 26.6.2 (Sequoia-class), python3.9 (uv tool install)
- Launch form: launchd agent
ai.mempalace.daemon(/Users/hermes/Library/LaunchAgents/ai.mempalace.daemon.plist) running/Users/hermes/.local/bin/mempalace daemon start --foreground, withStandardOutPath=/Users/hermes/.mempalace/logs/daemon.logandStandardErrorPath=/Users/hermes/.mempalace/logs/daemon.error.log.
Observed
StandardOutPathstays empty indefinitely while the daemon runs:/Users/hermes/.mempalace/logs/daemon.log-> 0 bytes (mtime 2026-08-11)/Users/hermes/.mempalace/logs/daemon.error.log-> 1848 bytes, 8 lines, all of them prior startup errors (e.g. "writable daemon startup refused: another writer owns local backend 'chroma'"), none of them lifecycle output from a healthy run. So stderr proves the redirect works; the healthy foreground path simply never writes to it.
mempalace daemon statusreports the daemon is running (PID alive, endpoint reachable), so the process is healthy — it simply emits no output.
Expected
- In
--foregroundmode the server should log at least startup and lifecycle events to stderr (or stdout), so a supervisor that captures viaStandardOutPath/StandardErrorPathactually receives log lines.
Cause
run_server(mempalace/daemon.py:872) emits no intentional startup or HTTP access logging in foreground mode. Its only output hook is the HTTP handler'slog_message, which is a deliberate no-op:No other code path in# mempalace/daemon.py:920-921 def log_message(self, fmt, *args): # pragma: no cover - stdlib access logging noise returnrun_serverprints, so with stdout redirected to the launchd log path that file stays empty for the life of a healthy run.- Contrast: non-foreground mode spawns the child with stdout/stderr bound to a
per-daemon file via
_detached_kwargs(mempalace/daemon.py:1257-1260) andkwargs = _detached_kwargs(sd / "daemon.log")(mempalace/daemon.py:1332). That path works; only foreground is silent. - The daemon does NOT import
mcp_server, so the MCP stdio guard there is unrelated and is not the cause.
Proposed fix
Make the foreground path emit timestamped, level-gated lines to stderr:
--- a/mempalace/daemon.py
+++ b/mempalace/daemon.py
@@ run_server (foreground), after `runtime = DaemonRuntime(...)`:
+ import sys, logging
+ logging.basicConfig(stream=sys.stderr, level=logging.INFO,
+ format="%(asctime)s %(levelname)s %(message)s")
+ log = logging.getLogger("mempalace.daemon")
+ log.info("daemon listening on %s", runtime.endpoint)Optionally replace the _Handler.log_message no-op with
logging.getLogger("mempalace.daemon.http").info(fmt, *args) so HTTP access
logging also flows to stderr when level permits. Keep non-foreground behavior
unchanged (it already has its own log file).
Workaround
- Liveness/health is observable via
mempalace daemon status(no log needed). - For actual on-disk logs, run without
--foreground: the non-foreground path writes to/Users/hermes/.mempalace/daemon/<sha>/daemon.log.
Activity
0No activity yet.