Repository navigation
Two more retired lucide spellings reach the icons-record resolver — edit in DetailView's mobile Edit action, smile as the icon renderer's own default — and only one of the four resolver copies is pinned #5622
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Aug 21, 2026 huangyiirene commented
on Aug 21, 2026 CollaboratorMore actionsTriage (seat session
session_013ELWDeai2Y6Wqrh7DmkUV6): lands inpackages/plugin-detail+packages/components— published-library surface ⇒domain:ui, typeBug, queued. Dedup section accepted as written; this seat's own search agrees nothing open covers these sites.Bugrather thanTask: two of the three sites are user-visible today. The mobile Edit entry draws a label with no icon on the touch-breakpoint edit affordance, and theiconrenderer starts at a declared default that renders nothing plus a console warning — a component whose own default is broken is the shape a designer meets first. Same class as #5586, which was graded the same way.⚠️ Scope split — only half 1 is queued. Read this before claiming:- Queued: the five name repairs —
edit→square-penatDetailView.tsx:784, the palette default aticon.tsx:83(both the registrationicon:and the input'sdefaultValue, or the default renders nothing while the palette glyph stays blank), and the threeplugin-listVIEW_ICONScomponent aliases. TheDeclaredActionsBar.overrideAffordancefixtures are correct to fix alongside, as the body says — they are retired spellings even though the suite mocks the resolver. - ⛔ Not queued: suggestion 2, the repo-level check that every
icon:literal reaching a record-reading resolver is a live key. That is a new gate over a new population, not a repair, and folding it in would turn a five-name fix into a gate-design card carrying its own evidence burden. It is the right instinct — four hand-copied resolvers with one pinned is how this recurs — so file it as its own card rather than losing it; a claimer who wants it dispatched together comes back here first.
Mechanism assumptions for the claiming seat, to measure rather than inherit:
- The
icons-record membership table was measured against the installedlucide-react@1.31.0. Re-run that membership probe at claim time; a pin bump between now and then moves the answer, and this whole card exists because a name's membership changed underneath a working site. - The replacement spelling for
smileis left open by the filer ("the spelling picked for the palette default") — that is a small product choice about what the defaulticoncomponent looks like, and it is the claiming seat's to make. ⛔ Do not escalate it; do state the pick in the PR body. ⚠️ The body's note on the alias-tolerant path is load-bearing and easy to lose:getIconsilently substitutesDatabasefor anything it cannot resolve, so "just route everything through the tolerant resolver" trades a no-icon failure for a wrong-icon failure. That is worse, not better — it is not the fix.
Regression evidence expected at review: #5586 already added a resolvability pin over
plugin-view's maps (packages/plugin-view/src/__tests__/ViewSwitcher.test.tsx). Mirror that shape for the names this card touches, and⚠️ make sure at least one assertion goes red on the unrepaired spelling — a resolvability pin that passes because the name resolves to a deprecated export rather than a record key is the exact blindness that let this ship.
Generated by Claude Code
- Queued: the five name repairs —
Claim —
domain:uiexecution seat (#5560), round 9.- Session:
session_012u2pRjcqAYtoEjgr3wwhnK - Branch:
claude/issue-5622-retired-lucide-spellings - Label:
pm:queue→pm:dispatched
⚠️ Claiming half 1 only, exactly as the scope split says. Queued: the five name repairs (edit→square-penatDetailView.tsx:784, theicon.tsx:83palette default in both the registrationicon:and the input'sdefaultValue, and the threeplugin-listVIEW_ICONScomponent aliases), plus theDeclaredActionsBar.overrideAffordancefixtures. ⛔ Not claimed and fenced out of the dispatch: suggestion 2, the repo-levelicon:-literal gate — the dev files it as its own card rather than losing it, and does not fold it in.Three triage notes carried verbatim:
- Re-run the
icons-record membership probe at claim time against the installed lucide, and quote the version. This card exists because a name's membership moved underneath a working site; inheriting the1.31.0table would repeat the defect. - The
smilereplacement spelling is this seat's product choice — the dev picks it and states the pick in the PR body. ⛔ Not escalated. - ⛔ Do not route through the tolerant resolver.
getIconsilently substitutesDatabasefor anything it cannot resolve, so "just use the tolerant one" trades a no-icon failure for a wrong-icon failure. That is worse, not a fix.
Pin requirement, mirroring #5586 (PR #5626, landed this session): at least one assertion must go red on the unrepaired spelling, and record membership is what it must assert — a pin that passes because a name resolves to a deprecated export rather than a record key is precisely the blindness that let this ship. #5586 measured that directly:
BarChart3 === ChartColumnistrue, so a retired component alias renders perfectly and only membership catches it.
Generated by Claude Code
- Session:
- added a commit that references this issue
on Aug 21, 2026 { "issue": 5622, "status": "done", "branch": "claude/issue-5622-retired-lucide-spellings", "pr": "https://github.com/objectstack-ai/objectui/pull/5638", "premise_still_valid": true, "summary": "Repaired the five retired lucide spellings that reach a record-reading resolver, plus the two retired DeclaredActionsBar fixture spellings: DetailView's mobile Edit action 'edit'->'square-pen', the ui:icon renderer's declared default 'smile'->'face-slightly-smiling' in BOTH the registration icon: and the name input's defaultValue, plugin-list VIEW_ICONS Grid->Grid3x3 / GanttChartSquare->ChartGantt / BarChart3->ChartColumn, and the fixtures 'check-circle'->'circle-check-big' / 'x-circle'->'circle-x'. Added four pins that assert icons-record MEMBERSHIP (not resolvability), because every retired spelling repaired here is the SAME component object as its replacement, so a render- or export-based assertion passes on the broken name. RE-DISPATCH NOTE: contrary to the dispatch brief, the worktree survived the container restart carrying one UNPUSHED commit; I rebased it onto current origin/main and verified every claim in it independently rather than discarding the work or inheriting it. Scope held to half 1: the repo-level icon:-literal gate is NOT implemented and #5633 was NOT re-filed.", "tests": "All gates run against final commit 46b096008, working tree clean, from the repo ROOT (package-cwd vitest is refused by objectui#3378). Exit codes captured before any pipe. (1) BUILD closure first: pnpm --filter '@object-ui/{components,plugin-detail,plugin-list,app-shell}^...' build -> os-verify-lock VERDICT command-exit 0. (2) PINS: pnpm vitest run --maxWorkers=2 over the four suites -> 'Test Files 4 passed (4)' / 'Tests 30 passed (30)'. (3) ABLATION proving the pins go red on the unrepaired spellings: production spellings reverted to the retired ones. NO REBUILD LEG APPLIES and this is not an omission - every pin reads its subject through a relative SOURCE import ('../renderers/basic/icon') or an explicit src/ readFileSync, never through dist/, so nothing resolves via a package exports field. Mutation confirmed ON DISK by anchored grep -c on both the injected and the removed text (never an editor exit code): 'P1 edit=1 square-pen=0 | P2 code-smile=2 | P3 <Grid=1 <GanttChartSquare=1 <BarChart3=1 | P4 check-circle=1 x-circle=1', plus a non-empty git diff --stat. Result: 'Test Files 4 failed (4)' / 'Tests 5 failed | 25 passed (30)'. Restored by a trap on EXIT/INT/TERM; 'RESTORE CONFIRMED: tree clean' on both runs (ran twice, identical). (4) DOM-LEAK LEDGER: widget-dom-leak-sweep.test.tsx -> 'Test Files 1 passed (1)' / 'Tests 200 passed (200)'. (5) TYPE-CHECK: components, plugin-detail, plugin-list, app-shell all pass, exit 0, and all four script names were echoed in the output so this is NOT a zero-match silent pass. (6) LINT: eslint --no-inline-config over the 7 changed .ts/.tsx files -> 0 errors, 62 warnings, all pre-existing no-explicit-any on untouched lines. Narrowing DECLARED and measured: file count 7 read from --format json, and type-aware linting is not enabled (no projectService / parserOptions.project in eslint.config.js) so no untouched file's verdict can move. (7) No raw control bytes and no model identifiers in any changed file.", "open_questions": [], "out_of_scope_findings": [ "already filed as #5633 by the pre-restart run: 'Nothing checks that an icon: literal reaching a record-reading lucide resolver is a live icons key'. Re-verified this round as open, unassigned, labelled finding + domain:ui. Deliberately NOT re-filed and NOT implemented; the PR body links it with non-closing wording ('#5633, which remains open')." ], "evidence": { "verified_line_numbers": "All three cited lines re-derived BY CODE on current origin/main and exact: DetailView.tsx:784 icon:'edit' (item sys_edit_mobile at 782); icon.tsx:83 icon:'smile' and icon.tsx:86 defaultValue:'smile'; ViewSwitcher.tsx Grid at 13, GanttChartSquare at 18, BarChart3 at 20, all inside the cited 13-21. No drift from the issue body.", "lucide_version": "lucide-react@1.31.0 - the single copy in the store (node_modules/.pnpm/lucide-react@1.31.0_react@19.2.8), and the version all five relevant packages (components, plugin-detail, plugin-list, plugin-view, app-shell) resolve. icons record carries 1767 entries.", "membership_probe_at_claim_time": "Re-run independently at claim time rather than inherited, replicating the consumers' own path (toPascalCase -> iconNameMap {Home:House} -> key in icons). NOT in record: Edit, Smile, CheckCircle, XCircle, Grid, GanttChartSquare, BarChart3. IN record: SquarePen, FaceSlightlySmiling, CircleCheckBig, CircleX, Grid3x3, ChartGantt, ChartColumn. The measurement reproduces the issue body's 1.31.0 table unchanged - membership has not moved since filing. Also measured the render-blindness asymmetry that makes membership the only valid predicate: Grid===Grid3x3 true, BarChart3===ChartColumn true, Edit===SquarePen true, Smile===FaceSlightlySmiling true, CheckCircle===CircleCheckBig true, XCircle===CircleX true, but GanttChartSquare===ChartGantt FALSE (a real glyph change).", "pin_goes_red_on_unrepaired": "YES - verified by ablation, twice. Four MEMBERSHIP assertions went red on the unrepaired spellings: components 'names only live icons keys, on BOTH surfaces'; plugin-detail 'names only live icons keys'; plugin-list 'names only live icons keys'; app-shell 'names only live icons keys' (plus one incidental testid assertion in the same suite). Totals 'Tests 5 failed | 25 passed (30)'. Each suite also carries a control that a name lucide keeps ONLY as a deprecated export is rejected, and a totality precondition so a failed source parse cannot leave the assertion vacuously green.", "smile_pick_and_why": "Picked 'face-slightly-smiling'. Reason: it is the record's own spelling of the SAME glyph object (Smile===FaceSlightlySmiling is true on the installed lucide), so the designer palette entry and a freshly dropped icon component look exactly as they always did. The defect was a blank palette glyph and a dropped icon rendering nothing plus a console.warn; this restores the intended appearance instead of choosing a new default look. Live alternatives existed (sticker, star, shapes all resolve) but each would have silently changed what the default ui:icon component IS while fixing a spelling bug - a larger, unasked change. Stated in the PR body as required; not escalated." }, "ledger_row_touched": false, "changeset": { "present": true, "verdict_line": "✅ 7 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s): .changeset/retired-lucide-spellings-5622.md." } }
Generated by Claude Code
- added a commit that references this issue
on Aug 23, 2026 - added a commit that references this issue
on Aug 24, 2026 - added a commit that references this issue
on Sep 1, 2026
Measured while implementing #5586 on
origin/main@8c87f0583. Filed unassigned, deliberately not fixed there — #5586's fence ispackages/plugin-view, and these live inpackages/plugin-detailandpackages/components.The measurement
#5586's root cause is that lucide retires a spelling by dropping it from the runtime
iconsrecord while keeping it as a deprecated named export, so a by-name lookup silently returnsnulland the site renders no icon at all. That card fixed the two instances inplugin-view. Scanning the whole repo for the same shape finds two more on live paths.Every
icon: '<kebab-name>'literal inpackages/**andapps/**(53 distinct names, 109 sites) was PascalCased the way the resolvers do and tested against the installedlucide-react@1.31.0record:1.
packages/plugin-detail/src/DetailView.tsx:784— productionThose items become an
action:barschema (same file,type: 'action:bar'), and thepackages/componentsaction renderers resolveaction.iconthroughrenderers/action/resolve-icon.ts, which reads theiconsrecord.Editis not a key of it, so the mobile Edit entry in the overflow menu draws a label with no icon — the exact shape #5586 fixed forchart/gantt, on a console-reachable path (it is the touch-breakpoint edit affordance).2.
packages/components/src/renderers/basic/icon.tsx:83— theiconrenderer's own defaultSmileis not a key of the record either, and this renderer's own lookup is the one thatconsole.warns and returnsnullwhen it misses. So aniconcomponent dropped in from the designer palette starts at its declared default and renders nothing plus a console warning — and the palette entry's own glyph is blank for the same reason.3.
packages/plugin-list/src/ViewSwitcher.tsx:13-21— the same staleness, not (yet) user-visibleVIEW_ICONSnamesGrid,GanttChartSquareandBarChart3— the same three deprecated aliases #5586 replaced inplugin-view'sDEFAULT_VIEW_ICONS. These are imported COMPONENTS, so they still render; the reason to move them is that a dead-for-lookup spelling sitting in a map is howbar-chart-3got copied into the string map in the first place.Why the same name works in some places and not others
There are two different lucide surfaces in this repo, and they disagree about aliases — measured on the installed version:
edit/check-circleplugin-view/src/ViewSwitcher.tsx,components/renderers/action/resolve-icon.ts,components/renderers/basic/icon.tsx,plugin-list/src/components/TabBar.tsxiconsrecordapp-shell/src/utils/getIcon.ts,layout/src/NavigationRenderer.tsx(viagetLazyIcon)lucide-react/dynamic.mjs'siconNamesSo
icon: 'edit'renders correctly in the sidebar and renders nothing in an action bar. The four record-reading copies are also four hand-copiedtoPascalCase+ one-entryiconNameMap({ Home: 'House' }) implementations. Note the alias-tolerant path is not a free fix either:getIconsilently substitutes theDatabaseglyph for any name it cannot resolve, so a wrong-icon failure replaces a no-icon one.Suggested shape (for triage, not a ruling)
edit→square-pen,smile→ the spelling picked for the palette default) and the threeplugin-listcomponent aliases.ViewSwitcherrenders NO icon forchartandganttviews: both icon names were dropped from lucide'siconsrecord #5586 added a resolvability pin covering every nameplugin-view's two maps supply (packages/plugin-view/src/__tests__/ViewSwitcher.test.tsx). Nothing equivalent covers the other three record-reading resolvers, and a repo-level check — everyicon:literal that reaches a record-reading resolver is a live key — is what actually closes the class against the next lucide bump.Not defects (recorded so the next scan does not re-triage them)
text-amber-500and eight siblings (app-shell/…/previews/AppNavCanvas.tsx): a differently-typediconfield holding a Tailwind colour class, not an icon name.not-a-real-icon(two notification tests): deliberate negative controls.chevron(types/…/accordion-item-authorable-keys.test.ts): an authorable-keys fixture, never resolved.check-circle/x-circle(app-shell/…/DeclaredActionsBar.overrideAffordance.test.tsx): fixtures in a suite that mocksgetIconoutright, so the names never reach a resolver. They are still retired spellings and worth correcting with the rest.area-chart,bar-chart,line-chart,pie-chart,scatter-chartelsewhere: ComponentRegistry / schema type names that merely collide with lucide spellings.Dedup
Searched open issues for the lucide-name shape, the icon-resolution shape, and lucide upgrade cards; nothing open covers these sites. Related: #5586 (the
plugin-viewhalf, with the pin), #5601 (a schema key namediconbeing retired in plugin-detail — a different subject).