Phase 06 Plan 06: End-to-End Proof with Fakes Summary
RT-06, CLI-01, CLI-02, and CLI-03 proven end-to-end with fakes: CLI-driven up/status/down lifecycle, the bounded PODMAN_MISSING fast path, the full doctor report, socket permissions, and the static no-container-mounts invariant.
One-liner
CLI-over-fake-daemon lifecycle, PODMAN_MISSING fast path, doctor report, 0700/0600 socket guards, and no-.sock-in-container-argv proof in one fake-backed suite.
Tasks completed
Task 1: Lifecycle e2e up/status/down plus PODMAN_MISSING (CLI-01, CLI-02, RT-06)
test/daemon/e2e.test.ts drives the real runCli against the real startDaemon (built exactly like test/daemon/daemon.test.ts: FakeExecutionService, FakeBridge, recording sweep, scripted healthy probe) over a real Unix socket in a temporary state directory:
- Lifecycle:
upreturns exit 0 with snapshotactiveand labelrealm active;statusconfirms active;downreturnsunbound; teardown resolvesdonewith the socket file gone; a follow-upstatusreportsdisconnectedvia the daemon-absent local path. No second spawn occurs while the daemon serves. - Fast path (CLI-02, T-06-15):
upwith arunCommandfake that throwsENOENTreturns exit 1 with stdoutPODMAN_MISSINGand the catalogue reason on stderr, with zero spawns, zero socket sends, zero Podman-socket dials, and aDate.now()wall-clock assertion under 2000 ms. - Doctor (CLI-03):
doctorthrough the CLI against the fake-backed daemon with failing Podman/socket probes returns the full report; every non-pass item carries fix text and fix is null only on pass (see deviation 2 for the count).
Task 2: Socket permissions and no-container-mounts proof (RT-06, P6-D15)
- Permissions: after
startDaemon, the temp run directory stats exactly0700and the bound socket exactly0600; the socket path equalscontrolSocketPath(stateDir). - No-mounts (T-06-14):
buildCreateArgvoutput andbuildPolicyDocumentoutput (JSON-stringified) carry no.sockentry, no control-socket value, and nopodman.sock/docker.sockstring; plus ano-bypass.test.ts-style source guard assertingsrc/runtime/openshell.tsnames no.sockpath.
Deviations from Plan
1. [Rule 3 - blocking issue] CLI up sends no params; daemon up requires workspaceRoot
- Found during: Task 1.
- Issue:
runCli'supsends{ id, method: "up" }with no params and accepts no workspace argv, while the daemon'supdispatch answersSCHEMA_INVALIDwithout aworkspaceRootparam. Driving the CLI entry straight at the daemon yields exit 1SCHEMA_INVALID, not the lifecycle. - Fix: the e2e
sendseam forwards every method byte-identical to the real daemon socket via the realsendRequest, except a param-lessup, which gets{ params: { workspaceRoot: fixtureWs } }attached. Documented in the file's comment block. - Follow-up (not this plan): teach
realm upto take (or default) the workspace root instead of bridging it in a test seam. No source file was changed here — test-only bridging.
2. Doctor report asserts nine ids, not eight
- Found during: Task 1 doctor case.
- Issue: the plan says "eight items", but
src/doctor/checks.tsregisters seven items today and 06-05 appends two (control-channel,agentos) afteranda-bridgewithout reshaping — nine total. Pinning exactly eight would contradict 06-05's own append rule. - Fix: the test asserts presence of all nine ids, the two new ids appended after
anda-bridge, and the fix-text invariant on every item. If 06-05 lands with exactly eight (merging one existing item against its own prohibition), trim the id list. This test also requires 06-05's items to exist: run it after 06-05 lands.
3. Fast-path bound follows the cli.test.ts precedent
- Wall-clock bound 2000 ms with
timeoutMs: 50, mirroring the existingrealm up with Podman missingCLI test. No new timing policy introduced.
Decisions made
- Lifecycle, fast path, and doctor cases all flow through
runCli(not around it): probe, exit codes, and reply rendering are exercised, with only socket I/O forwarded to the live fake-backed daemon. - Permission assertions read real
statSyncbits on the temp directory, never injected stat seams — the channel unit tests already cover the seam matrix. - The no-mounts proof is runtime-first (real builders) with a source-grep kicker on
openshell.ts, so either a runtime regression or a newly named socket path breaks the suite.
Auth gates
None.
Known stubs
None. Every assertion reads a real builder, a real socket, or real file stats; the doctor report comes from the real runChecks runner (default daemon path) with only its I/O probes faked.
Threat flags
None — test-only file. It implements the plan's mitigations directly: the no-mounts proof fails the suite on any future .sock mount (T-06-14) and the wall-clock bound proves up cannot hang without Podman (T-06-15).
Files changed
test/daemon/e2e.test.ts(new): five tests citing CLI-01, CLI-02, CLI-03, RT-06 in their titles.
Verification (for the main agent; not run here)
Per assignment constraints no command was run. To verify:
npx tsc -p tsconfig.json && node --test dist/test/daemon/e2e.test.js
grep test/daemon/e2e.test.ts -c "CLI-0"Note: the doctor case needs 06-05's control-channel and agentos items in src/doctor/checks.ts first; until then that single test fails while the other four pass.
Self-Check: PASSED
test/daemon/e2e.test.tsexists; all imports resolve to existing modules (src/cli/main.js,src/daemon/channel.js,src/daemon/daemon.js,src/doctor/checks.js,src/runtime/openshell.js,src/session/paths.js,src/core/errors.js,src/core/status.js,test/support/fake-execution.js,test/support/fake-bridge.js).- Commits: intentionally 0 — the assignment forbids git commands; the main agent commits.