Skip to content

[finding] element:button icon: the spec's shipped .describe() says the renderer uses "its own" normaliser, but objectui#5993 moved it to the shared resolveIcon — prose and read-point anchors are both stale at pin 9602dc820450 #12968

Description

@os-elon

Split out of #12687 / PR #12955 by maintainer ruling (option A, 2026-08-28): this is packages/spec authorable-surface contract prose, which is not a console pin bump's to rewrite, and not a domain:devx seat's to authorise. #12687 and PR #12955 are blocked on this cardcheck:objectui-pin-citations stays red on exactly the two records below until it lands, and that red is the deliberately-accepted state, not an oversight.

Everything below is already measured at objectui 9602dc820450. Nothing here needs re-deriving.

The two records

  • packages/spec/src/ui/component.zod.ts:1827 — the ElementButtonPropsSchema icon docblock
  • packages/spec/src/ui/component.test.ts:2440 — the #10053 companion accept-pin

Both still assert `.objectui-sha` = `190fbd01d`, which the repo no longer builds against.

What broke — one clause, and it is shipped to authors

The .describe() on ElementButtonPropsSchema.icon tells authors, verbatim:

…the renderer resolves it through lucide-react's icons map using its own PascalCase normaliser and rename map, NOT the LazyIcon slot the container icons use…

At 9602dc820450 the emphasised clause is false. objectui#5993 deleted button.tsx's file-local toPascalCase + iconNameMap + icons index and routed the button through the shared resolveIcon (packages/components/src/renderers/action/resolve-icon.ts). The docblock above the key repeats the same claim and cites button.tsx:14-27 for it; that code is no longer in the file.

objectui#5993's own reason for the move is the argument that the shared resolver is right and the prose is merely stale, quoted from the new code comment:

This file used to carry its own toPascalCase + iconNameMap + icons index — the same algorithm, but not the same function, so an alias added to resolve-icon.ts to absorb a lucide retirement (objectui#5586, #5622) reached every action:* site and silently missed ui:button.

So the duplicate was a defect, its removal was correct, and nothing about the platform's behaviour regressed. Only the sentence describing it went out of date.

What did NOT break — do not re-establish this

  • The key is still LIVE. resolveIcon(schema.icon) is read and the glyph is rendered on both sides of the label per iconPosition, both suppressed while loading.
  • The record's verdict still holds. These records exist to stop a liveness sweep re-opening a false retirement candidate (the spec declares icon on page:accordion items, but the console pinned after objectui#4652 no longer reads it — ADR-0049 enforce-or-remove #9397 lineage). That purpose is intact — this is not a dead read point.
  • The LazyIcon contrast survives. resolveIcon ends return (icons as Record<string, LucideIcon>)[mapped] ?? null — an unknown name still yields null, so "an unknown name here renders nothing rather than a fallback glyph" remains true, and it still differs from getLazyIcon, which degrades an unknown name to the Database glyph (lazy-icon.tsx:66-80, byte-identical across the pin move).
  • icon is still unpublished to the designer. Re-checked at the new pin: the button registration lists label, variant, size, className and no icon input, so "unpublished is not unread" still holds.

Re-derived anchors at 9602dc820450

what the record cites at 190fbd01d at 9602dc820450
icon resolution button.tsx:44-47 button.tsx:36 — and it now hops into a different file
the normaliser itself button.tsx:14-27 resolve-icon.ts:14-24 (toPascalCase + iconNameMap) and :30-35 (resolveIcon). button.tsx:14-27 is now imports plus the forwardRef opening
render, left / right button.tsx:69 / :71 button.tsx:57 / :59 (mr-2 h-4 w-4 / ml-2 h-4 w-4, unchanged)
registration input list button.tsx:82-98 button.tsx:70-87 (:88-92 is now defaultProps)

button.tsx changed across the move; lazy-icon.tsx did not.

What closing this card involves

  1. Correct the .describe() clause so it describes the shared resolver — the resolution is now shared with every action:* site through resolveIcon, while the accept/reject behaviour it promises authors is unchanged.
  2. Re-point the two records' anchors to the table above, including the new cross-file hop.
  3. Move both citations to `.objectui-sha` = `9602dc820` in the same edit, so check:objectui-pin-citations goes green with anchors that are actually true of the tree they name.

⛔ Do not close it by moving the citations to the historical spelling (`.objectui-sha` pin `190fbd01d`). These are live claims about the pin the repo builds against; converting them to dated records is the trade that gate exists to refuse, and it would strand a now-false .describe() with nothing checking it.

Why this was not just fixed in PR #12955

check-objectui-pin-citations's own instruction: "If a read point has moved or died, the record needs more than a citation refresh — report it rather than re-pointing it." This one moved, and the part that cannot be repaired by moving line numbers is contract prose with a different owner. Bundling it into a console pin bump would also breach that card's standing scope rule.

Filed unassigned, and deliberately without a domain:* label — that is triage's call, made by reading the code.

Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions