Workspace Composition Surface Migration Map
WCP-014 turns the current Claude UI source surfaces into an executable migration map. It does not move React code. It records what each surface should become when @maya/claude-workspace packages product defaults and @maya/assistant-ui renders a WorkspaceComposition.
The map is backed by tools/workspace-surface-migration-map.mjs and verified by pnpm verify:surface-migration-map.
Locked Invariants
Host resolves runtime.
Composer composes structure.
Render runtime executes UI.
ClaudeWorkspace packages product defaults.
Modules are trusted React UI adapters, not state/runtime owners.
Linked decisions: WCP-ADR-011, WCP-ADR-012, WCP-ADR-013, WCP-ADR-014.
First Safe Visual Parity Milestone
Descriptor-To-Render Shell Parity is the first safe visual parity milestone.
Scope: route, sidebar, settings, and customize shell selection may be driven from WorkspaceComposition, but handles must resolve to the existing Claude UI React surfaces and existing host-owned controllers. The milestone is safe only if navigation, route rendering, Settings, Customize, transcript, and fallback visuals remain unchanged.
Acceptance:
- No second route table, global render registry, or built-in page patch path is introduced.
- WCP-013 descriptor counts still match current Claude UI metadata.
- Current Chat, Projects, Customize, Settings, Sidebar, package-boundary, and docs/DX gates pass.
- Public module route publishing waits until this milestone is visually stable.
Verification:
pnpm verify:claude-builtins-fixture-paritypnpm verify:surface-migration-mappnpm verify:chat-surfacepnpm verify:projects-visualpnpm verify:customize-surfacepnpm verify:settings-surfacepnpm verify:sidebar-surfacepnpm verify:package-boundary:builtpnpm verify:docs-dx
Surface Map
| ID | Current surface | Target category | Risk | Migration kind |
|---|---|---|---|---|
route-metadata-and-paths | workspaceRouteMeta, href helpers, matchers, route capability checks | routes, sidebarItems | Medium | descriptor-first: move identity and metadata first; keep execution in existing components until render runtime lands. |
route-render-switch | WorkspaceMainSurface and RouteSurface choose conversation, Chat history, Projects list/detail, Customize, product, and Generic surfaces | routes, ReactRenderPack | High | handle-first-with-existing-components: replace switch selection with route render handles only after the descriptor shell can render with visual parity. |
generic-product-routes | New chat, Artifacts, Code, Design, and artifact preview surfaces currently folded into GenericRouteSurface | routes, ReactRenderPack | Medium | defer-component-split: keep component splitting deferred; do not overfit handles before product ownership is moved. |
sidebar-primary-product-navigation | Primary/product sidebar nav derived from route metadata | sidebarItems, routes | Medium | descriptor-first: compose navigation descriptors; preserve capability filtering and active state. |
sidebar-host-owned-history-and-account | Recents, project shortcuts, sidebar search, account menu, and Add menu | hostOwnedControllerState, notV0ContributionSurface | High | keep-product-shell-internal: keep inside Claude product shell; do not expose arbitrary sidebar slots in v0. |
settings-section-inventory | Settings hashes, section routes, nav labels, and Customize links | configurationSurfaces | Medium | descriptor-first: descriptor-first migration with hash parity. |
settings-dialog-rendering | SettingsDialog section switch and SettingsShell chrome | configurationSurfaces, ReactRenderPack | High | shell-first-render-handle: keep shell state and focus behavior local; render sections through handles after parity. |
settings-search-actions-and-draft-state | Search query/results, draft save state, data export, action receipts, allowed-domain input | hostOwnedControllerState, notV0ContributionSurface | High | keep-host-owned: host owns persistent settings and actions; modules may render granted surfaces only. |
customize-views-and-cards | Customize overview, Skills, Connectors, product cards, personal plugins panel | integrationSurfaces, ReactRenderPack | High | descriptor-first-with-shell-internal-state: model semantic integration surfaces without binding modules to dialog or route placement. |
customize-directory-and-custom-connector | Directory catalog and custom connector dialogs | integrationSurfaces, hostOwnedControllerState | Medium | integration-surface-with-host-controller: dialog UI stays an integration surface; remote URL, OAuth client id, OAuth client secret, create/status/browse behavior remain host-owned controller data. |
transcript-default-message-rendering | Transcript, TranscriptMessageLog, MessageRow, default Markdown, and assistant error panel | messageRenderers, notV0ContributionSurface | High | protect-default-renderer: protect ordinary user/assistant text rendering; only custom/error claims are descriptor-addressable in v0. |
message-appenders | Attachments, protocol stack, artifacts, version controls, and message action controls | messageAppenders | High | appenders-after-default-renderer: compose cards around default rendering; preserve order, read-only behavior, and host-owned action authority. |
message-host-owned-controls | Attachment, artifact, edit, feedback, read-aloud, retry, version-change, and activity action callbacks | hostOwnedControllerState, notV0ContributionSurface | High | keep-host-owned: render handles may show controls, but host controllers own action authority. |
protocol-tool-cards | Claimed tool use/result cards for web_search and project_knowledge_search | toolRenderers, messageAppenders | Medium | claim-by-tool-name: claim renderer by explicit tool name; generic tool fallback stays inside the built-in protocol stack appender. |
generic-tool-fallback-in-protocol-stack | Generic protocol tool labels and generic tool result labels for tool names without v0 renderer claims | messageAppenders, notV0ContributionSurface | Medium | keep-inside-protocol-stack-appender: keep generic tool fallback inside the built-in protocol stack appender. |
protocol-activity-cards | Activity lifecycle cards, approval/resume actions, and activity metadata | activityRenderers, messageAppenders | Medium | claim-by-activity-kind: claim renderer by activity kind; host keeps action authority. |
protocol-side-cards | Citation, interaction, usage-limit, and stop-reason cards | messageAppenders, notV0ContributionSurface | Medium | keep-inside-protocol-stack-appender: keep inside the built-in protocol stack appender until a separate claim taxonomy is justified. |
workspace-status-and-non-ready-fallbacks | Workspace shell status, retry affordance, and module non-ready state fallbacks | activityRenderers, fallbackSurfaces | Medium | fallback-descriptor-with-host-status: render status and fallback handles, but runtime status decisions stay host-owned. |
default-composition-policy | Claude ordering plus explicit hide/reorder/replace/pathAlias/rendererOverride policy | CompositionPolicy | Medium | explicit-policy-only: keep policy explicit and diagnostic-driven; do not let it become a patch language. |
Explicit Gaps
These are not v0 extension points:
| ID | Why it stays out of v0 |
|---|---|
public-built-in-page-replacement-api | Built-in page replacement needs stable route context and owner-approved parity. Policy can model replacement for diagnostics, but no public API exposes it yet. |
default-user-assistant-text-override | Ordinary user/assistant text rendering is protected by WCP-ADR-012. |
dynamic-and-hash-route-contributions | /chat/:id, /project/:id, and /new#settings/... are host/router view state in v0, not public module route paths. |
arbitrary-sidebar-slots | Recents, account, Add menu, search, and project shortcuts mix host-owned data, account actions, and settings mutation. |
settings-search-provider-extension | Search currently indexes built-in metadata; module-search contribution needs host-owned filtering and stable searchable metadata. |
project-detail-subroute-extension | Project detail list/detail and knowledge/source tabs are product-internal, controller-heavy surfaces. Modules can add their own pages in v0. |
protocol-stack-sub-card-claim-taxonomy | Citations, stop reasons, usage notices, and interaction cards remain under the built-in protocol stack appender until tool/activity/message claims prove stable. |
generic-tool-renderer-claim | Unknown tool names keep generic visual fallback inside the built-in protocol stack; v0 only publishes explicit claims for web_search and project_knowledge_search. |
module-owned-persistent-settings-or-storage | Modules are UI adapters; host owns persistence, network, credentials, and audit behavior. |
runtime-untrusted-plugin-loading | Phase one is trusted build-time npm React modules only. No marketplace, iframe, remote ESM, module federation, or runtime unknown-code install path exists. |
suspense-loading-as-runtime-fallback | WorkspaceSurfaceFallback and route Suspense placeholders are React loading states, not host-resolved module runtime fallbackSurfaces. |
Risk Ranking
High product-impact migration areas:
route-render-switchsettings-dialog-renderingsettings-search-actions-and-draft-statecustomize-views-and-cardstranscript-default-message-renderingmessage-appendersmessage-host-owned-controlssidebar-host-owned-history-and-account
Medium product-impact migration areas:
route-metadata-and-pathsgeneric-product-routessidebar-primary-product-navigationsettings-section-inventorycustomize-directory-and-custom-connectorprotocol-tool-cardsgeneric-tool-fallback-in-protocol-stackprotocol-activity-cardsprotocol-side-cardsworkspace-status-and-non-ready-fallbacksdefault-composition-policy
No low-risk migration is claimed in WCP-014. The map is intentionally conservative because these surfaces are user-visible and controller-rich.
Source Anchors
The verifier checks that these source anchors still exist:
projects/assistant-composition/src/policy.tsprojects/assistant-composition/test/fixtures/wcp-010/fixture-policy-all-operations.jsonprojects/assistant-composition/test/fixtures/wcp-013/fixture-claude-builtins-default-composition.jsonprojects/claude-ui/src/components/chat/Transcript.tsxprojects/claude-ui/src/components/chat/TranscriptMessageLog.tsxprojects/claude-ui/src/components/directory/ProjectCustomConnectorDialog.tsxprojects/claude-ui/src/components/directory/ProjectDirectoryDialog.tsxprojects/claude-ui/src/components/directory/projectCustomConnectorForm.tsprojects/claude-ui/src/components/message/MessageActions.tsxprojects/claude-ui/src/components/message/MessageArtifactCards.tsxprojects/claude-ui/src/components/message/MessageAttachmentCards.tsxprojects/claude-ui/src/components/message/MessageProtocolCards.tsxprojects/claude-ui/src/components/message/MessageRow.tsxprojects/claude-ui/src/components/message/MessageVersionControls.tsxprojects/claude-ui/src/components/message/protocolActivityCards.tsxprojects/claude-ui/src/components/message/protocolActivityMetadata.tsprojects/claude-ui/src/components/message/protocolCitationCards.tsxprojects/claude-ui/src/components/message/protocolInteractionCard.tsxprojects/claude-ui/src/components/message/protocolLimitCard.tsxprojects/claude-ui/src/components/message/protocolStopReasonCard.tsxprojects/claude-ui/src/components/message/protocolToolCards.tsxprojects/claude-ui/src/components/settings/SettingsDialog.tsxprojects/claude-ui/src/components/settings/SettingsSearchNav.tsxprojects/claude-ui/src/components/settings/SettingsShell.tsxprojects/claude-ui/src/components/settings/metadata.tsprojects/claude-ui/src/components/workspace/AddMenu.tsxprojects/claude-ui/src/components/workspace/ArtifactPreviewPanel.tsxprojects/claude-ui/src/components/workspace/ArtifactVersionStrip.tsxprojects/claude-ui/src/components/workspace/ConnectorsSurface.tsxprojects/claude-ui/src/components/workspace/CustomizeOverviewSurface.tsxprojects/claude-ui/src/components/workspace/GenericRouteSurface.tsxprojects/claude-ui/src/components/workspace/ProductSurface.tsxprojects/claude-ui/src/components/workspace/SidebarNavigation.tsxprojects/claude-ui/src/components/workspace/SkillsSurface.tsxprojects/claude-ui/src/components/workspace/WorkspaceMainSurface.tsxprojects/claude-ui/src/components/workspace/WorkspaceRouteSurface.tsxprojects/claude-ui/src/components/workspace/WorkspaceSidebar.tsxprojects/claude-ui/src/components/workspace/WorkspaceStatus.tsxprojects/claude-ui/src/components/workspace/WorkspaceStatusContent.tsxprojects/claude-ui/src/components/workspace/sidebarAccountMenu.tsxprojects/claude-ui/src/components/workspace/workspaceLazySurfaces.tsxprojects/claude-ui/src/types/components/transcript.tsprojects/claude-ui/src/workspaceRoutes.ts
Verification Contract
pnpm verify:surface-migration-map validates:
- manifest schema, owner, invariants, decision links, surface counts, and unsupported gap counts
- every mapped surface has target categories, risk, source anchors, and verification commands
- the Markdown surface table's ID, target categories, risk, and migration kind match
tools/workspace-surface-migration-map.mjs - every source file and named symbol in
tools/workspace-surface-migration-map.mjsstill exists through TypeScript AST or JSON key/id checks where possible - this document contains every mapped surface ID, explicit gap ID, and manifest source path
- WCP-013 built-in descriptor fixture counts and message/tool/activity/fallback IDs remain aligned with the map
- current Settings metadata still exposes eight primary navigation sections, three Customize links, forty-nine search items, eleven settings hashes, three Customize views, and forty-four required render handles
- message rendering order still keeps default text/error before appenders, protocol cards before artifacts/version controls/actions, read-only controls hidden, host-owned message callbacks present, generic tool fallback present, and protocol stack cards ordered activity, tool use, tool result, citation, interaction, limit, stop
The Markdown is the human-readable record; tools/workspace-surface-migration-map.mjs is the machine-readable checklist. Do not create a second migration table in a package root, generated docs folder, or copied README. This keeps no second documentation truth for WCP-014.