fix(types): retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654) - #7669
Draft
os-sam wants to merge 1 commit into
Draft
fix(types): retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654)#7669os-sam wants to merge 1 commit into
os-sam wants to merge 1 commit into
Conversation
…ombstone (objectui#7654) `triggerIcon` was declared `?: string` with `@default 'MessageCircle'` and read by nothing. `FloatingChatbot` destructures six of the interface's seven keys and never this one; `FloatingChatbotTrigger` takes no icon prop, so the advertised default never rendered. It is absent from the `chatbot-floating` registration's `inputs` and `defaultProps`, so no designer control offered it and no designer-created node carries it -- TypeScript was the only way to reach it. `triggerIcon?: string` becomes `triggerIcon?: never`. The refusal is TYPE-LEVEL ONLY: `FloatingChatbotConfig` has no Zod mirror, `floatingConfig` sits in the UnmirroredDeclared ledger, and `BaseSchema` is `.passthrough()`, so runtime parse behaviour is unchanged. Minting a mirror to host a `retirementTombstone()` would be the declared-but-unmirrored axis (objectui#6152), a different defect. Deletion was measured and refused: excess-property checking only reaches a fresh literal, so a deleted key still rides a widened value silently. The declared `never` refuses both paths. Both rows are pinned, the "deleted" row as a live control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This was referenced Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #7654
Retires
FloatingChatbotConfig.triggerIconunder ADR-0049 enforce-or-remove. This is thetriggerIconhalf only of the card, per thedomain:uiPM split ruling incomment 5542748081.
Part of, notFixes, on purpose: the card's other key,displayMode, is a maintainerdecision and the card carries
needs-user-decisionfor it. A closing keyword here wouldsilently close a card that still has an open decision on it when this merges. #7654 remains
open.
Premise re-measured on this branch's base (
f7cf7e8a), with a lit controlThe card body's assertions were verified rather than trusted. All three hold:
triggerIcongit grepover tracked files, build output excluded: 2 hits — the declaration and one historical CHANGELOG lineinputsinputscarriesfloatingConfig.{position,defaultOpen,panelWidth,panelHeight,title,triggerSize}, not this keydefaultPropsfloatingConfigseed carries the same six keysLit control: the same query shape over
triggerSize, a key that is read, returns 10hits across 4 files. The zeros above are readings, not a dark instrument. Line numbers
drifted from the card (declaration is at
complex.ts:928, not:907); the facts did not.FloatingChatbotdestructures six of the interface's seven keys and never this one, andFloatingChatbotTriggertakes no icon prop at all — so the advertised'MessageCircle'default never rendered either.
The finding that changed the shape of the fix: there is no Zod mirror
The brief asked me to check whether
FloatingChatbotConfig's mirror is strict ornon-strict. It is neither — there is no mirror at all.
floatingConfigsits in theUnmirroredDeclaredledger (zod-mirror-parity.test.ts,complex.zod.ts#ChatbotSchema),and
BaseSchemaends.passthrough(), so the whole object rides through unvalidated.Consequences, stated plainly:
retirementTombstone()half in this PR, and that is not an omission.There is no twin to carry one.
carrying
floatingConfig.triggerIconparsed green before this change and parses greenafter it.
(121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible #6152), a different defect from this card's declared-but-unread axis. This PR does
not widen into it.
A tripwire is pinned instead: the new test asserts that such a node still parses green,
so if #6152 ever mints a
FloatingChatbotConfigSchemathis goes red — the signal thatwhoever lands the mirror must add the
retirementTombstone()half at the same time and flipthe control rather than delete it into a vacuum.
Why a tombstone and not a deletion, when the usual argument is unavailable
This repo refuses deletion because an undeclared key is silently stripped by a
non-strict
z.object. That argument needs a mirror. With none here, the route was measuredon the
tscchannel alone — predicted in writing first, then observed:TS2353excess-property errorTS2322TS2322Excess-property checking only reaches a fresh literal, so deletion would have left
const raw = { triggerIcon: 'Sparkles' }; const cfg: FloatingChatbotConfig = raw;silentlyaccepting a key nothing reads. The declared
nevermakes the assignment itself ill-typed,so freshness stops mattering. The tombstone strictly dominates deletion on the only
channel available.
Both rows are pinned in the new test — the "deleted" row as a live control on a genuinely
undeclared key, not as prose, so the contrast cannot rot.
Evidence
Instrument named:
tsc -p packages/types/tsconfig.test.json --noEmit. Type-level assertionsare erased at runtime, so a green vitest run is not evidence about them. Program membership
proved with
--listFiles:complex.ts,complex.zod.tsand the new test file are allprogram inputs.
triggerIcon: 'Sparkles'andtriggerSize: 56both compiledclean; a bogus key fired
TS2353. That third reading is the discrimination control: itproves "no error" meant accepted, not not in the program.
b0d9db27to14fdcb53), then restored under anEXIT INT TERMtrap using absolute paths, verified byempty
git diff HEADand blob-hash equality back tob0d9db27.plugin-chatbotresolves@object-ui/typesthrough
dist/*.d.ts, so this proves consumers read the rebuilt declaration. Injecting atriggerIconwrite there turned a greentype-checkred with exactly one error at theinjected site; restored and re-verified by blob hash.
assignable to type
'never'". It reads'undefined'—?: neverunderexactOptionalPropertyTypes: falseisnever | undefined, which collapses toundefined.The direction was right, the printed type name was wrong. No assertion in this PR depends
on the word "never".
PRECONDITION NOT METand is reported asNOT MEASURED, never as a pass or a red: the baseline was already red (
Cannot find module '@object-ui/components') because the dependency closure had not been built. Closure built,leg re-run from a green baseline.
Union re-run after the final commit, at
a1170f28:pnpm --filter @object-ui/types type-check— exit 0 (all three tsc projects; script nameechoed, so not a zero-match)
vitest run packages/types/src/__tests__— 104 files / 1738 tests passed, thezod-mirror-parityledger includedpnpm --filter @object-ui/plugin-chatbot type-check— exit 0pnpm lint(whole repo, 47/47 tasks) — exit 0, 0 errorscheck:control-bytes,check:published-tsconfig-exclude,check:published-dist,check:shell-escape-residue,check:doc-fences— all exit 0Exit codes were captured by redirect before any pipe.
Clause-② determination: yes —
needs:contract-reviewMy own determination, and it survives the mirror finding.
FloatingChatbotConfigisexported from
@object-ui/types, so this is a published authoring surface: a write thatcompiled for an external consumer now fails their build. That is a contract change and a
breaking one for anyone authoring the key today, even though the in-repo census found no
such site.
What sharpens rather than softens it: the refusal lands in exactly one channel. A
reviewer should not read "tombstone" here and assume the usual two-channel shape — the
runtime accept set is untouched. Labelled on both carriers (#7654 and this PR). Opened as a
draft; not flipped ready, not enqueued.
Scope
packages/types/src/complex.tsand one new test, plus the changeset.displayModeisuntouched — not tombstoned, not removed, not made live.
content/docs/plugins/plugin-chatbot.mdxis not modified:
triggerIconwas never documented there (the Properties section #7656landed documents the six live
floatingConfigkeys and correctly omits this one), so thetombstone required no doc change. No other fenced file was touched.
Generated by Claude Code