-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Problem
The A2UI spec cleanly separates component structure (updateComponents) from component data (updateDataModel). Components bind to data model values via DataBinding — a JSON Pointer path into the data model.
However, the data model is completely untyped. The updateDataModel message accepts any JSON value at any path:
"updateDataModel": {
"surfaceId": "string",
"path": "string",
"value": "any" // ← no schema, no constraints
}This works fine for simple components with inline properties (like Text or Button in the basic catalog). But for complex data-bound components — where a component binds to a rich configuration object via DataBinding — there is no spec-level way for a catalog to declare what shape of data the component expects.
Why this matters
- LLM agents generate data model values with no schema guidance. Common errors include missing required fields, wrong types, broken enum values, and malformed nested structures. These errors are only caught at the client renderer — too late for the agent to self-correct.
- Validators check component tree topology (IDs, cycles, orphans, recursion limits, path syntax) but have zero visibility into data model content. Invalid data passes all 5 validation levels.
- Clients receive
updateDataModelpayloads with no way to validate before rendering. - Catalog authors can define complex data expectations but must resort to description strings or proprietary extensions — nothing machine-readable.
- Tooling (TypeScript generators, mock data generators, documentation tools) cannot extract data shape information from catalogs.
Example
A catalog defines a ContactCard component that binds to a config object:
"ContactCard": {
"properties": {
"component": { "const": "ContactCard" },
"configuration": {
"$ref": "common_types.json#/$defs/DataBinding",
"description": "Path to a ContactCardConfig object in the data model."
}
}
}The component expects an object with name (string), email (string, format: email), and role (enum) at the bound path — but that expectation lives only in a description string. The spec has no mechanism to express it formally.
Proposed Solution
Two additions to the catalog format, with no wire protocol changes:
1. configSchemas — a new top-level catalog section
A map of schema name to JSON Schema (Draft 2020-12), declaring the expected shapes of data model values:
{
"catalogId": "urn:example:my-catalog:v1",
"components": { ... },
"functions": { ... },
"configSchemas": {
"ContactCardConfig": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"role": { "type": "string", "enum": ["engineer", "manager", "designer"] }
},
"required": ["name", "email", "role"]
}
}
}2. expectedSchema — an annotation on DataBinding properties
A machine-readable link from a component's binding property to its expected data shape:
"ContactCard": {
"allOf": [
{ "$ref": "common_types.json#/$defs/ComponentCommon" },
{
"properties": {
"component": { "const": "ContactCard" },
"configuration": {
"$ref": "common_types.json#/$defs/DataBinding",
"expectedSchema": { "$ref": "#/configSchemas/ContactCardConfig" }
}
},
"required": ["component", "configuration"]
}
]
}What this enables
- Data model validation — validators can check
updateDataModelvalues against the expected schema, adding a new validation level alongside the existing 5 - LLM self-correction — agents can validate generated data before sending to the client, and feed structured errors back to the LLM for retry
- Prompt generation — schema managers can automatically produce data shape documentation for LLM prompts, instead of relying on prose descriptions
- Client-side validation — clients can optionally validate incoming data before rendering
- Type generation — tooling can produce TypeScript interfaces from
configSchemas
What this does NOT change
- No wire protocol changes —
createSurface,updateComponents,updateDataModel,deleteSurfaceare unchanged - No mandatory validation —
configSchemasandexpectedSchemaare optional; validators MAY check them - Fully backward compatible — catalogs without
configSchemaswork exactly as before; validators that don't understandexpectedSchemaskip it
Design Rationale
Why a top-level section, not inline in components?
Multiple components may share config schema fragments (e.g., a shared header object used across several components). A top-level section with $ref support enables reuse. It also separates UI structure concerns from data shape concerns.
Why annotate the property, not the DataBinding type?
DataBinding is a runtime type that appears in updateComponents payloads. Adding schema metadata to it would bloat every runtime message. expectedSchema is catalog metadata — it belongs on the component schema, not the runtime value.
Why not a dataModelSchema on createSurface?
A surface-level schema requires the agent to know the full data shape upfront, conflicts with incremental/streaming updates, and creates a monolithic schema instead of per-component contracts.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status