feat(types)!: retire ChatbotSchema.displayMode as an ADR-0049 tombstone; remove the Display Mode control and seed (objectui#7654) - #7739
Conversation
…ne; drop the Display Mode control and seed (objectui#7654) `displayMode?: 'inline' | 'floating'` was declared on `ChatbotSchema` and on `ChatbotFloatingSchema`, offered as a "Display Mode" control in the `chatbot-floating` registration's `inputs`, seeded as `'floating'` by that registration's `defaultProps` -- and read by nothing. The node `type` is the one selector of presentation; this key was a second spelling of that choice. Maintainer ruling B (2026-09-05, decision batch #44): retire it. Both declarations become `displayMode?: never` with the JSDoc pointing at `type` as the replacement and citing discriminator prong 2 (advertised at CHANGELOG.md:578, 3.3.0). The designer control and the `defaultProps` seed are removed; the restatement of the control is the tombstone plus the changeset. Runtime validation of the key is unchanged -- zero before and after: it has no Zod arm on either twin and `BaseSchema` is `.passthrough()`, so stored documents the designer wrote parse exactly as they did. No `retirementTombstone()` half is minted (that is the objectui#6152 axis); the new test pins both twins' shapes as the tripwire that fires when the mirror is minted. Measured on this member (tsc, `FloatingChatbotConfig` lit as the no-index- signature control): declared, a wrong value is TS2322 and presence is clean; DELETED, every shape is silent and the member reads as `any`; TOMBSTONED, presence with any value is TS2322 in every shape. On a `BaseSchema` carrier the routes are loud-vs-silent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
✅ 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
|
Contract review — VERDICT: PASS, with one Clause-② gap that rides before landing
Ruling B is executed on both faces, and the discriminator premise was re-measured rather than restatedThree tree states run on this member, with
⭐ Read the DELETED column: with the member gone, even a wrong-typed value goes quiet and the member reads as The sibling review's inversion was the right thing to fear, and it does not bite
And the render: through the real host, a node carrying the key renders byte-identical DOM (8088 bytes) to one without it, on both the LIVE and TOMBSTONED renderer blobs. The stray
|
… the tripwire prose (objectui#7654) Text-only land-prep on the retirement PR. No runtime, type or manifest behaviour changes: every hunk is changeset prose, a test title string, or a comment block. 1. Clause-② / changeset — the accept-set list named the TypeScript, runtime and designer faces but not the manifest one. Re-measured on both sides of this change, with the `chatbot-floating` registration restored from `origin/main` for the LIVE leg (mutation and restore proven by blob hash): `inputs` 20 -> 19, `defaultProps` 9 keys -> 8; `validateTree` on a stored node carrying `displayMode` goes 0 -> 1 diagnostic, code `unknown-prop`, severity `warning`; the props interface `generateDts` derives from the same `inputs` goes 20 -> 19 members. Author-time only: `compile()` still returns a tree carrying the value on both legs, and the page renderer filters diagnostics to `severity === 'error'` before failing a page. 2. The shape-pin comment and the 7655 TRIPWIRE comment claimed the parse-green line stays green for a non-strict mirror arm, citing objectui#7678 item 2. That transposition is false: item 2 measured object-mirror shapes, and `displayMode` is a scalar union. Minting the arm a scalar union takes — an optional `z.enum` over 'inline' | 'floating' — on `ChatbotSchema`'s twin reddens BOTH: `tsc` with exactly one error at zod-mirror-parity.test.ts(1586,14) and vitest with exactly two failures, the shape pin and the parse-green line. The prose now says what was measured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
✅ 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
|
⛔ Correction — two sentences in my verdict are falsified, and the implementing seat is the one that caught themMy verdict (5552415144) asked for the manifest face to be named in the changeset, and described its consequence in words I took from the contract review. The implementing seat re-measured all four readings as instructed and refused to write down the part that did not reproduce. That is exactly right, and the record is corrected here rather than quietly. What reproduced — three of four, exactly
with both controls lit on both legs: a bogus key → 1 ⛔ What did NOT reproduce — my sentence was wrong in two places
⇒ The changeset and PR body now say only what was measured, and they name the three unmoved neighbours rather than leaving them to be assumed. ⭐ Why this matters more than the wording. My verdict's own instruction was "⛔ Re-measure all four yourself before writing them down … If any does not reproduce, stop and report rather than writing a softer version." One did not reproduce, and the seat reported instead of softening. Had it written the sentence I handed it, this PR would have shipped a false consequence in a release-note input — the exact defect class card objectui#7678 exists to track. Land-prep verified by me, not taken on trustThe PR's own diff is still the same eight files — none added, none dropped — and every behaviour carrier is byte-identical in count to the reviewed head The
Generated by Claude Code |
Fixes #7654
Retires
displayModeunder ADR-0049 enforce-or-remove — thedisplayModehalf of the card, per the maintainer's ruling B recorded in comment 5548848737 (director seat, decision batch #44, verbatim 「同意」). ThetriggerIconhalf landed in PR #7669. A closing keyword this time, on purpose: the ruling states that once this merges the card has no open half, andneeds-user-decisionwas stripped in the same stroke.What changed
packages/types/src/complex.ts—ChatbotSchema.displayMode?: 'inline' | 'floating'?: nevertombstone; the JSDoc points attypeas the replacement and cites discriminator prong 2 (advertised atCHANGELOG.md:578, release 3.3.0)packages/types/src/complex.ts—ChatbotFloatingSchema.displayMode?: never, pointing at theChatbotSchemacommentpackages/plugin-chatbot/src/renderer.tsx—chatbot-floatinginputsDisplay Modecontrol,defaultValue: 'floating'packages/plugin-chatbot/src/renderer.tsx—chatbot-floatingdefaultPropsdisplayMode: 'floating'content/docs/plugins/plugin-chatbot.mdxdisplayModerow in the floating key tabletypeselects the presentation, and that stored documents are unaffectedA control is restated, never deleted into a vacuum (objectui#7070): the restatement is the tombstone's guidance plus the changeset.
Scope reading, stated so it can be objected to: the ruling names
ChatbotSchema.displayMode. The floating face's copy was minted by #7705 after the batch was formulated, carrying the explicit note that the retiring PR "must find the member on both faces as ruled"; leaving that copy typed would keep the published face of the one registration that offered the control teaching the switch — the exact defect the ruling's rejection of option C names. So both faces carry the tombstone.Premise re-measured on this branch's base (
a00db9ef9), with a lit controldisplayModegit grep -w, tracked files, build output excluded: the two declarations, theinputsentry and thedefaultPropsseed, the doc comments / ledger entries / test pins beside them, one CHANGELOG line, and two unrelateddisplayModeprops (GridField,MasterDetailForm). No renderer read.renderer.tsx:498and the seed at:520ChatbotSchemaLit control: the same query over
floatingConfig, a key that IS read, returns 79 lines. Sibling checkout (../objectstack): the sweep fordisplayModereturned zero, but so did its controls (chatbot-floating, achatbotnode type) — that tree authors no chatbot nodes at all, so it is reported as no population, not as zero authors measured.The tombstone is loud-vs-silent on this carrier — re-measured, not restated
ChatbotSchemaextendsBaseSchema, which carries an index signature. Measured on THIS member withtsc -p packages/types/tsconfig.test.jsonin three tree states — LIVE (the base), DELETED (both declarations removed; mutation proved on disk by anchored grep 2 to 0 and blobfd59df10to98e7d66d, restored under anEXIT INT TERMtrap with absolute paths and verified by an emptygit diff HEADplus blob equality back tofd59df10), and TOMBSTONED (this branch).FloatingChatbotConfig, which has no index signature, was the lit control carrier in every run.'floating'(well-typed)TS2322'bogus'(wrong-typed)TS2322TS2322'floating' as constTS2322TS2353TS2353TS2353TS2559TS2559TS2559anyundefinedDeleted, the member reads as
anyand even the wrong-typed value goes silent. Tombstoned, presence with any value is refused — a diagnostic deletion cannot produce on this carrier at all. Both faces gave identical rows. One instrument defect of mine, corrected before the readings above were taken: the first widened case wrotedisplayMode: 'floating'withoutas const, which widened tostringand produced aTS2322on LIVE that was literal widening, not presence; the table is theas constre-run.Runtime: zero validation before, zero after — and the tripwire
No
retirementTombstone()half, deliberately:displayModehas never had a Zod arm (it sits in both twins'UnmirroredDeclaredledger entries) andBaseSchemais.passthrough(), so every stored document the designer wrote —displayMode: 'floating'— parses green before and after. Minting an arm is objectui#6152's axis.packages/types/src/__tests__/chatbot-display-mode-retired.test.tspins both twins'shape.displayModeas undefined, and the same file carries the parse-green pin.Correction, measured at
6b254df25and now in the tree. An earlier revision of this PR said the parse-green pin stays green for a house-style non-strict arm and reddens only for a.strict()mirror, citing objectui#7678 item 2. That transposition is false. Item 2 measured OBJECT-MIRROR shapes;displayModeis a scalar union, whose house-style arm is an enum. Minting an optionalz.enumover'inline' | 'floating'onChatbotSchema's twin — injected at a counted anchor (marker 0 to 1), blob53fd0ed0to86793dd7, restored under a trap on EXIT INT TERM with the blob back to53fd0ed0andgit diff HEADempty — reddens both instruments:tsc -p packages/types/tsconfig.test.jsonwith exactly 1 error,zod-mirror-parity.test.ts(1586,14): error TS2322, ANDvitestwith exactly 2 failures — the shape pin and the parse-green line, because that line asserts'anything-at-all'parses and an enum arm refuses it. So the tripwire fires harder than the old prose claimed; it does not take a.strict()mirror. Both in-tree sentences (this file's shape-pin comment and the 7655 TRIPWIRE comment) now say the measured thing, and the shape-pin test title no longer calls itself "the tripwire that fires". The two OLD objectui#7678 sites are untouched and keep their own carrier. Same tripwire shape as #7669.Fixture triage in
chatbot-registration-authoring-faces-7655.test.tsdisplayMode: 'floating'— they used the alias; the key is dropped from both.displayMode: 'bogus'. That pin's purpose is "a wrong value on a declared union key is refused, not swallowed asany"; a tombstone refuses presence, a different fact, so the pin moves toprocessVisibility: 'bogus'and the tombstone gets its own file.Equalpins on the member flip from the union toundefinedon both faces (and still tellundefinedfrom theanya deletion leaves).Evidence
Instrument named:
tsc -p tsconfig.test.json --noEmit; program membership proved with--listFiles(complex.ts,complex.zod.ts, the 7655 test and the new test are all inputs). Type-level assertions are erased at runtime, so a green vitest run is not evidence about them.b8244f051:@object-ui/typesbuilt (dist/complex.d.tscarries 2 tombstones);plugin-chatbotresolves@object-ui/typesthroughpackages/types/dist/complex.d.ts(--listFiles), so this reads the REBUILT declaration; baselinetype-checkexit 0 with the script name echoed; aChatbotFloatingSchemaliteral carryingdisplayMode: 'floating'appended torenderer.tsx(marker 0 to 1, blob8529f2cbtoed0f9d5b) turned it red with exactly ONE error, at the injected line —TS2322: Type 'string' is not assignable to type 'undefined'(the printed type isundefined, as feat(types)!: retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654) #7669 reported for?: neverwithoutexactOptionalPropertyTypes) — restored under trap, emptygit diff HEAD, blob back to8529f2cb.origin/main, head5a0059834):pnpm --filter @object-ui/types buildexit 0;pnpm --filter @object-ui/types type-checkexit 0 (all three tsc projects, script name echoed);pnpm --filter @object-ui/plugin-chatbot type-checkexit 0 (echoed);pnpm exec vitest runover the five namedpackages/typestests pluspackages/plugin-chatbot/— 42 files / 534 tests passed (37 plugin-chatbot files + 5);pnpm lintwhole repo —Tasks: 47 successful, 47 total, exit 0, 0 errors, no narrowing;check:control-bytes,check:doc-fences,check:doc-types,check:doc-snippets,check:published-dist,check:published-tsconfig-exclude,check:shell-escape-residue,check-changeset-presence,check-changeset-no-major— all exit 0, each read from its own verdict line. Heavy runs went throughos-verify-lock.sh; exit codes were captured by redirect before any pipe.NOT GOVERNEDfor all 8 paths in the diff.Serial constraints
#7732 and #7726 both landed while this was in flight, as did #7731 (the derived parity population).
origin/mainwas merged in (5a0059834, no rebase, no force-push);merge-treewas clean against both PR heads and againstorigin/main; the 7655 test'sdisabledpins from #7726 and the parity ledger's new derivation were re-read after the merge, and the union above is the merged head's.Clause-②: yes —
needs:contract-reviewA public type member retires on two exported faces; the ruling says so explicitly.
Accept-set change, one line per face (the changeset carries the same list, since it is the release-note input):
displayModeagainst either chatbot face used to compile and now does not.safeValidateSchema). Nothing changes at all: a stored document carrying the key parses green before and after, value kept.chatbot-floatingproperty panel, and newly created nodes no longer carry the key.inputsgo 20 to 19 and itsdefaultProps9 keys to 8, so the manifest projected from them no longer lists the prop.validateTreeon a storedchatbot-floatingnode carryingdisplayModegoes 0 to 1 diagnostic — codeunknown-prop, severitywarning, messagechatbot-floating has no prop "displayMode"(the real message wraps the tag in angle brackets; spelled out here because GitHub's body sanitizer eats tag-shaped fragments even inside backticks). The props interfacegenerateDtsderives from the sameinputsgoes 20 members to 19, losing itsdisplayMode?: stringline, so a.tsxpage written against those generated intrinsics no longer type-checks the attribute. Author-time only — no stored document stops parsing and nothing at render moves:compile()still returns a tree carryingdisplayMode: 'floating'on both legs, and awarningnever blocks a page (the page renderer filters toseverity === 'error'first, atpage.tsx:552-553).Semver:
@object-ui/typesminor with a BREAKING banner and the launch-window banner,@object-ui/plugin-chatbotpatch;majoris CI-refused (AGENTS.md 版本号策略). Opened as a draft; not flipped ready, not enqueued. Session:session_01KbJQ1y1J12nZxYzFWhP8Q3.Land-prep round — re-measured at
6b254df25(text-only)Three files changed in this round and every hunk is prose: the changeset's accept-set list, one
it(...)title string, and two comment blocks. No runtime, type or manifest behaviour moves — the round's diff contains no executable line.The four manifest-face readings, re-measured here rather than inherited. Both legs ran in this worktree; the LIVE leg is
packages/plugin-chatbot/src/renderer.tsxrestored toorigin/main's content (blob8529f2cbto3dbd2634, anchored grep on the removed control and the removed seed 0 to 1, restored under a trap on EXIT INT TERM with the blob back to8529f2cbandgit diff HEADempty). vitest resolves every@object-ui/*specifier through the root config'sresolve.aliastosrc, and the harness imports the registration relatively, so both legs read the mutated SOURCE — the differing readings below are themselves the proof that no staledistwas consulted.ComponentRegistry.getMeta('chatbot-floating').inputsdisplayModepresentdefaultPropsdisplayMode: 'floating'validateTreeon a node carryingdisplayModeunknown-prop, severitywarningunknown-propwarningunknown-propwarningenableMarkdown)generateDtsprops interface for the tagdisplayMode?: stringThree neighbours were measured and are NOT moved, stated so the face is not read wider than it is.
compile()returns a tree still carryingdisplayMode: 'floating'on both legs — the value is not stripped anywhere I could measure, so the codegen consequence is a type one (the generated interface stops declaring the key), not a silent attribute drop.os validaterunssafeValidateSchema, the Zod path, and is silent on this key before and after. And the build-timesdui-intrinsics.d.tsartifact is generated from the PUBLIC tier (31 components), which does not containchatbot-floatingon either leg, so that published file is untouched.Parity ratchet, still lit at
6b254df25. Removing'displayMode'from theChatbotSchemaUnmirroredDeclaredentry (blob681ed828to1f7a2b33, anchored count 1 to 0 for the old spelling and 0 to 1 for the new, restored under a trap with the blob back to681ed828) givestsc -p packages/types/tsconfig.test.jsonexactly 1 error, atzod-mirror-parity.test.ts(1586,14). Under that same mutationviteston the parity file stays 15/15 green: the runtime census is blind to a per-key mismatch, so the instrument for this pair istsc— CI's Type Check — and vitest must never be cited as the parity evidence.Union at
6b254df25, exit codes captured by redirect before any pipe, each verdict read from the tool's own line:tsc -p packages/types/tsconfig.test.json --noEmitexit 0 with 0error TSlines;pnpm --filter @object-ui/types buildexit 0,dist/complex.d.tscarrying 2displayMode?: nevertombstones;pnpm exec vitest run packages/types/ packages/plugin-chatbot/— 156 files / 2497 tests passed;pnpm lint(repo-wideeslint) —Tasks: 47 successful, 47 total, exit 0, 0 errors, no narrowing;check-changeset-presence,check-changeset-no-major,check-changeset-fixed,check-changeset-overwrite,check:control-bytes,check:shell-escape-residue,check:doc-fences,check:doc-types— all exit 0.check:doc-snippetsreturned its own PRECONDITION NOT MET (exit 2) in this worktree (nodistfor 26 packages); that is "could not run", not a failure, and this round touches no document. Heavy runs went through the container's shared verify lock.origin/mainhad moved to81a2eb1fb; it was merged in atc8e5f66fb— no rebase, no force-push, and the push was a fast-forward (5a0059834..6b254df25).Session:
session_01KbJQ1y1J12nZxYzFWhP8Q3.Generated by Claude Code