<!-- source: openspec/current/canvas.md -->

---
title: Canvas
status: deprecated
owner: pavel
audience: [pavel, ai, engineer]
tldr: Deprecated 2026-05-03. The Canvas v2 React Flow visualization vision is superseded by the Mirror perceptual spec layer.
---
# Canvas

> **Deprecated 2026-05-03.** Replaced by `mirror.md`.

---

## Status: deprecated

The Canvas v2 vision — a real-time React Flow visualization layer for live domain mapping during client conversations — is no longer pursued. The studio captures client-perception truth as a **spec layer** (Mirror) rather than a graph visualization. Renders downstream of Mirror handle visualization needs.

See `mirror.md` for the active capability.

---

## Naming clarification — superseded 2026-05-22 (RAP-1170 Wave 1)

The studio v1 "canvas" surface was renamed end-to-end in the
`rearchitect-spec-document-model` change. The DB tables
(`canvas_sessions`, `canvas_messages`, `canvas_files`,
`canvas_invitees`, `canvas_attachments`, `canvas_instructions`,
`canvas_tool_uses`, `canvas_stage_events`, `canvas_daily_costs`,
`canvas_ideas`) were dropped and replaced by the
project → spec → idea → {messages, files} hierarchy — see
`db/entities.md § Project → spec → idea hierarchy` for the canonical
catalog. The `/spec/[id]` URL was already aligned with the new
vocabulary and is unchanged. The Architect-mode chat surface still
lives there; its DB anchor is now `ideas` instead of
`canvas_sessions`.

The historical references in `muse.md`, `studio-workspace/`, and
`platform.md` to "canvas" describe behavior that has been preserved
across the rename — the **surface** still exists, only the table /
identifier names changed. Each of those files carries its own
RAP-1170 callout where the rename is consequential.

---

## Partial schema landed (retained — pre-Wave 1)

The schema additions made under the canvas heading were absorbed
into the post-Wave-1 schema:

- `canvas_invitees` → `spec_invitees` (access scope moved one level
  up from idea to spec); see `db/entities.md § Project → spec → idea
  hierarchy` for the new shape.
- `canvas_messages.author_id` → `messages.author_id` — same column,
  table renamed.
- `canvas_invitees.persona` → `spec_invitees.persona` — preserved.
- Role-based access — same role enum, now scoped to
  `spec_invitees` + `project_members` (the project-level role
  enforcement layer added in `redesign-canvas-permission-model`).

---

## Why the pivot

The Canvas v2 vision belonged to a "visualization-as-product" mental model — the rendered graph was the deliverable. The studio's wedge is "spec-as-product": the perceptual truth itself is the deliverable, and the render is a deterministic function of (Mirror + OpenSpec). See `mirror.md § Why concept rather than greenfield`.

---

## Related capabilities

- `mirror.md` — successor capability
- `muse.md` — uses "canvas" for the active v1 Architect surface
- `studio-workspace/README.md` — hosts the v1 canvas chat sessions
- `db/README.md` — schema for `canvas_*` tables (still active)

---

## Canvas chat — primitive-hardening contracts (applied 2026-05-16 from change `harden-chat-primitive`)

The v1 canvas chat (`/spec/[id]`, `chatMachine` + `canvas-chat-adapter`)
adopts the hardened `@rapoport-studio/ui` chat primitive contracts — see
`ui.md § Chat primitive hardening`.

- **Optimistic send.** `chatMachine` appends the user's message to
  `context.messages` synchronously on `TURN_REQUEST` (entry action on the
  `sending` state) with `status: "submitted"` and a generated `clientId`.
  The `clientId` round-trips through the `/spec/[id]/turn` SSE POST body
  and is echoed in the `turn_complete` payload; `applyTurnResult` reconciles
  the optimistic row by it on settle.
- **Failed-send retry.** On `STREAM_ERROR` / `TURN_RESPONSE_ERROR` /
  non-ok `TURN_COMPLETE` the optimistic row is kept with `status: "error"`;
  a `RETRY_FAILED` event flips it back to `submitted` in place. `ABORT`
  removes the row (a deliberate cancel is not a delivery failure).
- The adapter projects `status`/`clientId` straight through and emits the
  baseline `thinking` block in the in-flight streaming placeholder.

Source: `openspec/archive/2026-05-16-harden-chat-primitive/`.

---

## Canvas chat — Extended Thinking reasoning (applied 2026-05-16 from change `wire-architect-extended-thinking`)

An architect turn that produced Claude Extended Thinking surfaces its
chain-of-thought in canvas chat as a collapsible `reasoning` block (the
`<ReasoningBlock>` renderer from `harden-chat-primitive` Phase 6).

Flow: `runMuseTurnStream` carries the turn's `TurnReasoning` out via
`RunMuseTurnResult.reasoning`; the `/spec/[id]/turn` SSE route stamps it
onto the in-memory `MessageRow.reasoning` field of the `assistantMessage` in
`SendMessageResult`. `chatMachine.applyTurnResult` already spreads the whole
`assistantMessage` into context, so reasoning needs no machine change.
`canvas-chat-adapter` prepends a `{ kind: "reasoning", … }` content block
when an assistant row carries reasoning.

**Live-only (D-XT-2):** `MessageRow.reasoning` is in-memory only — the
`canvas_messages` INSERT is unchanged, no schema column. History reloads
(and `CHAT.RESYNC`) rebuild `messages` from the DB without reasoning, so the
block is absent for past turns and present only for turns produced in the
live session. Persisting reasoning is a deferred follow-up.

