packages/app-shell/src/views/metadata-admin/widgets.tsx, seat-verified at source on origin/main 02bc27b — and on PR objectui#9929's head bd82760a7, which does ⛔ not touch these arms.
Four of the eleven widgets WIDGET_LABELLING declares 'control' have a catalog-FAILURE arm that renders no labelable element at all. PickerLoadFailure (a div[role="status"]) replaces the picker, so the id the host handed down lands nowhere:
:593 <div className="space-y-2">
:594 {selectedChips}
:595 <PickerLoadFailure message={objectsState.message} testId="object-selector-load-failed" />
:596 </div> // object-selector
:790 <div className="space-y-2">
:791 {selectedChips}
:792 <PickerLoadFailure message={loadError} testId="field-selector-load-failed" />
:793 </div> // field-selector
:1316 if (fieldsState.status === 'error') {
:1317 return <PickerLoadFailure message={fieldsState.message} testId="field-ref-load-failed" />; // field-ref
:1318 }
:1393 if (viewsState.status === 'error') {
:1394 return <PickerLoadFailure message={viewsState.message} testId="view-ref-load-failed" />; // view-ref
:1395 }
⇒ FieldRow emits <label for> for a 'control' widget — SchemaForm.tsx:1606, const labelAssociation = groupLabelled ? { id: labelId } : { htmlFor: id }; — so on a failed catalog that for DANGLES and the field has no accessible name. ⚠️ This is ⛔ not the grid-cell case: it is true in the card layout too, where the visible label is right there and points at nothing.
The contract this violates, in its own words — class (b)
The 'control' half of WIDGET_LABELLING's own docblock, quoted from the same file:
The widget puts id on the ONE labelable element that is the field's primary control, in EVERY branch it can render (loading, empty-catalog, read-only)
The failure branch is a branch it can render. The clause even names why the list is what it is: field-multi / action-multi are excluded from 'control' precisely because they were measured dangling in one arm. These four are inside the declaration and dangle in another arm.
⭐ The in-file counterexample — this is a shape the file already knows
Two of the six 'control' widgets with a failure arm keep a named control on it, so the repair does not need inventing:
ref:object (:341–:344) renders PickerLoadFailure beside a freeform <Input id={id}> it deliberately keeps enabled, "so a failed catalog does not also block authoring";
filter-builder puts the failure inside its popover; the trigger <Button> that carries the naming still renders.
⇒ the question this card must settle is which of the two shapes each of the four takes: keep an editable anchor for the stored value (the ref:object shape), or keep the disabled trigger and put the failure beneath it (the filter-builder shape). ⚠️ field-selector's own comment argues against the second — "with no options it could only render as a dead, disabled dropdown next to a banner saying the options are unknown, which is the very conflation this arm exists to end" — so a naive "render the disabled picker anyway" would undo objectui#5170's repair. That is the decision, not a detail of it.
What was measured, and what was ⛔ not
- MEASURED by the objectui#9889 dev, failure arm and loaded arm in the same test:
field-ref, view-ref, object-selector — dangling for, no accessible name.
- ⛔ NOT MEASURED:
field-selector. It is structurally identical to object-selector above ({selectedChips} + the failure block, no anchor), and it is listed here on that reading of the source — ⛔ it is not a negative, and it is not a measurement.
- The objectui#9889 pin renders
loaded(...) catalogs only, so it is green on all eleven and says nothing about this arm. ⇒ this is a pre-existing hole, ⛔ not a regression from PR objectui#9929.
Dedupe words
PickerLoadFailure · dangling label for · catalog error arm · accessible name · WIDGET_LABELLING control
Cross-references, each resolved by a read in THIS act, with its title printed
- objectui#9889 — finding(app-shell): a
labelling: control widget inside a grid repeater cell renders with NO accessible name … (open; the parent card, landed as PR objectui#9929)
- objectui#5170 — [finding] Three option-picker loaders in ResourceEditPage swallow a failed load into an empty option list, with no trace at all (closed; the card that created these failure arms)
filed by the domain:ui#2 execution seat · session_018HrVaotisyhgmot9o2MLRq · ⛔ this seat does ⛔ not grade or route: no priority:* and no domain:* set here · handed over by the objectui#9889 dev and re-taken at source by the seat on origin/main 02bc27b · read at 2026-09-18T18:06Z · ⭐ every cross-reference in this card was resolved by a read in this act, with its title printed — a card number written from memory is a claim, ⛔ not a pointer
Generated by Claude Code
packages/app-shell/src/views/metadata-admin/widgets.tsx, seat-verified at source onorigin/main02bc27b — and on PR objectui#9929's headbd82760a7, which does ⛔ not touch these arms.Four of the eleven widgets
WIDGET_LABELLINGdeclares'control'have a catalog-FAILURE arm that renders no labelable element at all.PickerLoadFailure(adiv[role="status"]) replaces the picker, so theidthe host handed down lands nowhere:⇒⚠️ This is ⛔ not the grid-cell case: it is true in the card layout too, where the visible label is right there and points at nothing.
FieldRowemits<label for>for a'control'widget —SchemaForm.tsx:1606,const labelAssociation = groupLabelled ? { id: labelId } : { htmlFor: id };— so on a failed catalog thatforDANGLES and the field has no accessible name.The contract this violates, in its own words — class (b)
The
'control'half ofWIDGET_LABELLING's own docblock, quoted from the same file:The failure branch is a branch it can render. The clause even names why the list is what it is:
field-multi/action-multiare excluded from'control'precisely because they were measured dangling in one arm. These four are inside the declaration and dangle in another arm.⭐ The in-file counterexample — this is a shape the file already knows
Two of the six
'control'widgets with a failure arm keep a named control on it, so the repair does not need inventing:ref:object(:341–:344) rendersPickerLoadFailurebeside a freeform<Input id={id}>it deliberately keeps enabled, "so a failed catalog does not also block authoring";filter-builderputs the failure inside its popover; the trigger<Button>that carries the naming still renders.⇒ the question this card must settle is which of the two shapes each of the four takes: keep an editable anchor for the stored value (the⚠️
ref:objectshape), or keep the disabled trigger and put the failure beneath it (thefilter-buildershape).field-selector's own comment argues against the second — "with no options it could only render as a dead, disabled dropdown next to a banner saying the options are unknown, which is the very conflation this arm exists to end" — so a naive "render the disabled picker anyway" would undo objectui#5170's repair. That is the decision, not a detail of it.What was measured, and what was ⛔ not
field-ref,view-ref,object-selector— danglingfor, no accessible name.field-selector. It is structurally identical toobject-selectorabove ({selectedChips}+ the failure block, no anchor), and it is listed here on that reading of the source — ⛔ it is not a negative, and it is not a measurement.loaded(...)catalogs only, so it is green on all eleven and says nothing about this arm. ⇒ this is a pre-existing hole, ⛔ not a regression from PR objectui#9929.Dedupe words
PickerLoadFailure·dangling label for·catalog error arm·accessible name·WIDGET_LABELLING controlCross-references, each resolved by a read in THIS act, with its title printed
labelling: controlwidget inside a grid repeater cell renders with NO accessible name … (open; the parent card, landed as PR objectui#9929)filed by the
domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· ⛔ this seat does ⛔ not grade or route: nopriority:*and nodomain:*set here · handed over by the objectui#9889 dev and re-taken at source by the seat onorigin/main02bc27b · read at 2026-09-18T18:06Z · ⭐ every cross-reference in this card was resolved by a read in this act, with its title printed — a card number written from memory is a claim, ⛔ not a pointerGenerated by Claude Code