Context
Split from #13573 (its AC1 — the one daemon that can't take a simple pre-start guard). #13573's PR guarded the kb-* daemons (kb-alerting/kb-gc/kb-reconciliation) and verified embed already self-guards. The wake daemon is structurally different and needs a refactor, not a call-site add — carved out here.
The Problem
ai/daemons/wake/daemon.mjs reads config at module-load (top-level), e.g.:
const DB_PATH = memoryCoreConfig.storagePaths.graph;
const DAEMON_DATA_DIR = memoryCoreConfig.wakeDaemon.dataDir;
fs.ensureDirSync(DAEMON_DATA_DIR);
So a pre-start assertConfigFresh(...).then(() => start()) wrapper (the kb-* / orchestrator pattern) can't protect it — the config-dependent module-load code runs at import, before any guarded entry. On a stale memory-core overlay (missing a leaf its template added), these top-level reads crash with a cryptic undefined-deref, exactly the #13560 class.
Acceptance Criteria
Out of Scope
- The kb-* daemons (done in #13573's PR) +
embed (already self-guards via getMissingMemoryWalLeaves).
- The
assertConfigFresh detection logic (unchanged).
Decision Record impact
none — applies the #13568 guard via an entry-point refactor specific to wake's module-load shape.
Release classification
post-release / reliability hardening — boardless.
Authored by Vega (Claude Opus 4.8, Claude Code). Session 64ee317e-53b6-4f76-8241-f4eade1c084d.
Context
Split from #13573 (its AC1 — the one daemon that can't take a simple pre-start guard). #13573's PR guarded the kb-* daemons (kb-alerting/kb-gc/kb-reconciliation) and verified
embedalready self-guards. The wake daemon is structurally different and needs a refactor, not a call-site add — carved out here.The Problem
ai/daemons/wake/daemon.mjsreads config at module-load (top-level), e.g.:const DB_PATH = memoryCoreConfig.storagePaths.graph; // ~line 74 const DAEMON_DATA_DIR = memoryCoreConfig.wakeDaemon.dataDir; // ~line 75 fs.ensureDirSync(DAEMON_DATA_DIR); // + pruneOldLogs() etc. at module-loadSo a pre-start
assertConfigFresh(...).then(() => start())wrapper (the kb-* / orchestrator pattern) can't protect it — the config-dependent module-load code runs at import, before any guarded entry. On a stale memory-core overlay (missing a leaf its template added), these top-level reads crash with a crypticundefined-deref, exactly the #13560 class.Acceptance Criteria
assertConfigFresh-guarded async entry (main()): the guard runs first, then the config-dependent setup (DB_PATH/DAEMON_DATA_DIR/fs.ensureDirSync/pruneOldLogs/the poll loop).serverPath: memory-core) — wake reads config viamemoryCoreConfig(the memory-core overlay'sstoragePaths/wakeDaemonleaves) +AiConfig(Tier-1). The memory-core guard covers the crash-prone leaves; confirm whether a Tier-1 guard is also warranted.Out of Scope
embed(already self-guards viagetMissingMemoryWalLeaves).assertConfigFreshdetection logic (unchanged).Decision Record impact
none— applies the #13568 guard via an entry-point refactor specific to wake's module-load shape.Release classification
post-release / reliability hardening — boardless.
Authored by Vega (Claude Opus 4.8, Claude Code). Session 64ee317e-53b6-4f76-8241-f4eade1c084d.