Source: `openspec/archive/2026-05-16-wire-architect-extended-thinking/`.

---

## Files API backing for attachments (applied 2026-05-16 from change `files-api-for-client-intake-canvas`)

Client briefs/PDFs at intake and Canvas file attachments are uploaded once via the Claude Platform Files API and referenced by `provider_id` instead of re-embedding bytes on every request. A typed Files API wrapper lives in `apps/studio/src/lib/files/`; uploaded files are tracked in the `ai_files` Supabase table (≤30-day TTL, hourly retention sweep). A ZDR guard rejects uploads when the request context is `zdr_required`. Source: `openspec/archive/2026-05-16-files-api-for-client-intake-canvas/`.

---

## Spec citations via Search results API (applied 2026-05-16 from change `spec-citations-in-canvas-via-search`)

Canvas answers about OpenSpec capabilities now carry citations. `openspec/current/*.md` is chunked into a build-time search index (`apps/studio/src/generated/openspec-search-index.ts`, regenerated by `tools/index-openspec.mjs` in postinstall — gitignored, not committed). `buildSearchResults` feeds ranked chunks to the chat completion via the Claude Search results API; returned citations render as inline deep links into the in-app spec viewer. The Canvas search/tools path is ZDR-constrained — an ESLint rule forbids persistence imports there. Source: `openspec/archive/2026-05-16-spec-citations-in-canvas-via-search/`.

---

## Canvas chat — action-workflow actor module (applied 2026-05-20 from change `extract-actions-machine`)

Canvas chat's six non-turn action workflows (`promote`, `convert`, `saveAll`, `openPR`, `saveArtifact`, `saveFile`) ship their server-action actor wrappers and action-domain types in `apps/studio/src/app/(auth-walled)/(canvas)/spec/[id]/machines/actions.machine.ts`. See `state.md § Action-workflow actors` for the pattern contract.

Source: `openspec/archive/2026-05-20-extract-actions-machine/`.

---

## Chat e2e verification contract (applied 2026-05-21 from change `add-chat-e2e`)

The v1 canvas chat send flow (`/spec/[id]`) carries end-to-end coverage.
`apps/studio/e2e/chat-send-flow.spec.ts` exercises six scenarios — happy
path, failed-network, retry, abort-mid-stream, queue-while-streaming,
reconnect-mid-turn — replaying captured SSE streams from
`apps/studio/e2e/sse-fixtures/`. A synthetic monitor
(`.github/workflows/chat-synthetic.yml`, hourly cron) measures TTFT /
throughput / settle latency against staging and alerts on p95 TTFT
> 600 ms or throughput < 100 tok/s via Telegram `#alerts` plus a Sentry
`chat.synthetic.slo_breach` breadcrumb.

Source: `openspec/archive/2026-05-21-add-chat-e2e/`.

---

## Chat message-list virtualization contract (applied 2026-05-21 from change `polish-chat-virtualization`)

The canvas chat message list virtualizes via TanStack Virtual on
`[data-slot="message-list-inner"]` once a transcript reaches the
100-message threshold. Because virtualized rows mount/unmount, keyboard
navigation (`j`/`k`) is preserved by a parent-owned tabindex coordinator
with an out-of-flow focus anchor, so focus survives row recycling across a
1000-message transcript. Sibling `←`/`→` chevrons and empty-state
suggestion chips are additive surface — see `ui.md`.

Source: `openspec/archive/2026-05-21-polish-chat-virtualization/`.

## Canvas vocabulary retirement complete (applied 2026-05-28 from change `retire-canvas-vocabulary`, RAP-1252)

The Canvas vocabulary across the workspace surface is fully retired as of 2026-05-28. The working-session entity is `idea`; the deliverable document set is `spec`; the user-facing word is **Spec** per D-CV-1.

- Studio code identifiers tracking the working session use `idea*` (`apps/studio/src/components/idea/`, `IdeaView`, `canvasId → ideaId`, etc.); identifiers tracking the deliverable doc set use `spec*` (`spec_invitees`, `SpecInvitation`).
- User-facing copy in the studio (sidebar, knowledge dashboard, settings, error pages, dialogs) renders **Spec** — no `canvas` survives in JSX text under `apps/studio/src/`.
- `packages/canvas` was renamed to `packages/spec` with subpath exports `@rapoport-studio/spec/{views,wizard}`.
- The Atlas guard set was extended with two `grep-forbidden` rules under D-ATLAS-5 (D-CV-3): CV-A (no `Canvas*` symbol declarations under `apps/studio/src/`) and CV-B (no `canvas`/`canvases` JSX text under `apps/studio/src/`).
- `openspec/_root/glossary.md § Appendix 2` lists `Canvas` as a banned bare term with the D-CV-1 replacement.

**This `canvas.md` capability file stays `status: deprecated`**; the Canvas v2 React Flow visualization vision is unchanged (still superseded by Mirror). Only the *word* `canvas` is retired from the live vocabulary; this file is the historical record of the deprecated visualization product.

Sources:
- `openspec/archive/2026-05-28-retire-canvas-vocabulary/` (archived 2026-05-28 as `openspec/archive/2026-05-28-retire-canvas-vocabulary/`).
- `openspec/_atlas/current/decisions.yaml § D-ATLAS-5` (extended with CV-A + CV-B guards).
- Linear: RAP-1252.
