Phase 06 Plan 04: CLI Summary
realm CLI with up/down/status/doctor/mcp, exits 0/1/2, detached argv-form spawn, probe-first up with bounded PODMAN_MISSING, and the Phase 5 MCP server over stdio
Performance
- Duration: single orchestrated session
- Started: 2026-10-22
- Completed: 2026-10-22
- Tasks: 3
- Files modified: 2
Accomplishments
src/cli/main.ts: five-command parsing with strict single-arg usage (exit 2), state dir resolved from the passed environment, socket dial bounded bySOCKET_TIMEOUT_MS, probe-firstupthat never spawns on probe failure, spawn-then-retry within the bound, localstatus/doctorthat name the absent daemon, andmcpservingcreateMcpServerfrom the shared handle with stdin-close shutdowntest/cli/cli.test.ts: nine tests citing CLI-01/CLI-02 — spawn argv/detached/stdio assertions, down dispatch, local status/doctor content, usage exits, bounded PODMAN_MISSING (exit 1, no spawn, no dial, wall-clock bound), mcp tools/list plus tools/call through a fake-backed handle with the wiring request pinned, mcp refusal without runtime configuration, and a static allowlist guard (onlynode:net/node:fs/node:child_process, oneprocess.envread, noshell: true)- No new runtime import, no new error code, no new npm dependency; machine-readable codes on stdout, human reasons on stderr, no env value printed
Task Commits
No per-task commits were made: the wave orchestrator owns all commits in this phase.
- Task 1: Argv parsing, exit codes, and daemon spawn - uncommitted (orchestrator commits)
- Task 2: PODMAN_MISSING fast path with bounded wait - uncommitted (orchestrator commits)
- Task 3: realm mcp stdio entry from the composition root - uncommitted (orchestrator commits)
Files Created/Modified
src/cli/main.ts- CLI: parsing, exit codes, spawn, socket client, probe path, mcp entry (P6-D1, P6-D5, P6-D7, P6-D9, P6-D12, P6-D13)test/cli/cli.test.ts- CLI-01 spawn/dial/exit cases, CLI-02 bounded PODMAN_MISSING case, mcp stdio case
Decisions Made
- Any probe failure (
PODMAN_MISSING,PODMAN_MACHINE_DOWN,PODMAN_UNSUPPORTED_VERSION) blocks the spawn, not justPODMAN_MISSING: a daemon without a working Podman cannot serve, and T-06-10 names the probe as the gate beforeupproceeds. - Local
status/doctorwith no daemon exit 0 with a local report that says the daemon is absent (OQ-P6-02 default):disconnectedon stdout plusdaemon absent (REALM_NOT_ACTIVE)on stderr for status; the checks JSON on stdout plus the absent note naming the local check count on stderr for doctor. realm mcpdefaults toprocess.stdin/stdout/stderr; tests injectPassThroughstreams, so no test starts a process.main()holds this module's singleprocess.envread (the entry edge);runCliand every command path takeenvas a parameter. The static-guard test pins the single occurrence.defaultBuildHandlestatically imports the 06-03buildDaemonHandleand passes{ runtimeInput, policyPath, sessionId, libpodSocketPath?, env }:policyPathdefaults to<stateDir>/policy.json(a missing file falls back to validated defaults perloadPolicyFile),sessionIddefaults to"mcp",libpodSocketPathis derived by the probe rule (<XDG_RUNTIME_DIR>/podman/podman.sock, absolute dir only; omitted when underivable since 06-03 refuses half-wired handles at build time), andruntimeInputhas no safe default (image, workload, workspace are host decisions) somcprefusesSCHEMA_INVALIDwithout it instead of inventing configuration. Livemcpstill gates on the AgentOS driver seam, which has no in-repo implementation.
Cross-plan note (not a deviation): 06-03 landed mid-wave, so the initial widened-string lazy import was replaced with the static import above, the mcp test now pins the wiring request, and a refusal test covers the missing-runtimeInput path. Production realm mcp still needs caller-supplied runtimeInput; there is no config-file story in Phase 6 scope.
Deviations from Plan
None - plan executed exactly as written, plus the 06-03-driven seam update above (same-plan files, requested by the sibling plan's landed API). Two style-rule reminders were evaluated during implementation (PodmanProbeResult named type instead of ReturnType; the single-use sleep helper inlined). The Promise.withResolvers suggestion was declined because the repo targets ES2023, whose lib lacks it. These are in-plan code-shape choices, not deviations.
Issues Encountered
- An
editrange mistake briefly duplicated thepodman-probeimport and dropped theSpawnDaemontype, leaving the file unparseable; re-read and two correcting edits restored it. No other issues. test/cli/cli.test.tsinitially imported a non-existentControlRequestShimtype; removed before finishing.
User Setup Required
None - no external service configuration required.
Next Phase Readiness
- 06-03 consumed (
buildDaemonHandlestatically imported; productionmcpneeds caller-suppliedruntimeInput). Ready for 06-05 (binpoints at the compileddist/src/cli/main.js, whose direct-invocation guard callsmain()) and 06-06 e2e (CLI-over-fake-daemon through the injectedsend/spawnseams). - Verification pending:
npx tsc -p tsconfig.json && node --test dist/test/cli/cli.test.jsplus theCLI-01/CLI-02title grep — deliberately not run here per wave constraints; the orchestrator gates on it. Coverage entries are thereforeunknown, which routes them to human/orchestrator verification rather than auto-pass.
Phase: 06-cli-daemon-and-host-packagingCompleted: 2026-10-22