Phase 06 Plan 03: Daemon Summary
Single-owner daemon with reap-before-serve, four-method channel dispatch, and graceful down shutdown, sharing the composition-root wiring with realm mcp
Performance
- Duration: single execution wave
- Started: 2026-10-22
- Completed: 2026-10-22
- Tasks: 3
- Files modified: 2
Accomplishments
buildDaemonHandlebuilds the exactcreateRealmHandlepath with production adapters (SessionExecution over the libpod client, AndaBridge over the configured URL, spawnOpenShellRunner, Date clock); tests inject the existing fakes at the same seam (P6-D12)startDaemonenforces single ownership per state directory: stale-socket connect-then-remove, then listen plus 0600 chmod; a live rival refuses with LOCK_CONFLICT- Reap-before-serve:
reapStartupwith the boundedremoveAllRealmContainerssweep runs before the serve gate opens; a failed sweep rejects startup with records untouched (P6-D4, T-06-06) - All four CONTROL_METHODS dispatch over the 06-01 channel with catalogue-code-only replies: status via displayStatus, up via probe-then-create, down via drain-then-close, doctor via runChecks (T-06-08)
- Graceful shutdown on down: stops accepting, drains in-flight within SOCKET_TIMEOUT_MS, delegates teardown to the RealmHandle close path, removes the socket, resolves done; a concurrent second down is a no-op (T-06-07)
- Threat mitigations T-06-06 (reap before serve), T-06-07 (single owner plus bounded drain), and T-06-08 (shared wiring, no second dispatcher path) are implemented as specified
Task Commits
Per-task commits were not made in this wave: the wave constraints prohibit running git commands, and the orchestrator gates and commits afterward.
- Task 1: buildDaemonHandle plus single-owner start - uncommitted (orchestrator commits)
- Task 2: Reap-before-serve and method dispatch - uncommitted (orchestrator commits)
- Task 3: Graceful shutdown on down - uncommitted (orchestrator commits)
Files Created/Modified
src/daemon/daemon.ts- buildDaemonHandle, startDaemon, method dispatch, graceful shutdown (P6-D1, P6-D4, P6-D12)test/daemon/daemon.test.ts- CLI-01 and RT-06 ownership, reap-order, dispatch, and shutdown cases
Decisions Made
- Second owner refuses with LOCK_CONFLICT; an EADDRINUSE bind race between the stale probe and listen maps to LOCK_CONFLICT rather than a generic failure
- No andaUrl and no injected bridge builds a module-private unreachable bridge (connect rejects ANDA_UNREACHABLE), so status and doctor keep working while up reports unreachable through the controller
- Omitting both the sweep seam and any libpod seam refuses with SCHEMA_INVALID at startup rather than silently skipping the P6-D4 reap
- down replies after controller close with the true outcome; a concurrent second down answers the current snapshot without re-running teardown
- down keeps its own in-flight count during the drain, so the drain waits only for other requests (inFlight above one)
- Probe defaults mirror the doctor defaults exactly (argv-form execFile, env spread copy, plain dial); no new helper contract was introduced
Deviations from Plan
None - plan executed exactly as written. Style-level adjustments during writing (inline narrowing instead of a local record guard, typed error members instead of inline casts, removal of a dead connection-tracking set) changed no behavior.
Issues Encountered
- The mid-write discovery that
src/cli/main.ts(sibling plan 06-04, same wave) guessesbuildDaemonHandle(env)with an env-only signature. That guess cannot satisfy P6-D12 (the handle needs runtimeInput, policyPath, sessionId). Messaged Exec06_04 with the exact 06-03 export contract so itsdefaultBuildHandlepasses full options. No 06-03 change was needed: the plan-specified options object is the correct contract. - Verification (tsc plus node --test) was not run: this wave prohibits running builds, tests, linters, and git commands. The gate run belongs to the orchestrator. Test titles cite CLI-01 or RT-06 as the plan requires; grep for those IDs plus the tsc/test invocation in the plan verification block is the orchestrator's check.
User Setup Required
None - no external service configuration required.
Next Phase Readiness
- 06-04 (CLI) can consume
buildDaemonHandleandstartDaemonthrough the contract messaged to Exec06_04: build{ runtimeInput, policyPath, sessionId, libpodSocketPath, env }from CLI-resolved values; every other seam is optional - The e2e proof can drive up/status/down/doctor over a real socket pair with scripted probe and sweep seams; no live Podman, OpenShell, or daemon is needed in tests
- Live Podman/OpenShell/daemon acceptance remains gated as the phase brief requires; the suite proves unit-level behavior only
Phase: 06-cli-daemon-and-host-packagingCompleted: 2026-10-22