# Stage 3: Installable DSH Bundle and Client Panels This document is the implementation specification and evidence record for Stage 3 of the [long-term roadmap](../dsh-control-plane-roadmap.md). It pins the current local DSH contract to `0.1.0-rc.8` and keeps the public package safe to publish without a machine-specific Profile. ## Deliverable shape The distribution is one installable package, `dsh-control-plane`, rather than a set of `file:packages/*` dependencies. This is intentional: a GitHub or tarball install must not rely on a source checkout being present beside the installed package. The package exposes: - `./lib/index.js`: the Host entry and `ControlPlaneGateway` service; - `./lib/typert.host.js`: the strict Host Typert manifest; - `./lib/client.js`: the checked-in browser bundle; - `./cordis.patch.yml`: one additive Loader row for the package. The `packages/` tree contains internal source modules used to build the single browser bundle. They are not separate published DSH plugin rows. The bundle orchestrator mounts the Remote contribution first, then activates three independent settings panels: Runtime Catalog, Session Board, and Plan Panel. ## DSH contract The package follows the inspected local DSH `0.1.0-rc.8` contract: ```json { "exports": { ".": "./lib/index.js", "./client": "./lib/client.js", "./typert": "./lib/typert.host.js", "./package.json": "./package.json" }, "dsh": { "bundle": { "patch": "./cordis.patch.yml" }, "client": { "platform": "web", "inject": ["remote", "slots"] } } } ``` The Host service extends `TypertRemoteService` and binds the visible Cordis service key `controlPlane`. The strict manifest publishes only three direct, zero-argument methods: - `controlPlane/runtimeCatalog`; - `controlPlane/sessionBoard`; - `controlPlane/planPanel`. The result schemas contain bounded identifiers, statuses, labels, progress, and summaries. They do not expose a Cordis Context, Service instance, process handle, executable path, environment value, raw log, transcript, or complete Session object. ## Host configuration boundary The Host service accepts a bounded configuration projection. It is empty by default, so a public or clean Profile shows empty panels. A local maintainer may add synthetic or machine-derived scalar summaries through an ignored Profile patch; the public example is [`examples/dsh-control-plane.profile.patch.yml`](../../examples/dsh-control-plane.profile.patch.yml). Stage 3 does not yet wire the Runtime Manager's process lifecycle mutations into the Web UI. Start/stop/health behavior remains behind the Stage 2 allowlisted Adapter API. Stage 4 may add a persistence and plan bridge after the public boundary is reviewed again. ## Client lifecycle and failure behavior Each panel registers only into the additive `settings.plugins.tab` Slot. Each registration is owned by its Cordis effect and returns the Slot disposer. A panel waits for the Slot declaration with `ctx.slots.inject()`; it never registers into `root` or replaces DSH application structure. Each panel also has: - a React error boundary that keeps render failures local; - a cancellable-by-lifecycle data request state, so an unloaded panel cannot update state after its owner is gone; - a generic user-facing error state without transport details; - a Refresh action that only repeats the bounded Remote read. The Remote contribution is mounted with `ctx.remote.$mount()` and disposed when the bundle fiber unloads. Because the Remote namespace is created during the same fiber, panels use the DSH-approved optional lookup `ctx.get("remote.controlPlane")` before invoking it. A missing namespace is a local panel failure, not a process-wide crash. ## Build and validation The public build and checks are: ```sh npm run build:dsh npm test npm run check:public node /scripts/preflight-dsh-extension.mjs . npm pack --dry-run ``` The final Stage 3 acceptance also uses a temporary DSH Web Profile and a temporary tarball install. The checked behavior is: - the tarball installs without `file:packages/*` resolution errors; - DSH Web cold-starts with the single bundle; - all three tabs appear under Plugin settings; - all three empty synthetic panels render without an RPC or render error; - the Host endpoint returns bounded JSON-safe empty projections; - an isolated Profile overlay can disable the bundle (the endpoint becomes unclaimed/404), and removing that overlay plus restarting restores the endpoint and panels; - no API key is entered and no production Profile is used. The preflight scanner may report the JSON Schema standard URL embedded in the bundled Zod runtime. This is a static string in validation metadata, not a runtime network request; it remains documented as a review item rather than being hidden. ## Explicit non-goals Stage 3 does not: - include credentials, private paths, real DSH homes, raw chats, or private knowledge; - execute arbitrary shell from the Host or Client; - make the DSH root, Session store, or knowledge base public; - claim GitHub publication, a Release, or an `awesome-dsh-plugin` PR; - claim that the Runtime Manager and the Web panels already share a durable database.