CLAUDE.md
August 12, 2026 · View on GitHub
This is the render half of A2UI. The emission half (where things
usually break) is backend — see backend/adk/CLAUDE.md and the memory
a2ui-workspace-render-trap. Read those first when a surface "won't render."
Rules
- No bespoke per-tool React. Every tool result / surface renders through the
generic
A2UISurfaceMount(→@a2ui/react). If you're about to write a component keyed off a specific tool name, STOP and register a backend result→A2UI mapping instead (backend/adk/a2ui_result_render.py). - Surfaces flow: backend
A2UI_SURFACECUSTOM event →WorkspaceA2uiEventRouter→SurfaceRegistry.appendMessages(surfaceId, …). The router MUST run insideSurfaceRegistryProvider(it reads the registry).appendMessagesauto-synthesizescreateSurface, so a mount isn't required for the surface state to exist. - Tabs & index come from
artifactmetadata.useArtifacts()lists surfaces that have BOTH a live surface ANDartifactmetadata; each becomes a workbench Result tab (7.5) and a row in the Workspace/Home index (WorkbenchHome/WorkbenchIndex).placement: "chat"renders inline in the transcript instead (elicitation forms), NOT as a tab. - Auto-focus new workbench elements (repo principle #7) — a new artifact
surface focuses its tab; a dominant
workspacesurface focuses the Workspace tab. Don't just badge.
Verify in a real browser
A surface passing jsdom tests can still render blank live (grounding-redirect
URLs, a missing catalog component, the surface never registering). Confirm with
the aitana-frontend-verify skill (chrome-devtools MCP) — rendered DOM +
the A2UI_SURFACE event on the network/SSE stream — before calling it done.