Uh oh!
There was an error while loading. Please reload this page.
fix(tooling): census ui:icon in the lucide gate, repair the three retired spellings it finds - #6290
Conversation
…etired spellings it finds `ui:icon` has read `schema.icon` straight out of lucide's runtime `icons` record since #5631 — `import { icons } from 'lucide-react'` plus `(icons as any)[key]` — which makes it exactly the resolver class `check-lucide-icon-record-names.mjs` exists for. Its `type` was absent from part 2's `RECORD_READING_TYPES`, so its 96 authored glyph names were SEEN and DECLINED while the gate stayed green: the failure shape this gate was built to end, one level up in its own census. Part 1 needed no change and got none. `icon.tsx` imports the record DIRECTLY, so `DECLARED_RECORD_READERS` has carried it since #5633's own discovery run — the asymmetry against `dropdown-menu.tsx`, which routes through `resolveIcon` and correctly stays out (#5992). Part 1 knowing a module and part 2 knowing which `type` sends names to it are two independent facts; this supplies the second. `discovered.record` stays at 8, pinned. No `descendants`: the renderer resolves one name and returns a single element, never walking `children`, so there is no untyped-child population for a descent to reach — and a descent that reaches nothing is an ERROR under the non-vacuity rule. The census entry alone turns the gate RED on three nodes, which is the non-vacuity proof and the reason both halves land together. The repair is `circle-check-big`, DERIVED by object identity rather than guessed: lucide's `CheckCircle` export IS `CircleCheckBig`, so that spelling keeps the same glyph. `circle-check` is a different glyph and would have changed the artwork silently.
yinlianghui-tw
commented
Aug 25, 2026
PM review — ACCEPT. ⭐ The |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#6009
ui:iconhas resolvedschema.iconstraight out of lucide's runtimeiconsrecord since #5631 —import { icons } from 'lucide-react'plus(icons as any)[mappedIconName]— which makes it exactly the resolver classcheck-lucide-icon-record-names.mjsexists for. Itstypewas absent from part 2'sRECORD_READING_TYPES, so its 96 authored glyph names were seen and declined while the gate stayed green. That is the failure shape this gate was built to end, occurring one level up inside its own census: not a gate that fails, a gate whose judged population quietly shrank relative to what now reaches a resolver.Both halves land together because they cannot separate: the census entry alone turns the gate red on three nodes, and that red is the non-vacuity proof.
Re-derived against post-#6277
mainThe card was written before PR #6277 landed at
e3784607f, and its line references and counters were stale. Branch point containse3784607f(git merge-base --is-ancestorconfirmed). Everything below is re-measured against the file as it now is.check-circleat lines ~53 / 69 / 85judged: 30 | declined: 254/350judged: 33 | declined: 347ate3784607f(#6277's own landing moved judged 30→33)circle-checkcircle-check-big— see belowThe card's central measurement held exactly: +96.
Part 1 needed no change, and got none
icon.tsximports theiconsrecord directly and indexes it, soDECLARED_RECORD_READERShas carried it since #5633's own discovery run — it is already in the discovered set onmain. This is the asymmetry againstdropdown-menu.tsx, which routes throughresolveIconand correctly stays out (the finding #5992 recorded).Part 1 knowing a module and part 2 knowing which
typesends names to it are two independent facts. This PR supplies only the second.discovered.recordstays at 8 and is pinned by a new test, so a future change that moved the wrong part of the gate fails loudly.The OK line's
N record-reading resolversreadsdiscovered.record.length(part 1's modules), not the size ofRECORD_READING_TYPES— which is why it does not move.RECORD_READING_TYPESgoes 9 → 10 entries; that count is not printed anywhere.No
descendantsRead off the renderer, the way every
pathsentry in that table was:icon.tsxresolves one name and returns a single element — the resolvedIconcomponent, or theSquareDashedplaceholder. It never walkschildren. So there is no untyped-child population for a descent to reach — and under the non-vacuity rule a descent that reaches nothing is an error, not a shrug.The repair is
circle-check-big, derived by identityThe dispatch proposed
circle-check. Probed against the installed lucide-react 1.31.0 instead of taken on trust:check-circle's retired export isCircleCheckBig.circle-checkis a live key naming a different glyph, so that repair would have silently changed the rendered artwork while going green.circle-check-bigkeeps the same glyph — and is what the gate's own identity derivation prints, unprompted, in the red output below.RED, then GREEN
Census entry alone —
EXIT=1, all three sites named:Counters across the two steps:
judgeddeclinede3784607fbaseline+96 / −96, exactly the population the card measured, andanchoredJudgedand the resolver count both hold still.The header's container table: re-taken, and unchanged
#6277 re-measured it at
ef2a3bd8das 61 untyped names across 7 containers. Re-taken ate3784607f: identical, per-container figures and all. It looks like this change should move it and it does not — the table counts untyped names, and atype: 'icon'node is typed, so the two populations are disjoint. Recorded in the header rather than left implicit, following the #6277/#6260 pattern of naming the commit rather than editing figures in place.Re-measuring also surfaced a scope caveat the count did not carry on its face, now written into the header: the table is measured over
examples/schema-catalog/, while the gate scanspackages/,apps/andexamples/. Over the full scan roots the same walk finds 76 untyped names across 9 containers — the extra 15 all inpackages/types/examples/(tree-view+6,timeline+3, pluslist3 andsidebar3, two containers the table does not name at all). Both extra containers were read:data-display/list.tsxandnavigation/sidebar.tsxnever readicon, so those names reach no resolver and decline correctly. All 15 were also checked against the record and are live spellings, so nothing is hiding there — the table is right about what it judges, it is simply not the whole unjudged census.Tests
Five new tests, and an ablation showing they are not vacuous. Deleting the census entry (mutation confirmed on disk, anchored grep
1 → 0, restored under atrap):The fifth staying green is the correct reading, not a hole: it asserts that
icon.tsxwas already a part-1 resolver, which is true independently of the part-2 entry. Restore leg confirmed on disk (entry back, tree clean againstHEAD, gate green).type-check:scriptsearned its keep here — it caughtexpect(RECORD_READING_TYPES.icon.descendants)asTS2339, sinceRECORD_READING_TYPESis a plain object literal and the key is absent fromicon's inferred type. Fixed at the assertion using the file's own'descendants' in …idiom, which also asserts the stronger fact (the key is absent, not merelyundefined) — not by widening the type to accommodate the test.#6277's 9 tests all still pass.
Verification — union re-run on the final commit
1443fcbf0Exit codes captured by redirect before any pipe; verdicts quoted from what each gate printed.
check:icon-record-namesOK lucide icon names: 163 authored/declared names reaching 8 record-reading resolvers are liveiconskeystype-check:scriptslint:rootunnarrowed✖ 28 problems (0 errors, 28 warnings)— all pre-existingno-explicit-anywarningscheck:control-bytes✅ check-control-bytes: OK (scanned 5178 tracked text file(s); skipped 85 binary).pnpm exec vitest run scripts/__tests__Test Files 77 passed (77)·Tests 2223 passed (2223)check-changeset-presence✅ No source of a released package changed in this range, so no changeset is owed.No narrowing anywhere —
lint:rootran unnarrowed and the scripts suite ran whole.Changeset
None, on the presence gate's own verdict rather than on my reading of
examples/:@object-ui/example-schema-catalogisprivate: trueand matches@object-ui/example-*in.changeset/config.json'signorelist. The gate reports3 file(s) changed, 0 of them published source of a package the release covers.scripts/needs none. Noskip-changesetlabel was created or applied — that label does not exist in this repo.Not addressed here
ui:iconbeing a record-reading site is an input to its table; out of scope: Consolidate the eight lucide record-reading icon resolvers into one seam — three tokenisers and a 5-of-8 alias map mean the same authored name renders on one surface and not another #5935 remains open.icons-record resolver —editin DetailView's mobile Edit action,smileas theiconrenderer's own default — and only one of the four resolver copies is pinned #5622 /ViewSwitcherrenders NO icon forchartandganttviews: both icon names were dropped from lucide'siconsrecord #5586 — the retired-spelling family. This PR repairs the three instances the new census entry surfaces, and nothing else.Generated by Claude Code