Skip to content

feat(types)!: retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654) - #7669

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-7654-trigger-icon-retire
Sep 5, 2026
Merged

feat(types)!: retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654)#7669
os-sam merged 1 commit into
mainfrom
claude/issue-7654-trigger-icon-retire

Conversation

@os-sam

@os-sam os-sam commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Part of #7654

Retires FloatingChatbotConfig.triggerIcon under ADR-0049 enforce-or-remove. This is the
triggerIcon half only of the card, per the domain:ui PM split ruling in
comment 5542748081.

Part of, not Fixes, on purpose: the card's other key, displayMode, is a maintainer
decision and the card carries needs-user-decision for it. A closing keyword here would
silently 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 control

The card body's assertions were verified rather than trusted. All three hold:

claim reading
nothing reads triggerIcon git grep over tracked files, build output excluded: 2 hits — the declaration and one historical CHANGELOG line
absent from the registration's inputs confirmed — inputs carries floatingConfig.{position,defaultOpen,panelWidth,panelHeight,title,triggerSize}, not this key
nothing seeds it into defaultProps confirmed — the floatingConfig seed carries the same six keys

Lit control: the same query shape over triggerSize, a key that is read, returns 10
hits 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.

FloatingChatbot destructures six of the interface's seven keys and never this one, and
FloatingChatbotTrigger takes 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 or
non-strict. It is neither — there is no mirror at all. floatingConfig sits in the
UnmirroredDeclared ledger (zod-mirror-parity.test.ts, complex.zod.ts#ChatbotSchema),
and BaseSchema ends .passthrough(), so the whole object rides through unvalidated.

Consequences, stated plainly:

A tripwire is pinned instead: the new test asserts that such a node still parses green,
so if #6152 ever mints a FloatingChatbotConfigSchema this goes red — the signal that
whoever lands the mirror must add the retirementTombstone() half at the same time and flip
the 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 measured
on the tsc channel alone — predicted in writing first, then observed:

route fresh object literal widened (non-fresh) value
deleted TS2353 excess-property error compiles CLEAN
tombstoned TS2322 TS2322

Excess-property checking only reaches a fresh literal, so deletion would have left
const raw = { triggerIcon: 'Sparkles' }; const cfg: FloatingChatbotConfig = raw; silently
accepting a key nothing reads. The declared never makes 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 assertions
are erased at runtime, so a green vitest run is not evidence about them. Program membership
proved with --listFiles: complex.ts, complex.zod.ts and the new test file are all
program inputs.

  • Baseline (pre-change)triggerIcon: 'Sparkles' and triggerSize: 56 both compiled
    clean; a bogus key fired TS2353. That third reading is the discrimination control: it
    proves "no error" meant accepted, not not in the program.
  • Deletion ablation — mutation proved on disk (anchored grep 1 to 0; blob b0d9db27 to
    14fdcb53), then restored under an EXIT INT TERM trap using absolute paths, verified by
    empty git diff HEAD and blob-hash equality back to b0d9db27.
  • After the tombstone — both paths error; the six live keys stay writable.
  • Cross-package reverse verificationplugin-chatbot resolves @object-ui/types
    through dist/*.d.ts, so this proves consumers read the rebuilt declaration. Injecting a
    triggerIcon write there turned a green type-check red with exactly one error at the
    injected site; restored and re-verified by blob hash.
  • One prediction MISS, reported as a miss: I predicted the error would read "not
    assignable to type 'never'"
    . It reads 'undefined'?: never under
    exactOptionalPropertyTypes: false is never | undefined, which collapses to undefined.
    The direction was right, the printed type name was wrong. No assertion in this PR depends
    on the word "never".
  • First attempt at the cross-package leg was PRECONDITION NOT MET and is reported as
    NOT 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 name
    echoed, so not a zero-match)
  • vitest run packages/types/src/__tests__104 files / 1738 tests passed, the
    zod-mirror-parity ledger included
  • pnpm --filter @object-ui/plugin-chatbot type-check — exit 0
  • pnpm lint (whole repo, 47/47 tasks) — exit 0, 0 errors
  • check:control-bytes, check:published-tsconfig-exclude, check:published-dist,
    check:shell-escape-residue, check:doc-fences — all exit 0

Exit codes were captured by redirect before any pipe.

Clause-② determination: yesneeds:contract-review

My own determination, and it survives the mirror finding. FloatingChatbotConfig is
exported from @object-ui/types, so this is a published authoring surface: a write that
compiled 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.ts and one new test, plus the changeset. displayMode is
untouched
— not tombstoned, not removed, not made live. content/docs/plugins/plugin-chatbot.mdx
is not modified: triggerIcon was never documented there (the Properties section #7656
landed documents the six live floatingConfig keys and correctly omits this one), so the
tombstone required no doc change. No other fenced file was touched.


Generated by Claude Code

…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
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3180.1 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-CI1E9WNz.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 510.70KB 116.21KB
core (index.js) 6.70KB 2.68KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.27KB 61.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 48.05KB 13.34KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.86KB 34.68KB
plugin-designer (index.js) 212.86KB 43.19KB
plugin-detail (index.js) 250.55KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.41KB 41.04KB
plugin-grid (index.js) 209.40KB 56.80KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.57KB 6.82KB
plugin-markdown (index.js) 13.70KB 4.68KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.35KB 3.23KB
plugin-view (index.js) 85.24KB 20.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

os-sam commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

契约复核 — 裁决(逐字采纳)

归属:domain:ui 派发席位,依 2026-08-31 裁定席内完成;董事席链条为可选事后审计,非放行必要条件;⛔ 无「等外部席位」的无限期状态。

档位与核验:走隔离 fable 子代理(⛔ 契约复核不适用额度耗尽豁免降档)。transcript 中 harness 逐消息盖章的 model 字段 89 次读数全部 claude-fable-5-1,0 次回退。亮控:同一 grep 在本会话其它 transcript 上读出 claude-opus-5(30 份)与 <synthetic>(1 份)—— 该 grep 能读出非 fable 值,所以这里的零是读数,不是暗仪器。达标 ⇒ 逐字采纳,未改一字。

独立性:只喂卡片 #7654、既有裁定与 PR 本身;⛔ 未喂派发简报、⛔ 未喂本席结论;简报按对抗性写法。

席位对三条非阻断建议的处置:③ 标题 fix(types):feat(types)!: 本席直接改(标题非代码,属席位权限,且复核明说「before un-drafting」);① 触发线注释指向 shape pin、② JSDoc 补引判别式 prong 2 —— 两处都是树内文字,单独立卡跟进,⛔ 不拿非阻断项卡住一个 PASS(那正是本席昨夜停放四个绿 PR 的错误)。


Contract review — objectui PR #7669 (card #7654, triggerIcon half)

Everything below was measured at PR head a1170f28 (base f7cf7e8a, merge-base confirmed), in a detached scratch worktree, with ablations restored and verified clean after each leg.

① Derived judgments — one line each

  1. Published TS surface FloatingChatbotConfig.triggerIcon: ?: string?: never (= undefined under root strict: true, no exactOptionalPropertyTypes). Measured: fresh literal refused (TS2322 Type 'string' is not assignable to type 'undefined' at test 64:7 once the directive is stripped), widened value refused, both pins fire TS2578 on re-widening, six live keys still compile (baseline tsc -p packages/types/tsconfig.test.json exit 0, all three files in --listFiles). Judged: correct tombstone shape, same ?: never + @deprecated "Not part of X's contract — the value was inert." form as base.ts inputType.
  2. Runtime accept set: unchanged. packages/types/src/zod/ has 0 hits for floatingConfig/FloatingChatbot; ChatbotSchema = BaseSchema.extend({…}) (complex.zod.ts:376) declares no floatingConfig; BaseSchemaCore ends .passthrough() (base.zod.ts:157); ledger UnmirroredDeclared['complex.zod.ts#ChatbotSchema'] = 'displayMode' | 'floatingConfig' | 'requestBody' (zod-mirror-parity.test.ts:1029); @objectstack/spec dist 0 hits; sdui-parser validateTree judges against manifest inputs, which never carried the key. Judged: the "no mirror" claim is TRUE; a retirementTombstone() half is impossible, not omitted.
  3. Designer surface: no control, no seed. chatbot-floating inputs (renderer.tsx:505–510) and defaultProps.floatingConfig (527–534) both carry exactly position, defaultOpen, panelWidth, panelHeight, title, triggerSize. Judged: no stored document written by this repo's designer can carry triggerIcon; the displayMode inversion does not apply.
  4. Read-site census: dead. git grep triggerIcon (tracked, dist/ excluded): the declaration, CHANGELOG.md:578, and the PR's own three files — nothing else. Lit control triggerSize: 19 lines / 7 files. FloatingChatbot destructures six keys (FloatingChatbot.tsx:39–46); FloatingChatbotTriggerProps is position | size | className (no icon). Sibling objectstack checkout: 0 hits for triggerIcon / FloatingChatbotConfig / floatingConfig. Judged: zero authors anywhere measurable.
  5. Docs: no change needed. content/docs/plugins/plugin-chatbot.mdx documents the six live floatingConfig.* keys only; its FloatingChatbotConfig TS example compiles (Doc Snippet Type Check green). CHANGELOG.md:578 (release 3.3.0, 2026-04-17) advertised triggerIcon — historical, correctly untouched.
  6. Test file: live instrument, not dark. 7/7 green. Ablations: (A) strip fresh-literal directive → TS2322 surfaces; (B) re-widen to ?: stringTS2578 ×2; (C) delete the member outright → only the widened pin goes unused (line 72) — the "deleted row" contrast is real; (E) mint a house-style non-strict z.object mirror → exactly 1 test red (shape.floatingConfig toBeUndefined); (F) mint a .strict() mirror → 2 tests red. Judged: the tripwire fires. Nit: the comment on the parse-green test says "if 121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible #6152 ever mints a FloatingChatbotConfigSchema, this goes RED" — false for the house-style non-strict mirror (that test stays green; only the shape pin fires). The file trips either way, so non-blocking, but the comment should be moved to or reference the shape pin.
  7. Route choice vs. the package's own discriminator — the strongest attack, dispositioned. The only precedents for an ADR-0049 retirement on an unmirrored @object-ui/types surface are finding(types): MobileOverrides.navigation 发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919feat(types,mobile)!: retire the MobileOverrides type and its mount point (#4919) #5941 and finding(types): MobileComponentConfig is now a fully inert published type — zero mount points, zero readers #5942feat(types,mobile)!: retire the MobileComponentConfig published type #7526 (2026-09-03), both removed outright on "a tombstone exists to steer authors to a named live replacement KEY, or to keep loud a key the docs taught as working" (mobile.ts:129, :343; both changesets). triggerIcon fails prong 1 (no replacement key), but satisfies prong 2: it was advertised as an option in the 3.3.0 release record and its published JSDoc promised @default 'MessageCircle', whereas MobileOverrides / AccordionItem.icon had 0 CHANGELOG lines. Judged: the tombstone route is within the discriminator. However, the PR's recorded rationale (complex.ts JSDoc, changeset, test header) argues from tsc alone — an argument that applied equally to finding(types): MobileOverrides.navigation 发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919/finding(types): MobileComponentConfig is now a fully inert published type — zero mount points, zero readers #5942 and was implicitly accepted there as residual ("Excess-property checking is load-bearing", finding(types): MobileOverrides.navigation 发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919 report). As written, complex.ts:967 says "unmirrored → tombstone" and mobile.ts:129 says "unmirrored zero-pull → remove outright" in the same package. Non-blocking, but the JSDoc should cite prong 2 (release-note advertisement + published default) so the two statements do not contradict.

② Semver grading

Declared: '@object-ui/types': minor (.changeset/7654-floating-chatbot-trigger-icon-tombstone.md).

Correct level: minor. AGENTS.md §版本号策略 line 239: objectui's own breaking changes are marked minor with the breaking semantics stated in the body; major is reserved for following @objectstack across a major and is CI-refused by scripts/check-changeset-no-major.mjs (Changeset Bump Policy: green). Every prior ADR-0049 retirement changeset in the tree (5905 ×2, 6121, 6896, 4919, 5942) is minor. The body states the accept-set change plainly ("a write of FloatingChatbotConfig.triggerIcon used to compile and now does not"), which is what line 239 requires. Match.

Nit (non-blocking): PR title is fix(types):; the two most recent tombstone PRs (#7639, #7526) used feat(types)!:. The root CHANGELOG is git-cliff-generated with a [**breaking**] marker keyed on !, but the workflow describes that file as hand-curated and the changeset is the release input. Recommend feat(types)!: before un-drafting.

③ Boundary-flag disposition

Verdict

VERDICT: PASS — the "no mirror" claim is true on measurement, the tombstone refuses on the only channel that exists, no designer control or default seeds the key, the bump matches the repo's CI-enforced policy, and every flag is answered. Three non-blocking wording fixes are recommended before un-drafting (tripwire comment → shape pin; JSDoc rationale → cite the discriminator's prong 2; title → feat(types)!:).

Implemented-by: claude/issue-7654-trigger-icon-retire
Reviewed-by: isolated subagent (session_01KbJQ1y1J12nZxYzFWhP8Q3)


Generated by Claude Code

@os-sam os-sam changed the title fix(types): retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654) feat(types)!: retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654) Sep 5, 2026
@os-sam
os-sam marked this pull request as ready for review September 5, 2026 02:01
@os-sam
os-sam added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit a3eb5d0 Sep 5, 2026
34 checks passed
@os-sam
os-sam deleted the claude/issue-7654-trigger-icon-retire branch September 5, 2026 02:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants