Records the 2026-07-12 grill-session decisions repurposing this repo for Veect v1 as a hosted SaaS: ADR-027 (hosted distribution + control-plane/runner split), ADR-028 (iframe canvas), ADR-029 (DesignDoc v1 canonical + editor rebuild). Seeds the work pipeline with the platform-retrofit and walking-skeleton PRDs (approved) and adds the Veect product-domain section to the glossary. These consciously supersede the .proto bundle's Electron/local-first distribution decisions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
13 KiB
id, title, type, status, author, elicitation-session, created
| id | title | type | status | author | elicitation-session | created |
|---|---|---|---|---|---|---|
| walking-skeleton | Walking skeleton — one real component, end to end through the hosted spine | prd | approved | Danijel Martinek | session_016j8z4VHjedXDTjEDNg7qHK | 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
workspacesfeature (new, minimal):connectWorkspace(name + git URL + PAT),getWorkspaceStatus,listWorkspaces; Workspaces Payload collection with encrypted credential storage; runner provisioning + lifecycle status events.discoveryfeature (new, minimal): registry snapshot storage;getRegistry; scan-result ingestion from runner events. Skeleton bar: one component with name + props from the fixture (fullComponentMetafidelity comes indiscovery-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 aButtoncomponent; served to tests via local bare-repo/git daemonhelpers incore-testing.- Runner provisioning port:
IRunnerProvisionerwith 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) andcore-realtime(browser push) end to end.
Out of scope
- Checkpoint, publish, worktrees,
projectsfeature; 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) forworkspacesanddiscovery; ADR-022 traces for any new feature/core-tier runtime dep (editor deps pre-approved byplatform-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-kitchenvia git URL + PAT → staged progress completes → editor showsButtonrendered 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/configin 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
- 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.
- As a user, I want staged, honest progress while my repo clones and installs, so that a minutes-long operation never looks hung.
- 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).
- 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.
- 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.
- 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).
- As an AI implementer, I want the runner protocol schemas in one shared package, so that features, editor, and runner cannot drift apart silently.
- 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-protocolschemas, envelope carriesprotocolVersion: "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 inworkspacesinfrastructure):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 (postfonts.loaded), and click-target resolution; editor overlays selection ring from reported geometry. PinnedtargetOriginboth 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 viarender-frame. - Events/realtime: runner lifecycle (
workspacespublishesrunner-status-changed; realtime channel pushes stage progress + adapter-ready to the browser). Registry snapshots land viadiscoveryingesting the scan event — cross-feature reaction throughcore-events, per rule E0. - Manifests:
workspacesdeclaresconnectWorkspace(mutates, audits: workspace-connected),getWorkspaceStatus,listWorkspaces, publishes runner events, realtime channel;discoverydeclaresgetRegistry, consumes the scan event. Coverage bands: template defaults for both features (use cases are thin orchestration; the heavy imperative code lives inapps/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-testinggains 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-servedvite-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 statusuntouched") is superseded by this PRD's Success criteria — hosted equivalents: isolated runner, credential-leak assertions, honest progress.