<objective> Compose the runtime into the ExecutionService implementation with hardening enforcement and the realm down sweep, and prove the no-bypass structure.
Purpose: Deliver the D-67 composition and the D-68 zero-orphan sweep plus the D-46 structural guarantee that all execution routes through OpenShell. Output: Session execution module and three test files. </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/03-container-runtime-and-bridges/03-CONTEXT.md @.planning/phases/03-container-runtime-and-bridges/03-PATTERNS.md </context>
<tasks>
<task type="tdd"> <name>Task 1: ExecutionService composition with hardening gate</name> <files>src/runtime/session-execution.ts, test/runtime/session-execution.test.ts</files> <read_first> - src/core/ports.ts (from plan 03-01; the exact frozen ExecutionService signatures to implement) - src/runtime/libpod-client.ts (from plan 03-04; inspect, stop, remove primitives) - src/runtime/openshell.ts (from plan 03-05; policy write, argv builder, idempotent create) - src/runtime/hardening.ts (from plan 03-07; HARDENING_PROFILE and verifyInspect) - test/lifecycle/idempotent.test.ts (repeat-create one-resource semantics to mirror for CORE-06) </read_first> <behavior> - Test: create reuses the labelled container with no second create call (OpenShell fake call count; CORE-06, OQ-P3-01 default) - Test: verification failure destroys the container and gives OPENSHELL_START_FAILED with the first failed property named (D-47: realm verifies what OpenShell creates; OQ-P3-03 default) - Test: policy document written before create (fake ordering assertion; RT-07 ordering) - Test: start on a non-running container gives OPENSHELL_START_FAILED; probe healthy only when running plus AgentOS alive true - Test titles cite CORE-06, RT-01, and RT-03 </behavior> <action>Write the failing tests first per behavior, then implement SessionExecution in src/runtime/session-execution.ts per D-67: create({ idempotencyKey, workspaceRoot }) builds the hardened RuntimeConfig view (plan 03-02 validator), writes the policy document (D-57), runs the OpenShell create with label idempotency (D-56), resolves the container id, and runs the hardening verifier (D-51); a mismatch destroys the container and throws OPENSHELL_START_FAILED naming the first failed property, with no fallback to a Realm-created container (D-47). start checks running through inspectContainer and starts nothing itself (--detach owns startup); probe gives healthy only when running and AgentOS alive() is true; stop calls stopContainer; destroy calls removeContainer(force). Every dependency (LibpodClient, OpenShellRunner, AgentOsSupervisor) arrives as an argument; nothing reads process.env. OQ-P3-04 (sandbox record persistence) defaults to containers-only removal here; the Phase 4 reaper owns records.</action> <acceptance_criteria> - test/runtime/session-execution.test.ts asserts reuse, destroy-on-mismatch with OPENSHELL_START_FAILED, and policy-before-create ordering - No process.env read exists in src/runtime/session-execution.ts </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/runtime/session-execution.test.js</automated> <fails_when>non-zero exit, or the summary shows 0 passed</fails_when> </verify> <done>Composition green with verify-then-destroy and idempotent create.</done> </task>
<task type="tdd"> <name>Task 2: Realm down sweep with zero-orphan proof</name> <files>src/runtime/session-execution.ts, test/runtime/down-sweep.test.ts</files> <read_first> - src/runtime/session-execution.ts (from Task 1 of this plan; removeAllRealmContainers lives beside the session destroy in this module) - src/lifecycle/controller.ts (destroy path lines around 241: two-phase destroy that resumes from stopping; the sweep mirrors that resume rule) </read_first> <behavior> - Test: three labelled containers plus one foreign container; after the sweep no labelled container remains and the foreign one is untouched - Test: a remaining container gives REALM_STOPPING with the session left in stopping; a repeated sweep resumes and is a no-op when clean - Test titles cite RT-09 </behavior> <action>Write the failing tests first per behavior, then implement removeAllRealmContainers(client) in src/runtime/session-execution.ts per D-68: list every container with io.realm.managed=true, remove each with force, then list again and fail with REALM_STOPPING if any remain, so the sweep proves zero orphans. Containers without the realm label are never touched. A repeated call resumes cleanly. The controller session destroy removes that session container and the sweep covers leftovers; neither path implements reaping (RT-08 is Phase 4).</action> <acceptance_criteria> - test/runtime/down-sweep.test.ts asserts labelled removal, foreign preservation, repeat no-op, and REALM_STOPPING on a survivor - Sweep lists twice (before and proof) on every run </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/runtime/down-sweep.test.js</automated> <fails_when>non-zero exit, or the summary shows 0 passed</fails_when> </verify> <done>Sweep green with proven zero orphans and foreign containers untouched.</done> </task>
<task type="auto"> <name>Task 3: Structural no-bypass routing proof</name> <files>test/runtime/openshell-routing.test.ts</files> <read_first> - src/runtime/session-execution.ts (from Task 1 of this plan; the workload start goes only through the OpenShell argv builder) - src/runtime/libpod-client.ts (from plan 03-04; key-set check shows no exec method) - src/runtime/agentos.ts (from plan 03-07; AgentOS starts only through the supervisor) - test/lifecycle/no-host-env.test.ts line 98 (runtime key-set check pattern, no ts-expect-error) </read_first> <action>Write test/runtime/openshell-routing.test.ts per D-46 as a structural test, not a behavior test: the session-execution workload start goes only through the OpenShell argv builder (assert on the OpenShell fake call log from plan 03-05); the libpod client prototype key set contains no exec (runtime key-set check in the no-host-env style); AgentOS starts only through the supervisor (assert on the AgentOS fake call log from plan 03-07). This is the D-46 guarantee that all agent execution routes through OpenShell despite the RT-03 wording drift. Test titles cite RT-03.</action> <acceptance_criteria> - All three no-bypass assertions hold in one test file with RT-03 in the titles - No live process, socket, or container is touched </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/runtime/openshell-routing.test.js</automated> <fails_when>non-zero exit, or the summary shows 0 passed</fails_when> </verify> <done>No-bypass structure proven with all three assertions green.</done> </task>
</tasks>
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| create result -> session | Container id crosses from OpenShell output into session state; only the label-lookup id is trusted, never caller text |
| inspect result -> verifier | Container description crosses into the destroy decision; mismatch destroys first, throws second |
| sweep list -> remover | Label query results cross into force-remove; only realm-labelled ids are removed |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-03-19 | T (Tampering) | src/runtime/session-execution.ts | high | mitigate | Hardening mismatch destroys the container before throwing; no unverified container is ever returned as running |
| T-03-20 | E (Elevation of Privilege) | test/runtime/openshell-routing.test.ts | high | mitigate | Structural test pins the only workload path to the OpenShell builder, no exec method on the client, and supervisor-only AgentOS start; a new path fails the test |
| T-03-21 | D (Denial of Service) | src/runtime/session-execution.ts | medium | mitigate | Sweep proves zero orphans by listing again and reports REALM_STOPPING on survivors; repeated sweep resumes |
| </threat_model> |
<verification> npx tsc -p tsconfig.json && node --test dist/test/runtime/session-execution.test.js dist/test/runtime/down-sweep.test.js dist/test/runtime/openshell-routing.test.js; grep the three test files for their requirement IDs. </verification>
<success_criteria> Verify-then-destroy, proven zero-orphan sweep, and structural no-bypass proof all green on fakes. </success_criteria>
<output> Create .planning/phases/03-container-runtime-and-bridges/03-08-SUMMARY.md when done </output>