Files
agentic-dev/docs/work/prds/walking-skeleton.prd.md
Danijel Martinek 442b70476c docs(work): add Veect ADRs 027-029, glossary terms, first two PRDs
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
2026-07-12 12:17:35 +02:00

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

  • 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.