ZEROlobby
22/22 strict⌘Kvisitor
skip to main
ADR-033
skin
——:——:——zcovenant · audit
a2ui· @zero/a2ui · 16 kinds · 1 registry · 1 reducer

an agent composes the brand from a flat list

A2UI is the streaming-UI sibling of A2A: the agent emits a flat array of typed nodes (each with an id and a children-by-id list). the renderer reconstructs the tree, so the agent can append nodes mid-stream without re-sending the entire surface. ZERO restricts the component vocabulary to 16 kinds — every kind maps to a primitive in @zero/ui. there is no "anything goes," there is no inline style, and there is no HTML escape hatch.

00 · livePOST /api/a2ui-stream · paced deltas · primitives reduced into a tree

watch the agent compose a surface in front of you

compose · live· /api/a2ui-stream · 1 nodes · 0 deltasidle
01 · vocabulary16 kinds · closed by construction

every kind maps to a brand primitive

plate · A · A2UiKind · the closed component vocabulary16 kinds
metric
a2ui
unit
nodes
window
static · stream
agg
count
source
packages/a2ui/src/types.ts
fresh
last build
groupkindmaps to
layoutstack vertical flex column
row horizontal flex row
card @zero/ui · Card
sectionTitle brand-voice h2
contenttext plain text node
mark @zero/ui · Mark
chip @zero/ui · Chip
hash @zero/ui · Hash
receipt @zero/ui · Receipt
statCell @zero/ui · StatCell
actionbutton @zero/ui · Button
approvalGate @zero/ui · ApprovalGate
toolCallCard @zero/ui · ToolCallCard
asyncTask @zero/ui · AsyncTask
planSurface @zero/ui · PlanSurface
streamingCursor@zero/ui · StreamingCursor
02 · stream8 deltas · build the tree step by step

each delta is a single, idempotent operation

plate · B · A2UiDelta sequence · stream that built the panel above8 deltas
metric
a2ui
unit
nodes
window
static · stream
agg
count
source
STREAM
fresh
last build
iopid / parentkindtree size after
01appendheader · parent rootsectionTitle2 nodes
02appendrow-stats · parent rootrow3 nodes
03appends-pnl · parent row-statsstatCell4 nodes
04appends-ref · parent row-statsstatCell5 nodes
05appendcard-1 · parent rootcard6 nodes
06appendr-trade · parent card-1receipt7 nodes
07appendcta · parent rootbutton8 nodes
08appendsig · parent roothash9 nodes
upsert semantics
appending a node whose id already exists upserts the node (it replaces in place, no duplication). this lets the agent incrementally refine a node's props as new information arrives, without rewriting the whole subtree.
03 · registrythe kind-to-primitive registry · single source of truth

adding a kind requires three changes, in order

plate · C · extending A2UI · the protocol-version contractschema · a2ui.v1
metric
a2ui
unit
nodes
window
static · stream
agg
count
source
packages/a2ui/src/types.ts
fresh
last build
stepfilechange
01packages/a2ui/src/types.tsadd the kind to A2UiKind union and A2UI_KINDS array
02packages/a2ui/src/registry.tsxadd a renderer in REGISTRY · must map to a brand primitive
03governance/adr/NNN-*.mdopen an ADR · bump the protocol schema if the change is not additive
04packages/a2ui/src/__tests__/a2ui.test.tsxadd a render test for the new kind · CI gate green
04 · covenantclosed vocabulary · no escape hatches · brand by construction

the brand cannot leak through the renderer

why no html escape hatch
an "html" kind would let any agent render any markup, which would let any agent ship arbitrary CSS, which would let any agent break the covenant in 50 ways. the closed registry keeps the contract enforceable: every primitive that lands on the page is one of the 16 brand-validated kinds, with the same accessibility, motion, and spacing contracts as anything elsewhere on the surface.
plate · D · renderA2Ui · enforced posturesigned
metric
a2ui
unit
nodes
window
static · stream
agg
count
source
packages/a2ui/src/render.tsx
fresh
last build
posturevalue
raw html refused
inline style refused
arbitrary class refused
third-party rendererrefused
agent-supplied scriptrefused
kind in registry required
props validated by primitiverequired
closed vocabularybrand-safe◆ a2u001a