# Stage 2: Local Runtime Manager MVP This document is the implementation specification for Stage 2 of the [long-term roadmap](../dsh-control-plane-roadmap.md). The implementation is the source-only Runtime Manager in `src/runtime/`. It manages local process handles through an explicit adapter boundary; it is not a remote control plane and it does not install a DSH Plugin. ## Allowlisted adapters Only these adapter IDs are accepted: - `official-dsh`; - `zhitro-dsh`; - `harvis-dev-dsh`. Each factory is independently named and carries the same version-qualified DSH CLI invocation shape observed in the local DSH `0.1.0-rc.8` contract: - inspect: `--profile --dump-config`; - start: `--profile `. The Manager has no API for arbitrary command strings or arbitrary argument arrays. The executable path and local profile name are supplied only by ignored machine configuration and are never included in a public Runtime record, response, fixture, or error summary. The adapter uses direct process spawn with `shell: false`. ## Isolation and lifecycle The caller supplies an ignored local `stateRoot`. For each Runtime, the Manager creates a private `dsh-home` directory and passes it as `DSH_HOME`. It also creates a per-Runtime `.runtime.lock` with a bounded stale-lock recovery path. Runtime records expose only JSON-safe identifiers, statuses, and sanitized error summaries. The lifecycle surface is: - register Runtime and Profile records; - inspect a Profile with the fixed dump-config invocation; - start and stop an adapter-backed process; - report scalar health state; - stop all registered Runtimes independently. Stop sends `SIGTERM`, waits for a bounded timeout, then uses `SIGKILL` only as the adapter's bounded recovery fallback. A process exit or adapter failure is recorded against that Runtime and does not prevent another Runtime from starting or stopping. ## Error and data boundary Inspection output is never returned as a raw log. Only a bounded, sanitized summary and an error code cross the Manager boundary. Executable paths, profile names, lock paths, process handles, environment values, and full DSH output remain private runtime state. The test-only child in `tests/fixtures/runtime-child.mjs` is a synthetic local process. It is not a DSH implementation and is used only to prove isolation, stop cleanup, stale-lock recovery, and failure independence without a real machine Profile or credential. ## Acceptance evidence `tests/runtime-manager.test.mjs` verifies fixed direct-spawn arguments, allowlist and spoof rejection, three independent adapter factories, isolated `DSH_HOME` directories, lock removal, marker cleanup, stale-lock recovery, bounded stop, restart lock ownership, and one Runtime failing while another remains healthy. The test fixture is not a substitute for a later isolated Web Profile cold start. DSH Bundle, Host Service, Client Slot, UI, and Desktop acceptance remain Stage 3 or later work.