diff --git a/packages/spec/src/ui/widget.zod.ts b/packages/spec/src/ui/widget.zod.ts index 502d8a5ba0..0d0466a03d 100644 --- a/packages/spec/src/ui/widget.zod.ts +++ b/packages/spec/src/ui/widget.zod.ts @@ -458,13 +458,27 @@ export const FieldWidgetPropsSchema = lazySchema(() => z.object({ /** * Whether the field is required. - * Widget should indicate required state visually and validate accordingly. + * + * The required MARKER (the `*`) is owned by the host's field label, not by + * the widget — a widget that draws its own produces two markers for one + * field (objectui#3222, landed in objectui#3289). Validation is likewise the + * host's: it owns the form state that decides whether the field passes. + * + * A widget reflects the state on the control it renders, via + * `aria-required` — `AriaAttributes` already declares that key, so this + * needs no additional contract key (objectui#3290). */ required: z.boolean().default(false).describe('Required field flag'), /** - * Validation error message to display. - * When present, widget should display the error in its UI. + * The active validation message for this field; `undefined` while the field + * is valid. + * + * Consumed as a SIGNAL, not as content: a widget reads it only to drive + * `aria-invalid` on the control it renders, which is the one element the + * host cannot reach. The message TEXT is rendered by the host's form message + * slot (`FormMessage` in objectui); a widget that renders it too + * double-displays it (objectui#3222, landed in objectui#3289). */ error: z.string().optional().describe('Validation error message'),