<objective> Prove the phase end-to-end with fakes: the up/status/down lifecycle, the bounded PODMAN_MISSING path, socket permissions, and the static no-container-mounts invariant.
Purpose: Close RT-06, CLI-01, CLI-02, and CLI-03 with the goal-backward evidence the phase verification reads. Output: The e2e test file. </objective>
<execution_context> @~/.claude/gsd-core/workflows/execute-plan.md @~/.claude/gsd-core/templates/summary.md </execution_context>
<context> @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/06-cli-daemon-and-host-packaging/06-CONTEXT.md @.planning/phases/06-cli-daemon-and-host-packaging/06-01-PLAN.md @.planning/phases/06-cli-daemon-and-host-packaging/06-03-PLAN.md @.planning/phases/06-cli-daemon-and-host-packaging/06-04-PLAN.md @.planning/phases/06-cli-daemon-and-host-packaging/06-05-PLAN.md </context>
<tasks>
<task type="tdd"> <name>Task 1: Lifecycle e2e up/status/down plus PODMAN_MISSING (CLI-01, CLI-02, RT-06)</name> <files>test/daemon/e2e.test.ts</files> <read_first> - src/cli/main.ts (from plan 06-04; the command entry and injected seams) - src/daemon/daemon.ts (from plan 06-03; startDaemon and buildDaemonHandle with fake adapters) - test/support/fake-tools.ts (makeHarness: real dispatcher over fake stages; first choice for the handle) - test/support/fake-execution.ts and fake-bridge.ts (adapter fakes for the daemon handle) - src/runtime/podman-probe.ts lines 39-56 (probe options and failure shape for the missing-Podman fake) </read_first> <behavior> - Test (CLI-01): up then status reports realm active with the shared label, then down returns realm disconnected with zero surviving daemon state - Test (CLI-02): up with a runCommand fake that fails start (ENOENT) reports disconnected with PODMAN_MISSING, spawns nothing, and returns within the asserted wall-clock bound - Test (CLI-03): doctor through the e2e path lists all eight items with fix text on every non-pass - Test titles cite CLI-01, CLI-02, CLI-03, or RT-06 </behavior> <action>Write the failing tests first per behavior, then implement test/daemon/e2e.test.ts: drive the 06-04 CLI entry against the 06-03 daemon built on makeHarness plus the fake execution and bridge helpers over a real socket in a temporary state directory, asserting the full lifecycle, the PODMAN_MISSING fast path with its wall-clock bound and no-spawn assertion, and the eight-item doctor report. No live process, no network, no container. Test titles cite CLI-01, CLI-02, CLI-03, or RT-06.</action> <acceptance_criteria> - The lifecycle, fast-path, and doctor cases pass against fakes with the wall-clock bound asserted on the PODMAN_MISSING path </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/daemon/e2e.test.js</automated> </verify> <done>Lifecycle and fast-path behavior proven end-to-end with fakes.</done> </task>
<task type="auto"> <name>Task 2: Socket permissions and no-container-mounts proof (RT-06, P6-D15)</name> <files>test/daemon/e2e.test.ts</files> <read_first> - test/daemon/e2e.test.ts (from Task 1 of this plan; the temp-state-dir harness this task extends) - src/runtime/openshell.ts lines 98-140 (buildCreateArgv: the argv the proof inspects) - src/runtime/openshell.ts lines 245-262 (buildPolicyDocument and writePolicyDocument: the mounts the proof inspects) - test/tools/no-bypass.test.ts (the grep-source static-guard method P6-D15 copies) </read_first> <action>Extend the Task 1 file per P6-D15: assert the temporary run directory stats 0700 and the bound socket 0600 after daemon start, and add the static no-mounts proof asserting buildCreateArgv output and the policy document carry no .sock entry, no controlSocketPath value, and no Podman or Docker socket path, in the test/tools/no-bypass.test.ts grep-source style. Test titles cite RT-06.</action> <acceptance_criteria> - Permission assertions read the real temp-dir stats; the no-mounts proof inspects the real argv and policy document builders </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/daemon/e2e.test.js</automated> </verify> <done>Socket permissions and the no-mounts invariant pinned.</done> </task>
</tasks>
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| fake probe -> up path | Simulated PODMAN_MISSING crosses here; the test proves the CLI stops before any spawn |
| create argv -> container | Future argv edits cross here; the static proof fails the suite if a socket path is ever mounted |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-06-14 | T (Tampering) | test/daemon/e2e.test.ts | medium | mitigate | The no-mounts proof inspects the real argv and policy builders, so a later edit mounting any .sock path breaks the suite (P6-D15) |
| T-06-15 | D (Denial of Service) | test/daemon/e2e.test.ts | low | mitigate | The wall-clock bound on the PODMAN_MISSING path proves up cannot hang when Podman is absent (P6-D5) |
| </threat_model> |
<verification> npx tsc -p tsconfig.json && node --test dist/test/daemon/e2e.test.js; grep test/daemon/e2e.test.ts for RT-06, CLI-01, CLI-02, CLI-03 in test titles. </verification>
<success_criteria> Lifecycle, fast-path, permissions, and no-mounts evidence complete with fakes; the phase verification can report unit-verified, live-gated. </success_criteria>
Artifacts this phase produces (contribution of this plan)
test/daemon/e2e.test.ts(new): RT-06, CLI-01, CLI-02, and CLI-03 end-to-end cases with fakes (lifecycle, fast path, permissions, no-mounts).
<output> Create .planning/phases/06-cli-daemon-and-host-packaging/06-06-SUMMARY.md when done </output>