Skip to content

[v0.10] Configuration Schemas for Data-Bound Components #989

@sarthak96agarwal

Description

@sarthak96agarwal

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 updateDataModel payloads 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 updateDataModel values 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 changescreateSurface, updateComponents, updateDataModel, deleteSurface are unchanged
  • No mandatory validationconfigSchemas and expectedSchema are optional; validators MAY check them
  • Fully backward compatible — catalogs without configSchemas work exactly as before; validators that don't understand expectedSchema skip 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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions