Skip to content

ADR 0008: Builder Frontend — Ella Chat Stream, Build Runtime, Spec Renderer

Accepted

Accepted

2026-11-06

The API now serves the Builder (/v1/builder/*) and Ella’s build chat (POST /v1/builder/sessions/:id/chat), an AI SDK v7 UI message stream over server-sent events. Each turn streams text, tool parts (question chips come from askUser) and data parts: data-build-step (the live checklist), data-build-spec, data-build-patch (the “Done” change lines), data-build-checks, data-build-suggestions and data-build-turned-away. A build is a JSON Build Spec validated by @cosella/build-runtime; its numbers come from running its data sources through POST /v1/builder/builds/:id/query.

Constraints:

  • createAuthedFetch aborts every request after 30 seconds. A build turn can stream for two minutes.
  • Specs are data, never code. The client must draw any valid spec, and must not break on a component it does not know yet.
  • The prototype (cosella-design-mocks, pages/cosella/dashboard/builder) defines the screens and copy: Builder home, the full-screen build workspace, the live feature page.

@ai-sdk/react useChat with a DefaultChatTransport pointed at the session’s chat endpoint. Its fetch is createStreamingFetch from @cosella/api-client: the same token refresh and single 401 retry as createAuthedFetch, without the timeout, cancelled only by the caller’s signal. Only the new message’s id and text are sent; the server owns the history, tool results and data parts.

use-build-chat reads data parts from each message and the hook exposes plain state: the checklist, the pending question, the changes, the latest Checks, the suggestions and the current draft. Suggestion chips carry a pre-computed patch and apply through PATCH /draft with no model call.

@cosella/build-runtime is vendored as a tarball at vendor/ and consumed with file:, as the API does, until the runtime repo publishes tagged releases. The frontend uses its types, COMPONENTS and measureKey; validation stays on the server.

features/builder/renderer draws a spec: a registry maps each component type to a React component, data-bound elements read their source’s query result by column, and every element sits in its own error boundary. Types without an implementation render a quiet placeholder, never a crash. Charts use Recharts, already in the catalog. The renderer lives in the dashboard for now; it moves to a package when share and embed pages or the admin app need it.

  • The chat keeps streaming past the usual request timeout, and still refreshes tokens.
  • New component types reach the dashboard one registry entry at a time, without breaking builds that already use them.
  • A runtime change means re-vendoring the tarball in both the API and the frontend.