Found while implementing #5428 (PR #5588). Not fixed there — different package, and outside that card's hard scope fence.
What
packages/components/src/renderers/basic/record-picker.tsx renders emptyText straight into a text node:
:213 ( p className="text-xs text-muted-foreground" ){props.emptyText ?? 'No records'}( /p )
:78 emptyText?: string;
@objectstack/spec widened this key to I18nLabel (string | Record< string, string >) at 17.0.0-rc.6. The renderer honours only the string arm: an authored inline per-locale map reaches a text node with no resolution.
The ComponentMeta entry at :301 narrows its declared type to 'string'deliberately, and says so at length — the narrowing is not the defect and should not be "fixed" by widening the declaration:
Declaring an arm the renderer drops would advertise a shape that never reaches the screen — the false-declaration defect this repo files separately — so 'string' keeps describing what actually works, and the description below carries the gap.
That reasoning is sound. The gap it defers is the issue.
Why it is filed now
Both the code comment at :308 and the ComponentMeta description at :318 name objectui#4163 as the home for the remaining work:
Tracked with the rest of the widened-I18nLabel render-site audit in objectui#4163
#4163 was closed as completed on 2026-08-15. The render-site audit it tracked is closed while this render site still drops the arm, so the gap now has no tracker at all — the same failure mode #5428 was filed about, in a third package.
What a fix would decide
Not obvious enough to guess, which is why this is a card and not a drive-by:
- Resolve at the render site — the sibling surfaces call
pickLocalized (objectui-side, '' on a miss) or the spec's resolveI18nLabel (undefined on a miss). Which one depends on the destination; emptyText is a text node, which argues for pickLocalized. Widening the ComponentMetatype to the union would then be correct rather than a false declaration. - Or record the narrowing as permanent — decide the picker's empty state is not a localizable surface, and say so in the contract rather than in a comment pointing at a closed card.
Option 1 is the one consistent with what every other widened-I18nLabel render site did. Filed unassigned and unqueued: which option, and whether this is worth a card at all now that the audit has been declared complete, is a triage call.
Generated by Claude Code
Found while implementing #5428 (PR #5588). Not fixed there — different package, and outside that card's hard scope fence.
What
packages/components/src/renderers/basic/record-picker.tsxrendersemptyTextstraight into a text node:@objectstack/specwidened this key toI18nLabel(string | Record< string, string >) at 17.0.0-rc.6. The renderer honours only the string arm: an authored inline per-locale map reaches a text node with no resolution.The
ComponentMetaentry at:301narrows its declaredtypeto'string'deliberately, and says so at length — the narrowing is not the defect and should not be "fixed" by widening the declaration:That reasoning is sound. The gap it defers is the issue.
Why it is filed now
Both the code comment at
:308and theComponentMetadescription at:318name objectui#4163 as the home for the remaining work:#4163 was closed as completed on 2026-08-15. The render-site audit it tracked is closed while this render site still drops the arm, so the gap now has no tracker at all — the same failure mode #5428 was filed about, in a third package.
What a fix would decide
Not obvious enough to guess, which is why this is a card and not a drive-by:
pickLocalized(objectui-side,''on a miss) or the spec'sresolveI18nLabel(undefinedon a miss). Which one depends on the destination;emptyTextis a text node, which argues forpickLocalized. Widening theComponentMetatypeto the union would then be correct rather than a false declaration.Option 1 is the one consistent with what every other widened-
I18nLabelrender site did. Filed unassigned and unqueued: which option, and whether this is worth a card at all now that the audit has been declared complete, is a triage call.Generated by Claude Code