feat(types): one named authoring-face type per chatbot registration — ChatbotEnhancedSchema and ChatbotFloatingSchema (objectui#7655) - #7705
Conversation
… ChatbotEnhancedSchema and ChatbotFloatingSchema (objectui#7655)
`plugin-chatbot` registers three components and `@object-ui/types` published
one face for the family with `type` pinned to `'chatbot'`. Add one interface
per registration, each declaring exactly the keys its registration reads
(read-site-censused per key with lit controls), with the twenty shared keys
picked off `ChatbotSchema` by name so they stay one declaration. Zod twins in
lockstep, registered in the parity ledger (two pairs born ledgered: three
runtime-slot refusals each in KnownDrift; `displayMode` | `floatingConfig`
carried unmirrored onto the floating pair, moved off `ChatbotSchema`'s entry).
`displayMode` crosses onto `ChatbotFloatingSchema` UNCHANGED; its fate stays
with objectui#7654. `chatbot-floating` now consumes the host-evaluated
`disabled` verdict like its siblings — the raw read it replaced was already
overridden by the trailing `{...props}` spread on every render.
Docs: the floating example is a typed `tsx` fence again, plus a typed
`chatbot-enhanced` example; per-face scope notes on the properties tables.
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 —
|
…tbot-registration-authoring-types # Conflicts: # packages/types/src/__tests__/handler-keys-json-refusal-6124.test.ts # packages/types/src/__tests__/zod-mirror-parity.test.ts
✅ 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
|
…Schema, state the floating spread channel truthfully, measure the parity counts (objectui#7655 review) Remediation of the contract review on PR #7705 (REFUSE, seven items): - ChatbotSchema keeps `displayMode` and `floatingConfig` (declarations verbatim from the base); ChatbotFloatingSchema declares the same two. Restores the typed read of both members on the published face and the objectui#7669 `triggerIcon` tombstone's reach on `chatbot` nodes, now pinned on the node in both faces' tests. The "moved" narrative is gone. - ChatbotFloatingSchema's docblock, the census pin, the docs rows and the changeset no longer claim `processVisibility` / `surface` / `showAvatars` are dark on `chatbot-floating`: the registration's trailing raw `{...props}` spread carries them to the panel today (measured through the real host, lit/dark pairs, `chatbot-enhanced` as control). The census counts NAMED reads; the spread is a second channel, pinned as a tripwire in the plugin's real-host test and carded as objectui#7708. - Every `displayMode` tense reads the ruled state: objectui#7654 ruling B (retire; tombstone, control and seed removed) executes in that card's own PR; this PR carries the key untouched on both faces. - zod-mirror-parity prose rewritten from measurement: 156 pairs at head (154 at base; the inherited "160/162" was six too high), KnownDrift 42/63 after the main merge, UnmirroredDeclared 16/98, 114 / 139 derivatives. - Changeset names the new published symbol `ChatbotSharedKey` (`@object-ui/types/complex` subpath) and the `floatingConfig` JSDoc fix. 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
|
…tbot-registration-authoring-types # Conflicts: # packages/types/src/__tests__/zod-mirror-parity.test.ts
✅ 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 re-review (round 2) —
|
Fixes #7655
What
One named, importable authoring-face type per
plugin-chatbotregistration.ChatbotEnhancedSchema(type: 'chatbot-enhanced') andChatbotFloatingSchema(type: 'chatbot-floating') joinChatbotSchemain@object-ui/types, with Zod twins in@object-ui/types/zodand both discriminants routed byComplexSchema; the two registrations inpackages/plugin-chatbot/src/renderer.tsxtype theirschemaas those faces and drop the anonymous intersections. The docs' floating example is a typedtsxfence again — the card's own evidence — plus a typedchatbot-enhancedexample.ChatbotSchemaitself is unchanged.Clause-②: yes — two new published node types, two new Zod twins, one new published type alias (
ChatbotSharedKey, below). Draft,needs:contract-review. Changeset:@object-ui/types: minorwith the accept-set change and the new symbol spelled out;@object-ui/plugin-chatbot: patch.Contract-review remediation — what changed between
30a53bd32and4579bdff9The review (PR comment 5550439212) returned REFUSE on seven items. Each is addressed in this push, and every measurement below was re-taken here rather than copied from the review.
processVisibilityandsurfacewere "not forwarded — the panel renders the component defaults" (inChatbotFloatingSchema's JSDoc, emitted intodist/complex.d.ts, and in the docs rows). Re-measured through the realSchemaRendererhost: thechatbot-floatingregistration ends itsFloatingChatbotelement with a raw{...props}spread, LAST, so every authored key reaches the panel'sChatbotEnhancedunfiltered —showAvatars,surfaceandprocessVisibilityare LIVE on a floating node (table below). The JSDoc, the census pin's docblock, the docs rows and this body now say so: the census counts NAMEDschema.KEYreads, the raw spread is a second channel, and the face neither declares nor promises it. The three readings are pinned as a TRIPWIRE in the plugin's real-host test (ablation 2 shows it bites), and the channel is carded as objectui#7708 for a fence-vs-declare ruling — not decided here.displayModetense reads the ruled state. objectui#7654 was ruled at 2026-09-05T02:48:39Z (comment 5548848737, maintainer verbatim 「同意」, ruling B: retire —?: nevertombstone onChatbotSchema, designer control anddefaultPropsseed removed). The eight "parked on a maintainer decision" lines (changeset,complex.tstwice,complex.zod.ts, the parity ledger, the census test) and this body now read: ruled B; execution is finding(types/plugin-chatbot):chatbot-floatingdeclares two keys nothing reads -displayMode(designer control included) andFloatingChatbotConfig.triggerIcon#7654's own PR; this PR carries the key untouched on both faces so that PR finds the member exactly as ruled.displayModeandfloatingConfigare restored onChatbotSchema— the declaration lines anddisplayMode's three JSDoc lines byte-identical to the base (diffed against6eebc54b6), the// --- Floating / FAB display mode ---heading included;ChatbotFloatingSchemadeclares the same two. TheUnmirroredDeclaredentry forChatbotSchemais back to its three keys with its base comment verbatim. The two "moved" pins are inverted intoassertionChatbotKeepsItsWholeFaceandassertionFloatingKeysStayTypedOnChatbot, and a third,assertionFloatingKeysHaveOneTypeOnBothFaces, pins the two members equal across the faces. The MOVED narrative is gone from every carrier. The measured reasons, verified here: on the first cutChatbotSchema['displayMode']and['floatingConfig']read asanyand{ type: 'chatbot', floatingConfig: { triggerIcon: 'Sparkles' } }compiled clean — the objectui#7669 tombstone had lost its reach onchatbotnodes, and nothing was red for it because that card's pins sit onFloatingChatbotConfigdirectly. Both are now pinned on the node (floating-chatbot-trigger-icon-retired.test.tsrefusestriggerIconthroughChatbotSchema.floatingConfigAND throughChatbotFloatingSchema.floatingConfig; the census test refusesdisplayMode: 'bogus'on achatbotnode), and ablation 1 shows every one of them turning red in the option-A state.ChatbotSharedKeyis named as a new published symbol: exported for TS4022, emitted intodist/complex.d.ts(5 hits), reachable through the@object-ui/types/complexsubpath, not re-exported from the entry. The "two keys MOVED" paragraph is replaced by "ChatbotSchemais unchanged".MIRRORSblock, theDeclaredentry count,Object.keys(MIRRORS).lengthat runtime) read 154 pairs where the prose said 160 — six too high, carried into every derivative — and the first cut had added its two to the stale number (162).mainfound the same rot independently at finding(types):DrillDownConfighas no zod mirror —drillDownis declared onChartSchemaandObjectDataTableSchemaand validated on neither #7352's contract review (its re-derivation: 155 afterDrillDownConfigSchema,UnmirroredDeclared13 / 94, union 14) and landed while this PR was open, so the merged file carries main's re-derived text plus this PR's two pairs, every figure re-measured on the merged tree by the same three instruments: 157 pairs,KnownDrift42 / 63,UnmirroredDeclared14 / 96 (2 / 12 spec-derived, 12 / 84 local; the seeded pair is gone), union of the two unmirrored ledgers 15, 115 pairs with noKnownDriftentry, 142 with no entry in either unmirrored ledger.floatingConfig's JSDoc was rewritten, on both faces. The base text said "Only used whendisplayModeis'floating'", which was false — the key is read bychatbot-floatingalone and forwarded toFloatingChatbot. The corrected sentence is what the restoredChatbotSchemamember carries too: its declaration line is verbatim, and that comment is the only difference from the base in the restored block (the diff shows exactly that).mainmerged — twice, no rebase, no amend, no force-push. First8ad218d58(merge commit368045e28; conflicts in the two ledger tests reconciled as the union of both sides — finding(types,components,docs):ui:alert-dialog's three surfaces name three DISJOINT key sets — every declared authoring key is inert, and every key the renderer reads is a type error #7104'sAlertDialogSchema.onActionplus this PR's six chatbot slots: 66 sites / 44 runtime slots / 22 retired). Then, becausemainmoved again under the first merge (fix(types): ChartDataSeriesSchema declares the six series keys the renderer reads (objectui#7546) #7684, fix(types): close two validating-face gaps — the ObjectQL union arms and the DrillDownConfig mirror (objectui#7363, objectui#7352) #7701),52c8cf741(merge commit4579bdff9, the head): twelve conflict hunks, all in the parity ledger's prose and figures, reconciled as main's finding(types):DrillDownConfighas no zod mirror —drillDownis declared onChartSchemaandObjectDataTableSchemaand validated on neither #7352 re-derivation plus this PR's two pairs and then re-measured (item 5). The head hasorigin/main52c8cf741as a parent;git merge-treeagainst it is clean.The ruling followed: one interface per registration, not a widened
typeThe governing line is quoted in the registration file itself (
renderer.tsx, thechatbotregistration's head comment): "objectui#6169, the #6172 family ruling: every component node has exactly one named, importable authoring-face type". WideningChatbotSchema['type']to the union of the three registered keys would give three nodes ONE type and re-open what #6169 closed. Each new face declares what ITS registration reads by name; the twenty keys all three read are picked offChatbotSchemaby name (ChatbotSharedKey, aPickkeyed by that alias) so they stay one declaration with one doc comment.The census — NAMED reads per key, per registration, with lit controls
Counts are
schema.KEYreads inside eachComponentRegistry.register(...)body ofrenderer.tsx(a script splits the file at the threeregister(calls and counts word-boundedschema.KEY), re-run onorigin/main8ad218d58, aschatbot/chatbot-enhanced/chatbot-floating:messages,placeholder,api,conversationId,systemPrompt,model,streamingEnabled,headers,requestBody,maxToolRoundtrips,onError,userAvatarUrl,userAvatarFallback,assistantAvatarUrl,assistantAvatarFallback,autoResponse,autoResponseText,autoResponseDelay,onSendChatbotSharedKey)showTimestampChatbotSharedKey)maxHeightChatbotSchema,ChatbotEnhancedSchemaprocessVisibilityChatbotEnhancedSchema(still onChatbotSchematoo — not retired here, #7703) — LIVE onchatbot-floatingthrough the raw spread, undeclared there (#7708)surfaceChatbotEnhancedSchema('card' or 'plain', objectui#6687) — LIVE onchatbot-floatingthrough the raw spread, undeclared there (#7708)enableMarkdown,enableFileUpload,onClearChatbotSchemanever declared themfloatingConfigChatbotSchema(unchanged) andChatbotFloatingSchemadisplayModeinputs0 / 0 / 1;defaultProps0 / 0 / 1ChatbotSchema(unchanged) andChatbotFloatingSchema— ruled retired on #7654, executed thereloading,showAvatars,userAvatar,assistantAvatar,markdown,heightshowAvatarsis nonetheless LIVE onchatbot-floatingthrough the raw spread (#7708; told on #7703)disabledis not redeclared on either face — it staysBaseSchema'sboolean | string(objectui#7087). Lit controls: the instrument reads non-zero exactly on the rows where the registrations differ (processVisibility,floatingConfig,surface), so the zeros are readings.The second channel, measured through the real host
chatbotandchatbot-enhancedopen their rendered element with{...toDomProps(props)}(whitelist-filtered, FIRST);chatbot-floatingends itsFloatingChatbotelement with raw{...props}(LAST), andFloatingChatbotforwards the rest ontoChatbotEnhanced. Rendered throughSchemaRendererProvider+SchemaRendererwith a lit/dark pair per key andchatbot-enhancedas the control — identical readings onorigin/main8ad218d58and on this branch (renderer.tsxis byte-identical between the first remediation commit and the head):chatbot-floatinglit / darkchatbot-enhancedlit / darkshowAvatars: true→ per-message avatar elementsurface: 'plain'→.max-w-2xlwrappersprocessVisibility: 'debug'→ raw tool name for a tool resultMechanism pinned on the whitelist itself:
toDomProps({ showAvatars, surface, processVisibility, className })keepsclassNameand drops the other three.One prediction missed, recorded: my first
processVisibilitycontrol onchatbot-enhancedused thedata-trace-idlink and read 0 where I predicted 1. Cause, read off the source:useObjectChat'snormalizeMessagesrebuilds every seed message with a fixed key set and dropstraceId, so the link cannot render on the named-read path — while on floating the raw spread overridesmessageswith the authored seed, which keepstraceId, so it does render there. The raw-tool-name marker ridestoolInvocations, which normalisation keeps, and lights on both; that is the marker the tripwire uses. The samemessagesoverride is why a sent message never renders on a floating node — measured (userShown: false, replyShown: falseon floating; bothtrueonchatbot-enhanced) and carded on #7708, deliberately not pinned here (pinning a bug as expected is the wrong shape).displayMode— ruled retired on objectui#7654; carried untouched here, on both facesRe-measured on the base: word-boundary
displayModeoverpackages/plugin-chatbot/src, tests excluded — two hits,renderer.tsx(a designerinputscontrol and adefaultPropsseed, both thechatbot-floatingregistration's), zero reads. #7654 ruled B on that measurement: the nodetypeis the one selector of presentation. The retirement — tombstone onChatbotSchema, control and seed removed — executes in #7654's own PR. Here the member stays onChatbotSchemabyte for byte and is declared onChatbotFloatingSchemawith the same three lines, unmirrored on both twins (UnmirroredDeclaredrecords it under both pairs), and the census test pins as a TRIPWIRE that any value still parses green — #7654's PR flips that pin with the ruling in hand. Nothing was retired, tombstoned, mirrored or made live.Zod twins and the ledgers — counts as measured on
4579bdff9zod-mirror-parity.test.ts: 157 pairs (155 onmainafter finding(types):DrillDownConfighas no zod mirror —drillDownis declared onChartSchemaandObjectDataTableSchemaand validated on neither #7352; 154 at this PR's original base).KnownDrift: each born with exactly its three runtime-slot refusals (onClear|onError|onSend, the objectui#6124 shape) — 42 entries / 63 keys (40 / 57 onmain).UnmirroredDeclared:ChatbotSchemakeepsdisplayMode|floatingConfig|requestBody;ChatbotFloatingSchemais born withdisplayMode|floatingConfig— 14 / 96 (13 / 94 onmain).RuntimeOnlyDeclared7 / 24 unchanged. Derivatives: 115 pairs with noKnownDriftentry, 142 with no entry in either unmirrored ledger.handler-keys-json-refusal-6124.test.ts: 66 sites, 44 runtime slots, 22 retired (this PR's six plus finding(types,components,docs):ui:alert-dialog's three surfaces name three DISJOINT key sets — every declared authoring key is inert, and every key the renderer reads is a type error #7104'sonActionfrom the first merge); the type-levelKeepsFunctionpins carry all of them.floatingConfig: noFloatingChatbotConfigmirror minted on either twin — objectui#6152's axis. The objectui#7669 tripwire (floating-chatbot-trigger-icon-retired.test.ts) now runs on BOTH twins (describe.each), and the tombstone's reach is pinned on both node types.requestBodyis mirrored under its own name on both twins;ChatbotSchema'sbodynaming collision (inKnownDrift) was not copied across and is not ruled on here. The twenty shared arms are taken offChatbotSchema's own.shapeby.pick(...), so each has one spelling; the test asserts description equality per key.Fence extension, declared:
packages/types/src/zod/index.zod.ts(two export lines)The fence lists
complex.zod.ts, so Zod twins were in scope, but the Zod barrel is an explicit named-export list — a twin not exported from@object-ui/types/zodwould be declared-but-unreachable. This PR's barrel edits are the same two lines the review read; the only other barrel change on the branch ismain's ownDrillDownConfigSchemaexport (#7701), carried by the merge.packages/types/src/registry.tswas deliberately NOT touched (#7704).chatbot-floatinganddisabled— one consequence, no render outcome movesUnchanged from the first cut: the raw
disabled={schema.disabled}read was already overridden on every render by the host verdict the trailing spread carried; the registration now destructuresdisabled: hostDisabledand forwards it, as its two siblings do. Pinned through the real host both ways. The trailing spread itself is untouched (see #7708) — only annotated at the site.Evidence — every run below is on
4579bdff9, after the final commit (tree clean,git status0 lines)pnpm --filter @object-ui/types build && pnpm --filter @object-ui/types type-check && pnpm --filter @object-ui/plugin-chatbot type-check, chained, under the verify lock:VERDICT command-exit 0, the sha echoed after the chain; each script name echoed (tsc && check-dist-completeness;tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json;tsc --noEmit && tsc -p tsconfig.test.json), 0error TSlines in all three logs. The typestype-checkis the channel the parity ratchet and every new pin live in; the plugintype-checkread the REBUILT types dist (which emitsdisplayMode?:on both faces andChatbotSharedKey5 hits) with the dependency closure built in this worktree (test-support, react-runtime, types, core, i18n, sdui-parser, data-objectstack, components, react — all exit 0).pnpm exec vitest run --maxWorkers=2over the four affectedpackages/typestest files plus all ofpackages/plugin-chatbot/→Test Files 39 passed (39),Tests 747 passed (747),git rev-parse --short HEAD=4579bdff9echoed at its head. One red on the way, recorded:chat-message-contract.test.tscounts the literal runtime-messages prop inrenderer.tsxand my first spread-site comment contained it — reworded; a comment tripping a source-scanning pin, no behaviour involved.check:doc-fences✅ (227 documents);check:doc-types✅ "Every documented component type is registered";check:control-bytes✅ (6280 files);check:published-tsconfig-excludeOK (34 packages);check:shell-escape-residue✅;check-doc-links"Links are valid across 17 scan roots";check-changeset-no-major✅.check:doc-snippetsNOT MEASURED locally — exit 2,PRECONDITION NOT MET(it wants the whole workspace built: app-shell, auth, cli, every plugin), which its own text says is "I could not run", not a verdict. Declared narrowing with evidence: a line-level diff of the mdx between the reviewed head and this branch changes 9 → 16 prose lines and 0 lines inside any code fence (26 fenced blocks before and after), against a lit control — the same instrument on the first cut vs the merge-base reads 29 changed fence lines; the mdx is untouched by both merges; and the only type change since the reviewed 455 / 455 run is additive onChatbotSchema. CI runs it.check:readme-exportsnot re-run: this PR's two barrel lines are byte-identical to the reviewed head30a53bd32, where it ran green after the closures were built; the merges add onlymain's own export. CI runs it.pnpm --filter @object-ui/types lintandpnpm --filter @object-ui/plugin-chatbot lint(each package's owneslint .) → exit 0, 0 errors (269 / 90 warnings, all pre-existing). Narrowing declared with the three evidences: the population is eslint's own config (eslint.config.js, 0projectService/parserOptionshits, so no untouched file's verdict can move); a--format jsonrun over the diff's ten lintable files judged 10 files, 0 errors, 13 warnings; other packages are untouched by the diff.packages/typessource, predictions written first, scratch files never committed;complex.tsis byte-identical between the first remediation commit and the head): atorigin/main8ad218d58—ChatbotSchema['displayMode']/['floatingConfig']typed (green);displayMode: 'bogus', a wrong-shapedfloatingConfigandfloatingConfig.triggerIconon achatbotnode all TS2322 (3 errors). At the first cut (368045e28) — the two typed-read pins TS2344 (any), all three wrong values compile CLEAN (2 errors, the opposite set). After the restore — identical to the base (3 errors on the same three lines), plus the floating face refusingtriggerIconand'bogus'(5 in total, 0 outside the probes).4579bdff9). After committing, the restored block was deleted fromChatbotSchema(heading count 1 → 0,displayMode?: 'inline'count 2 → 1, blob06d14941≠ HEADfd59df10); predicted red ontsc -p tsconfig.test.json: five TS2344 (the whole-face pin; the two typed-key pins; the two one-type-on-both-faces pins), two TS2578 unused-directive (displayMode: 'bogus'on achatbotnode;triggerIconreached throughChatbotSchema.floatingConfig), one TS2322 namingcomplex.zod.ts#ChatbotSchema(the ledger records three keys, the declaration then had one). Observed: exactly those 8, at lines 154 / 170 / 171 / 188 / 189 / 289 of the census test, 107 of the trigger-icon test, 1571 of the parity test. Restored withgit checkout HEAD -- ABSOLUTE_PATHunder anEXIT INT TERMtrap; blob back tofd59df10= HEAD,git diff HEADempty. Rebuild statement: none needed and none done —tsconfig.test.jsonresolves../complexfrom source (the reach probes read the same channel), and the dist on disk was built from the final source before the leg and untouched by it.4579bdff9). The floating spread was fenced to{...toDomProps(props)}(count 2 → 3, blob5acbed9b≠ HEAD3dbd2634); predicted: the three render tripwires red, the mechanism test and the other six green. Observed:Tests 3 failed | 7 passed (10)— exactlyshowAvatars,surface,processVisibility. Restored the same way; blob back to3dbd2634= HEAD,git diff HEADempty. No build involved: the test imports../rendererfrom source.os-verify-lock.sh(slotsissue-7655-*), every wait 0 s; wall-clock figures are shared-box readings.check:doc-snippetsandcheck:readme-exports(above), the fullpnpm lintfarm beyond the two packages,check:published-dist,check:sdui-registration-pins.Out of scope, filed — nothing addressed here
chatbot-floatingspreads the whole authored node raw and LAST onto its panel — three undeclared keys are live, the authoredmessagesseed overrides the runtime messages (a sent message never renders), and node keys land as DOM attributes #7708 (new this round): thechatbot-floatingraw spread — three undeclared keys live; the authoredmessagesseed overriding the runtime messages (a sent message never renders on a floating node, both render onchatbot-enhanced);displayMode/systemPrompt/modellanding as DOM attributes (1 / 1 / 1 vs 0 / 0 / 0). Two options with costs stated, none picked.ChatbotSchemakeys are declared and read by no registration —loading,showAvatars,userAvatar,assistantAvatar,markdown,height#7703: sixChatbotSchemakeys declared and read by no registration. Its tension is settled on the card:userAvatar/assistantAvatarare 0 everywhere; the "four avatar keys" were the*Url/*Fallbackfour;showAvatarsIS live on floating through the spread.SchemaRegistry/ComponentTypeomitchatbot-enhancedandchatbot-floating— two registered keys the layer can now name #7704:SchemaRegistry/ComponentTypeomit the two keys;registry.tsuntouched here.Related cards that stay open
objectui#7654 — ruled B (retire
displayMode), open for its own PR; this PR leaves the member on both faces for it. objectui#6152 (the unmirrored ledger) and objectui#7665 (SchemaRegistry's docblock) are untouched. objectui#7669's tripwire now watches both twins.Session
session_01KbJQ1y1J12nZxYzFWhP8Q3,domain:uidev seat.🤖 Generated with Claude Code
https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3