fix(types): SchemaRegistry names all three chatbot registrations - #8145
Conversation
`packages/plugin-chatbot/src/renderer.tsx` registers `chatbot`, `chatbot-enhanced` and `chatbot-floating`; `SchemaRegistry` mapped one of them. `ComponentType = keyof SchemaRegistry` is the published union, so a consumer discriminating on it was told two registered keys do not exist — while `packages/cli/src/utils/known-schema-types.ts` kept its own parallel list containing both, precisely because this map did not. The two entries can be honest and reachable only since objectui#7655 published `ChatbotEnhancedSchema` / `ChatbotFloatingSchema` from this package: each pins `type` to its own key, and each registration already takes that exact type as its `schema` parameter, so the map's value and the renderer's prop type are one declaration. Unlike the `kanban` case objectui#7645 measured, naming them costs this zero-workspace-dependency layer no plugin import. Pinned in two channels: compile-time (`tsc -p tsconfig.test.json` — the keys survive in `keyof`, each value IS its face, each value's `type` literal IS its key) and runtime (an AST census of the interface source, plus each key selecting its own arm through `safeValidateSchema`). Scope is these two keys, not a sweep of the map's other entries. 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
|
|
Contract review — PASS — PR #8145 (objectui#7704), head Reviewed-by: Diff read by ref against ① Derived judgments — every accept-set and public-surface change named and judgedDirection: WIDENS, and the changeset says so rather than eliding it. Honesty of the two entries — verified independently by this seat, ⛔ not taken from the report. Anchors re-measured on ⇒ each registration already takes as its ⭐ The cross-slot fence, which is the one thing I would not accept on assertion. Both faces are
⇒ the Pin quality — this is the card's actual deliverable and it is done properly.
② semver
③ Boundary flags
VerdictPASS. Same stroke: ⛔ Not landing yet. CI has not converged — enumerated 32 check runs against
Generated by Claude Code |
|
Landing — PR #8145 (objectui#7704), head Pre-landing three: ① Contract review PASS (comment 5561967644) at ② CI converged, enumerated 32 against ⭐ ⭐ Two gates the dev declared NOT MEASURED locally — The single failure is ③ Ready flipped, auto-merge SQUASH enabled ( ⭐ Landing control established BEFORE the queue picks it up, so the post-merge confirmation is a reading and ⛔ not a hopeful grep:
⇒ the zero is a reading on a live file. On merge this seat re-runs exactly these and expects 1 / 1. One judgement recorded, ⛔ not silently tidied. The pin docblock and PR body cite the
Generated by Claude Code |
Fixes #7704
What was wrong
packages/plugin-chatbot/src/renderer.tsxregisters three components —chatbot(:62),chatbot-enhanced(:241),chatbot-floating(:379).SchemaRegistrymapped one of them. SinceComponentType = keyof SchemaRegistryis the published union, a consumer discriminating on it was told two registered keys do not exist.The asymmetry that shows which half was wrong:
packages/cli/src/utils/known-schema-types.ts:83-84lists both keys — the CLI keeps its own parallel list precisely because the map did not.Why the entries can be honest now
This map's value has to be the type the registered renderer honours, and until objectui#7655 there was none to point at:
ChatbotSchemapinstypeto'chatbot', and each registration's real key set lived in an anonymousChatbotSchema & { ... }intersection local to the renderer file. objectui#7655 publishedChatbotEnhancedSchemaandChatbotFloatingSchemafrom@object-ui/typesitself, which makes the two entries simultaneouslytypeto its own key, and each registration already takes that exact type as itsschemaparameter (renderer.tsx:256,:394), so the map's value and the renderer's prop type are one declaration (the property the'kanban'arm gained in objectui#7664); and@object-ui/typeshas zero workspace dependencies and both faces are its own declarations. This is the difference from thekanbancase objectui#7645 measured, where the honoured type lived in a plugin this layer cannot import.check:phantom-deps— the gate that caught that attempt — is green, re-measured on this branch.The pin is the real deliverable
Without it this card regresses invisibly, which is how it arose.
packages/types/src/__tests__/schema-registry-chatbot-keys-7704.test.tspins it in two channels:tsc -p packages/types/tsconfig.test.json, chained off the package'stype-checkscript): the keys survive inkeyof; each value IS the face its renderer honours; each value's owntypeliteral IS its key; and each registry value is the same declaration as itsComplexSchemavalidator arm. Proven to be in that program:--listFileslists the file at position 440 of 595.tsc— plus each key selecting its own arm throughsafeValidateSchema, with the sibling key refused attypeon otherwise identical documents.Ablation — the pin is lit, and it is per-key
Both legs mutate on disk after the implementation was committed, and restore under a trap; the on-disk mutation is proven by a
grep -cFon the exact removed text (1 → 0) and agit hash-objectdiff, and each restore leg is proven by the blob hash matchingHEADagain.tsc'chatbot-enhanced'chatbot-enhanced(lines 90/96/103/116)eb39756db,git diff HEADempty'chatbot-floating'chatbot-floating(lines 91/97/104/119)eb39756db,git diff HEADemptyNeither leg reddens the other key's pins — the pins are per-key, not one blanket check.
Cross-package reverse verification, against the rebuilt
.d.ts@object-ui/typeswas rebuilt (dist/registry.d.ts:89-91carries all three keys) and probed frompackages/plugin-chatbot, resolving@object-ui/typesby package name so the read goes throughdist, not the source tree:const a: ComponentType = 'chatbot-enhanced'(and the floating twin, and bothSchemaRegistry[key]['type']reads):tscexit 0;const bad: ComponentType = 'chatbot-does-not-exist':tscexit 2,TS2322: Type '"chatbot-does-not-exist"' is not assignable to type 'keyof SchemaRegistry'.The negative leg is what makes the positive one mean something: the union is a real literal union, not widened to
string.Verification
Measured on the final commit
8c1c4019d.pnpm --filter @object-ui/types buildpnpm exec vitest run packages/types/pnpm --filter @object-ui/types type-checktsc --noEmit+tsconfig.examples.json+tsconfig.test.json)check:control-bytes·check:phantom-deps·check:unreferenced-sources·check:doc-types·check:self-importcheck-changeset-presence·-no-major·-fixed·-overwritecheck-type-check-coverage·check-lint-coverageEvery exit code was captured before any pipe (
cmd > log 2>&1; EXIT=$?), and each verdict above is the gate's own printed line.Declared narrowings (CI runs the full farm):
turbo ls --affectedreports 43 packages —@object-ui/typesis the graph root. That set is a graph-shape artifact, not a runtime risk surface: the changed file emits no runtime code (dist/registry.jsisexport {}, 207 bytes of licence header). The only real surface is type-level, and the only reference toSchemaRegistryorComponentTypeanywhere outsidepackages/typesis two comments (plugin-kanban/src/types.ts:28,app-shell/src/layout/AppSidebar.tsx:251) — no downstream code consumes either symbol.--format jsonreports 2 files, 0 errors, 0 warnings, exit 0. Population per eslint's own config (files: ['**/*.{ts,tsx}']minus itsignores): 4265 tracked files. Invariance: the config declares noparserOptions.projectand noprojectService(0 occurrences), so type-aware linting is off and this diff cannot move the verdict of any file it does not touch.check:sdui-registration-pinsandcheck:readme-exportsare NOT MEASURED, not red — both are prerequisite failures needing builds this seat did not run. The first prints❌ No console build to weigh at apps/console/dist/assets. This is exit 2, not a pass. The second's 484 findings are 481 of the formits type entry ./dist/index.d.ts is not on disk -- run pnpm build firstplus 3 floor failures caused by the same absence (packagesRead: found 2, floor is 25); zero of them namepackages/types. Neither gate can be affected by a diff that adds no registration and no README line.Scope
Two keys whose authoring faces now exist — not a sweep of the map's other entries, which objectui#7665 holds, and this PR does not answer objectui#7645's generalization question about whether every
SchemaRegistryvalue must be the type its renderer honours.Region fence honoured: exactly three files, additive only (288 insertions, 0 deletions).
packages/types/src/complex.tsandzod/complex.zod.tsare untouched (imported from only) — they belong to the concurrent objectui#7703, which retires members ofChatbotSchema, a different interface from the two faces named here. No semantic collision was measured:ChatbotEnhancedSchemaandChatbotFloatingSchemapick onlyChatbotSharedKey(plusmaxHeight/processVisibility), and the six members #7703 retires are the legacy ones no registration reads by name.Review posture
⛔ Stays DRAFT.
needs:contract-reviewis attached: adding two entries widens the publishedComponentTypeunion, which is a public-face change, not a spelling. It does not enqueue while that label is on it.check-governed-queue-guard --testconfirms the diff is not a governed surface (3 paths checked, none matched), so the draft hold here is the contract-review label alone.Changeset:
@object-ui/types: minor(majoris forbidden by repo convention, andcheck-changeset-no-majoris green).🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code