fix(plugin-chatbot): fence chatbot-floating's raw props spread - #8077
Conversation
chatbot-floating ended its <FloatingChatbot> element with a raw
{...props} spread, LAST, where its two sibling registrations (chatbot,
chatbot-enhanced) spread {...toDomProps(props)} FIRST. Per the card's
ruling (fence, not declare), moving it to the head and filtering it
through toDomProps closes all three consequences at once: a sent
message now renders on a floating chatbot (the authored `messages`
seed no longer overrides the live runtime messages), processVisibility
/ surface / showAvatars go dark on chatbot-floating nodes (matching
what ChatbotFloatingSchema has always declared), and displayMode /
systemPrompt / model stop leaking as DOM attributes on the panel root.
The three tripwire pins in renderer.authoring-faces-7655.test.tsx
(section 4) flip from lit to dark, deliberately, as planned when they
were written. New coverage owns the two consequences that were not
pinned anywhere before this fix: renderer.floating-spread-fence-7708.test.tsx.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
✅ 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
|
Landing — in-seat contract review PASSED; standing down on one red that is not this PR'sBy the dispatching seat (
|
job step 7 Start ObjectStack backend (published packages) |
job | |
|---|---|---|
main 09-05, run 33950494089 job 101264218858 |
✅ success in 76s | ✅ success — the lit control |
main 09-06, run 34017174769 job 101442890465 |
❌ failure after ~6 min | ❌ failure, steps 8–14 all skipped |
conclusion: success at the RUN level while its job fails. ⛔ Never read a workflow run's conclusion as evidence in this repo.
No fix exists to port — the break is undiagnosed and carded as objectui#8084, with the published-package hypothesis named as the first thing to test. ⛔ No re-run is being spent here: this is a reproducible base-red, not a flake, and a re-run would only confirm what two main runs already establish.
⇒ Per the standing rule, this is one comment naming the failing check and why it is not this PR's, and then the PR proceeds. Stripping needs:contract-review, flipping ready, enqueuing.
Generated by Claude Code
|
Follow-up to the correction:
|
loading, showAvatars, userAvatar, assistantAvatar, markdown and height were declared on ChatbotSchema, mirrored on its Zod twin, and read by no plugin-chatbot registration: a schema.KEY census per ComponentRegistry.register body returns 0/0/0 for all six, with placeholder 1/1/1, messages 1/1/1, userAvatarUrl 1/1/1, maxHeight 1/1/0, floatingConfig 0/0/1 and processVisibility 0/1/0 lit on the same instrument. Each becomes `?: never` on complex.ts plus retirementTombstone() on complex.zod.ts — both halves, the convention #6972 / #6355 / #7779 already carry. Deleting them was the wrong route: all six have a Zod arm, and BaseSchema is .passthrough() with a [key: string]: any index signature, so an undeclared key is KEPT, not refused. Enforce was refused per key: <Chatbot>, the component this registration renders, declares none of the six, so enforcing means growing a component prop or publishing a second spelling of a key that already works. showAvatars is the one key the FENCE turned dark rather than a key nothing ever read: <ChatbotEnhanced> has such a prop and chatbot-floating's raw props spread delivered an authored value to it until #7708 ruled fence (PR #8077). The distinction is recorded in the tombstone comment, the changeset and the pin. processVisibility is NOT folded in — chatbot-enhanced reads it (0/1/0) — and is pinned live as the scope control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
…es (#8154) loading, showAvatars, userAvatar, assistantAvatar, markdown and height were declared on ChatbotSchema, mirrored on its Zod twin, and read by no plugin-chatbot registration: a schema.KEY census per ComponentRegistry.register body returns 0/0/0 for all six, with placeholder 1/1/1, messages 1/1/1, userAvatarUrl 1/1/1, maxHeight 1/1/0, floatingConfig 0/0/1 and processVisibility 0/1/0 lit on the same instrument. Each becomes `?: never` on complex.ts plus retirementTombstone() on complex.zod.ts — both halves, the convention #6972 / #6355 / #7779 already carry. Deleting them was the wrong route: all six have a Zod arm, and BaseSchema is .passthrough() with a [key: string]: any index signature, so an undeclared key is KEPT, not refused. Enforce was refused per key: <Chatbot>, the component this registration renders, declares none of the six, so enforcing means growing a component prop or publishing a second spelling of a key that already works. showAvatars is the one key the FENCE turned dark rather than a key nothing ever read: <ChatbotEnhanced> has such a prop and chatbot-floating's raw props spread delivered an authored value to it until #7708 ruled fence (PR #8077). The distinction is recorded in the tombstone comment, the changeset and the pin. processVisibility is NOT folded in — chatbot-enhanced reads it (0/1/0) — and is pinned live as the scope control. Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S Co-authored-by: Claude <noreply@anthropic.com>
Fixes #7708
What
chatbot-floating's registration (packages/plugin-chatbot/src/renderer.tsx) ended its FloatingChatbot element with a rawpropsspread (object-spread, three dots), LAST — where its two siblings (chatbot,chatbot-enhanced) spreadtoDomProps(props)(object-spread) FIRST. Per the card's ruling (triage comment 5550678895 on #7708, PM dispatch comment 5559736018): fence it, matching the siblings. Option 2 (declare the three keys onChatbotFloatingSchema) was ruled out — it would fossilise an accidental channel as contract (AGENTS.md #0.1) and leaves themessagesoverride untouched.This closes all three consequences at once:
messagesseed used to override the livemessagesprop (bound to the runtime messages) on every render (the raw spread landed after it), so neither the user's own message nor anautoResponsereply ever appeared — the p2 half of the card's grade. Fixed.processVisibility,surfaceandshowAvatarsgo dark onchatbot-floatingnodes. These reached the panel's ChatbotEnhanced component unfiltered even thoughChatbotFloatingSchemanever declared them. The three tripwire pins inrenderer.authoring-faces-7655.test.tsx(section 4, from finding(types):ChatbotSchemapinstypeto'chatbot', sochatbot-enhancedandchatbot-floatingnodes have no authoring-face type #7655/PR feat(types): one named authoring-face type per chatbot registration — ChatbotEnhancedSchema and ChatbotFloatingSchema (objectui#7655) #7705) are flipped from lit to dark, deliberately, as that test's own docblock anticipated.displayMode/systemPrompt/modelstop leaking as DOM attributes on the panel root (objectui#4425's leak class, the oneplugin-chatbotregistration that hadn't closed it).systemPrompt/modelare still read normally by name — only the second, unfiltered forward is gone.Falsifiable premise checked before committing to the fence (per dispatch): every member
ChatbotFloatingSchemadeclares is consumed either byuseObjectChat(...)offschemadirectly, or forwarded by name below the spread — none depend on the raw-spread channel. Verified by readingChatbotFloatingSchema's own doc comment (packages/types/src/complex.ts) against the registration body before editing. No fork: fencing widens nothing and narrows only the accidental channel, matching the dispatch's Clause-② = no.Why
Quoting the card: "⛔ Note the asymmetry: only the first option addresses the p2 half." The
messagesoverride is a user-visible functional break (a floating chat cannot show anything the user types), not a hygiene finding, and only fencing fixes it.Tests
packages/plugin-chatbot/src/__tests__/renderer.floating-spread-fence-7708.test.tsx— the fix's own coverage, previously pinned nowhere:autoResponsereply) renders, through the realSchemaRendererhost.systemPrompt/model/displayModeno longer land as DOM attributes on the panel root.packages/plugin-chatbot/src/__tests__/renderer.authoring-faces-7655.test.tsxsection 4 — the three tripwire pins flip from lit to dark onchatbot-floating;chatbot-enhancedcontrol cases unchanged (still lit — it reads these by name).packages/types/src/complex.ts:ChatbotFloatingSchema's doc comment updated to describe the closed channel — no type-shape change.Run locally, from the repo root:
pnpm exec vitest run packages/plugin-chatbot/— 38 files / 458 tests passed.pnpm exec vitest run packages/types/— 131 files / 2418 tests passed.pnpm --filter @object-ui/plugin-chatbot type-check— pass.pnpm --filter @object-ui/types type-check— pass.node scripts/check-handler-key-read-sites.mjs— pass.node scripts/check-control-bytes.mjs— pass.node scripts/check-published-tsconfig-tooling-exclude.mjs— pass.node scripts/check-changeset-fixed.mjs,check-changeset-no-major.mjs,check-changeset-presence.mjs— pass.eslinton every changed file — 0 errors (pre-existinganywarnings only, none introduced by this diff).Not run locally, both release-time/nightly gates per their own file headers (require a full-repo build the CI-per-PR path deliberately skips):
check:published-distandcheck:sdui-registration-pins(the latter also unrelated in substance — nosideEffectsarray or registration key changed here).Risk / rollback
Behavior change on a shipped registration, shipped
minorper this repo's changeset policy (breaking changes shipminorhere, notmajor— see.changeset/7708-chatbot-floating-spread-fence.md). A document that authoredprocessVisibility/surface/showAvatarson achatbot-floatingnode to rely on this accidental channel loses that effect; author them on achatbot-enhancednode instead. Rollback is reverting this PR (the raw spread returns; no data migration involved).Generated by Claude Code