--- id: walking-skeleton title: Walking skeleton — one real component, end to end through the hosted spine type: prd status: approved author: Danijel Martinek elicitation-session: session_016j8z4VHjedXDTjEDNg7qHK created: 2026-07-12T09:10:00Z --- ## Problem Every Veect risk that matters lives in the seams, not the parts: control plane ↔ runner provisioning, runner ↔ repo toolchain (clone/install/dev-server of code we don't control), and editor ↔ iframe canvas across an origin boundary (ADR-028). The original tech spec front-loaded exactly this with its M0 "walking skeleton" — but its acceptance criteria ("boots with no system Node") died with the Electron distribution (ADR-027). Nothing yet proves the hosted spine works at all, and every subsequent PRD (workspaces, discovery, design-doc, codegen, projects, ai) builds on these seams. If the spine is wrong, we must find out before nine PRDs are stacked on it. ## Goal A signed-in user connects a fixture repo; a cloud runner clones and installs it; discovery finds one real component; the editor renders that component on the board **through the repo's own Vite** in an iframe; clicking it inside the iframe selects it in the editor. One thin, honest, end-to-end vertical. ## In scope - **`workspaces` feature (new, minimal):** `connectWorkspace` (name + git URL + PAT), `getWorkspaceStatus`, `listWorkspaces`; Workspaces Payload collection with encrypted credential storage; runner provisioning + lifecycle status events. - **`discovery` feature (new, minimal):** registry snapshot storage; `getRegistry`; scan-result ingestion from runner events. Skeleton bar: one component with name + props from the fixture (full `ComponentMeta` fidelity comes in `discovery-and-library`). - **`core-runner-protocol` (new core package):** versioned, zod-validated message schemas for the runner protocol (hello/ready, clone, install, scan, adapter-start, render-frame, status/error events) shared by features, editor, and runner. - **`apps/runner` (new app):** the runner process — WS server speaking the protocol; git clone (PAT via ephemeral credential helper); package-manager install; minimal react-docgen-typescript scan; embedded-Vite preview adapter serving a frame host page with the injected **canvas agent script**; status/progress events. - **`packages/editor` (new, minimal):** React Flow board shell, one iframe frame node, canvas-protocol client (selection round-trip), minimal zustand store. First code of the ADR-029 rebuild — stories + tests from day one. - **`apps/web-next`:** workspace list + connect form + staged progress (cloning → installing → scanning → starting preview, the ui-gap §5 pattern); editor route hosting the board; tRPC routers for the two features; realtime push of runner status. - **`fixtures/vite-kitchen` (new):** minimal Vite + React + TS + Tailwind repo with a `Button` component; served to tests via local bare-repo/`git daemon` helpers in `core-testing`. - **Runner provisioning port:** `IRunnerProvisioner` with a local-child-process implementation (dev/CI) and a single-host Docker implementation (the container-per-workspace baseline of ADR-027 §2). - Wire `core-events` (runner lifecycle events) and `core-realtime` (browser push) end to end. ## Out of scope - Checkpoint, publish, worktrees, `projects` feature; design-doc persistence (the skeleton's frame is an ephemeral in-memory doc in the editor). - AI compose, Polish, history, multi-view frames, isolation, code panel. - Next.js adapter (`preview-and-nextjs-depth`); watch/HMR and perf-500 budgets (`discovery-and-library`); full connect UX states — org walls, trust prompt, env manager (`workspaces-and-connections`). - Local runner / CLI agent (`local-runner-cli`); quotas, metering, billing. - Real GitHub/GitLab connections beyond git-URL+PAT against fixtures; runner fleet orchestration, hibernation, egress policy hardening (single-host Docker is the v1 baseline; hardening tracked in `workspaces-and-connections`). ## Constraints - ADR-027: container-per-workspace is the cloud baseline; runner protocol is plain WS/JSON (not tRPC); credentials never in URLs/argv/logs (spec §6 credential-helper rules apply verbatim). - ADR-028: the component renders through the repo's own toolchain in a cross-origin iframe with pinned `targetOrigin`; **no NodeRenderer on the board path**; chrome (selection rings) renders as editor-side overlays from agent-reported geometry. - ADR-029: editor code follows template conventions from the first commit (`component-must-have-story`/`-test`); React 19; no prototype vendoring. - Conformance: manifest-first for every use case in both new features; generator-first (`pnpm turbo gen feature`) for `workspaces` and `discovery`; ADR-022 traces for any new feature/core-tier runtime dep (editor deps pre-approved by `platform-retrofit`). - Budgets (adapted from spec §16): connect-to-rendered-component < 3 min on the warm fixture; board pan/zoom stays editor-side (< 16 ms/frame — no protocol round-trip on drag). - Security floor even at fixture scale: runner containers get no cross-tenant network; the agent script is Veect-authored, adapter-injected, never sourced from the repo (ADR-028 §consequences). ## Success criteria - Full gate set green: `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff`. - e2e (Playwright, local-process provisioner): sign in → connect `vite-kitchen` via git URL + PAT → staged progress completes → editor shows `Button` rendered inside the adapter's iframe (assert on iframe content, not a screenshot double) → click the button in the iframe → editor selection overlay appears and the selection state names the component. This is the M0-recast acceptance. - The same flow works with the Docker provisioner on a dev machine (documented manual check; CI uses the process provisioner). - Protocol integration suite passes against a real spawned runner + local git fixture: clone → install → scan returns ≥1 component → adapter serves the frame host with the agent script. - Credential never appears in any log, process argv, or `.git/config` in the runner workspace (asserted by the integration suite, per spec §15). - Both feature manifests declare their use cases, events, and realtime channels; boot assertion green in dev-seed and production modes. ## User stories 1. As a signed-in user, I want to connect a repository by git URL + token, so that Veect can turn it into a design surface. 2. As a user, I want staged, honest progress while my repo clones and installs, so that a minutes-long operation never looks hung. 3. As a user, I want the editor to show my repo's real component rendered by my repo's own toolchain, so that what I see is exactly what ships (invariant #1). 4. As a user, I want to click a component on the canvas and see it selected, so that direct manipulation is proven across the iframe boundary. 5. As a user, I want a clear, named error state when clone or install fails (bad URL, bad token, install failure), so that I know the cause and next action — never a blank board. 6. As the control plane operator, I want each workspace's repo code executing in its own isolated runner, so that a malicious repo cannot touch other tenants (ADR-027). 7. As an AI implementer, I want the runner protocol schemas in one shared package, so that features, editor, and runner cannot drift apart silently. 8. As a developer, I want the whole skeleton to run with a local-process runner and dev-seed mode, so that `pnpm dev` + fixtures need no cloud resources. ## Implementation decisions - **Protocol:** WS + JSON messages, every message zod-parsed against `core-runner-protocol` schemas, envelope carries `protocolVersion: "0"` and workspace-scoped auth token. Progress is event-streamed (stage + elapsed), mirroring the ui-gap §5 staged-progress pattern. - **Provisioning port:** `IRunnerProvisioner` (service port in `workspaces` infrastructure): `provision(workspaceId) → RunnerHandle`, `terminate`, `status`. Implementations: local child process (dev/test) and Docker single-host (dockerode or docker CLI — app/infra-tier choice at implementation time). Fleet orchestration is explicitly a later concern behind the same port. - **Credential handling:** PAT stored in the Workspaces collection encrypted AES-256-GCM with a key derived (scrypt) from `VEECT_SECRET` (the spec §13 Docker mechanism, now the primary); decrypted only server-side, passed to the runner over the authenticated protocol channel, delivered to git via ephemeral credential helper (spec §6 mechanics verbatim). The Payload API never returns the credential (write-only field). - **Runner internals (app-tier, imperative code is expected here):** clone → detect package manager → install (progress events) → scan (react-docgen-typescript over the fixture's component glob) → start embedded Vite rooted at the clone with a Veect frame-host entry that imports the requested component and mounts the agent script. Dedicated port = dedicated origin per adapter (spec §9). - **Canvas agent v0:** reports `runtime.ready`, component bounding boxes (post `fonts.loaded`), and click-target resolution; editor overlays selection ring from reported geometry. Pinned `targetOrigin` both directions. - **Editor v0:** React Flow board with one frame node hosting the adapter iframe; zustand store holds registry + selection only (no document tree yet — DesignDoc v1 arrives with `design-doc-and-editor-foundation`); the frame renders a single Element (the discovered component with default props) requested via `render-frame`. - **Events/realtime:** runner lifecycle (`workspaces` publishes `runner-status-changed`; realtime channel pushes stage progress + adapter-ready to the browser). Registry snapshots land via `discovery` ingesting the scan event — cross-feature reaction through `core-events`, per rule E0. - **Manifests:** `workspaces` declares `connectWorkspace` (mutates, audits: workspace-connected), `getWorkspaceStatus`, `listWorkspaces`, publishes runner events, realtime channel; `discovery` declares `getRegistry`, consumes the scan event. Coverage bands: template defaults for both features (use cases are thin orchestration; the heavy imperative code lives in `apps/runner`, covered by the integration suite — the honest-bands rationale from the grill session). - **Fixtures:** `fixtures/` at repo root, outside pnpm workspace and turbo graph; `core-testing` gains helpers to serve a fixture as a bare repo over the git protocol for tests. ## Testing decisions - **Use-case unit tests** for every declared use case (conformance-mandated), mocks injected directly. - **Repository contract suite** for the Workspaces repository (mock + Payload impls) including the write-only credential field behavior. - **Protocol integration suite** (the load-bearing one): spawns a real runner (process provisioner) against a `git daemon`-served `vite-kitchen`, drives clone→install→scan→adapter over real WS, asserts registry payload, adapter origin liveness, staged events, and credential-leak absence (grep argv/logs/`.git/config`). Runs in CI on ubuntu. - **Editor:** stories + component tests for board/frame/overlay per conformance rules; canvas-protocol client unit-tested against a scripted agent double. - **e2e:** the Playwright spec described in Success criteria, dev-seed + process provisioner, tagged as the repo's flagship spec. - **Good test** here means: assert through the protocol and the UI, never against runner internals — the runner must stay refactorable while the protocol is the contract. ## Open questions - Q1: dockerode vs docker CLI for the Docker provisioner? — **docker CLI via execa** (fewer moving parts, app-tier so no trace required); revisit with fleet work. - Q2: Does the skeleton editor live at a real route (`/w/[workspace]`) or a dev-only page? — **Real route**, minimal; it becomes the editor's permanent home and later PRDs extend it. - Q3: Runner base image? — **node:22-bookworm-slim + git**, pinned digest; doctor-style version checks come with `workspaces-and-connections`. ## Out of scope (deferred) - Everything listed per-PRD in Out of scope above; plus runner resource limits tuning, egress allowlisting beyond Docker network defaults, and workspace hibernation — tracked in `workspaces-and-connections`. ## Further notes - Builds on: `platform-retrofit` (clean floor, traces, scaffolded cores). Blocks: every subsequent Veect PRD. - Authority: ADR-027/028/029; tech spec §6 (credential mechanics), §9 (adapter/protocol), §15 (fixture + leak-assertion testing), §16 (budgets); ui-gap §5 (staged progress pattern). - The spec's M0 acceptance ("no system Node", "`git status` untouched") is superseded by this PRD's Success criteria — hosted equivalents: isolated runner, credential-leak assertions, honest progress.