Skip to content

feat(types,plugin-chatbot): name the chatbot node's authoring-face type - #6688

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-6169-chatbot-authoring-face-type
Aug 28, 2026
Merged

feat(types,plugin-chatbot): name the chatbot node's authoring-face type#6688
os-sales merged 1 commit into
mainfrom
claude/issue-6169-chatbot-authoring-face-type

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes #6169

Docs half already merged — untouched here

PR #6193 (ead74abe4) already fixed the body/requestBody collision on this page. This PR does not re-touch that row's wording; it adds one sentence above the table naming the type these keys now come from (see "Docs" below).

The re-derived key set, and where the card's list didn't reconcile

The maintainer ruling scoped this to the anonymous inline intersection at packages/plugin-chatbot/src/renderer.tsx:62 — the chatbot registration specifically (not chatbot-enhanced / chatbot-floating, which have their own, different, still-anonymous intersections; out of scope, noted below). Counting that literal block:

{ schema: ChatbotSchema & {
    showTimestamp?: boolean;
    disabled?: boolean;
    userAvatarUrl?: string;
    userAvatarFallback?: string;
    assistantAvatarUrl?: string;
    assistantAvatarFallback?: string;
    maxHeight?: string;
    autoResponse?: boolean;
    autoResponseText?: string;
    autoResponseDelay?: number;
    onSend?: (content: string, messages: ObjectChatMessage[]) => void;
  }; className?: string; disabled?: boolean; [key: string]: any }

11 keys inside the intersection — matches the card's "11". Two things in the dispatch's warning did not reconcile, and both turned out to be measurement errors in the card, not in the count:

  • surface — named by the card as one of the 11. It is not in this block, not in either of the other two registrations' blocks, and not forwarded anywhere in renderer.tsx (grep -n surface packages/plugin-chatbot/src/renderer.tsx → zero matches). It's a real prop of ChatbotEnhanced (surface?: ChatbotSurface, ChatbotEnhanced.tsx:805), but no registration ever reads schema.surface to forward it. The docs table's surface row (also un-backed) is a separate, pre-existing defect — filed as finding(plugin-chatbot): the docs surface row names a key no chatbot* registration reads — zero read points across all three #6687, not touched here (see "Out of scope" below).
  • disabled — the card said the intersection "also carries disabled, which the registration destructures separately." It does: disabled?: boolean appears twice — once inside the intersection (an authored schema key) and once at the sibling level of the destructure type (; className?: string; disabled?: boolean; ...}, the host-EVALUATED verdict SchemaRenderer forwards as hostDisabled). These are two different carriers for two different questions that happen to share a spelling — not a miscount. renderer.tsx's own comment (right above disabled={hostDisabled || isLoading}) already documents why hostDisabled is read there and not schema.disabled.

Read-site census (the clause that makes this more than a rename)

key read site(s) verdict
showTimestamp renderer.tsx (both useObjectChat() call and <Chatbot> render prop); useObjectChat.ts:750,768 (sets/omits message timestamp); index.tsx:203 (conditional render) IN
userAvatarUrl renderer.tsx render prop; index.tsx:104,174 IN
userAvatarFallback renderer.tsx render prop; index.tsx:105,177 IN
assistantAvatarUrl renderer.tsx render prop; index.tsx:106,174 IN
assistantAvatarFallback renderer.tsx render prop; index.tsx:107,178 IN
maxHeight renderer.tsx render prop; index.tsx:47,88 (style={{ maxHeight }}) IN
autoResponse renderer.tsxuseObjectChat(); useObjectChat.ts:427,761 (gates the auto-response timer); live consumer packages/app-shell/src/console/ai/AiChatPage.tsx:1662 IN
autoResponseText same hook; useObjectChat.ts:428,767; AiChatPage.tsx:1663 IN
autoResponseDelay same hook; useObjectChat.ts:429,772 (setTimeout delay); AiChatPage.tsx:1664 IN
onSend same hook; useObjectChat.ts:429,718,720,755 (invoked with content + messages) IN
disabled (the authored, in-intersection one) not read by name inside the chatbot registration body — but it's inherited from BaseSchema.disabled (boolean | string), read generically for every node type at packages/react/src/SchemaRenderer.tsx:1085 (newSchema.disabled) IN, but not via this intersection — already declared, nothing to lift

Zero keys took the ADR-0049 route. Every key in the 11-key block has a real, measured reader — ten of them directly inside the very file that declared them anonymously, which is exactly the "authorable surface with no declaration anyone can import" the ruling described. disabled needed no action at all: it was never actually anonymous — it's BaseSchema.disabled, already named, already inherited by ChatbotSchema, and the anonymous block's local re-declaration of it (as boolean only) would have narrowed away the inherited string-expression case had I copied it forward. I did not.

Positive control, same query shape: the query that reports disabled as "not read in this file's body" is the same plain-text schema\.<key> grep run against all eleven keys — it correctly reports real hits (with line numbers) for the other ten. A zero from a query that also produces real positives on siblings is a measurement, not a blind spot.

Structural choice: extend ChatbotSchema in place, not a new named type

The ruling allowed either. I extended ChatbotSchema (packages/types/src/complex.ts) directly, for three reasons:

  1. "Exactly one … type" reads most literally as one type. Every other schema interface in this package is <X>Schema extends BaseSchema — there is no existing "<X>Schema + <X>ExtraSchema" pattern to follow, and inventing one here would leave the chatbot node with two types where the ruling asks for one.
  2. Zero new barrel-export surface. ChatbotSchema is already exported from packages/types/src/index.ts (line 303) — I did not touch that file. (Noting this explicitly per the fence: packages/types/src/index.ts is the one real collision point with the concurrent finding(components): data-table reads two column keys TableColumn does not declare — headerIcon and fitContent #6424 dispatch, and I left it alone.)
  3. A new type would not have escaped the real hazard. BaseSchema carries [key: string]: any (objectui#5155). Any object typed against ChatbotSchema — new sibling type or not — already swallows any unlisted key as any. The fix that actually matters is declaring the field with its real type, so a wrong value is refusable; which interface holds the declaration is secondary. See the ablation below for the empirical version of this point.

disabled is deliberately not redeclared in the new group — see the census above.

onSend's message-array parameter is typed ChatMessage[] (the existing, already-exported authoring type ChatbotSchema.messages uses), not plugin-chatbot's internal ObjectChatMessage. renderer.tsx's own doc comment already established that a callback declaring ChatMessage[] type-checks against ObjectChatMessage[] (a structural superset per chatMessageAdapter.ts's documented seam), so this is a no-behavior-change, dependency-direction-respecting choice: packages/types cannot import a packages/plugin-chatbot-local type without a wrong-direction dependency.

Zod mirror — kept in lockstep (the "validatable" third of the ruling)

The ruling asks for "referenceable, validatable, and documentable." The Zod mirror (packages/types/src/zod/complex.zod.ts) gained the same ten keys, matched type-for-type. Two things that made this necessary, not optional:

Tests

New file: packages/types/src/__tests__/chatbot-authoring-face-keys.test.ts — pins both the TS and Zod halves: all ten keys accept their declared type on ChatbotSchema and through the Zod mirror; a wrong-typed value on autoResponseDelay is refused by both (a @ts-expect-error on the TS side, safeParse().success === false on the Zod side); disabled stays wide (boolean | string) rather than being shadowed narrower; the Zod shape lists all ten keys by name.

Ablation — each leg predicted before it ran, mutated on disk, restored, re-verified

Both legs mutated packages/types source with a trap ... EXIT INT TERM restoring via git checkout HEAD -- <path> (never bare checkout --), field counts asserted before AND after the mutation (never an editor's exit code), and git diff HEAD confirmed empty after restore, both times.

Leg 1 — remove the ten Zod fields only, keep the TS declaration.
Predicted: packages/types type-check goes red — the drift ratchet cannot absorb new unmirrored keys on the already-ledgered ChatbotSchema pair.
First attempt, and a real correction: I first ran only vitest run zod-mirror-parity.test.ts and it stayed green — which contradicted the prediction. The reason is itself worth recording: that file's actual drift ratchet (ReconcileAgainstLedger / assertionRatchetRejectsGrowth) is a type-level export type assertion... = Expect<Equal<...>> construct, invisible to a vitest run (transpile-only, types erased) and checked only by tsc. The file's five it() blocks test population completeness, not per-key drift. Re-ran against the actual instrument:
Observed: pnpm --filter @object-ui/types type-checkexit 2src/__tests__/zod-mirror-parity.test.ts(1203,14): error TS2322: Type '"complex.zod.ts#ChatbotSchema"' is not assignable to type 'never'. Exactly the ratchet firing, exactly on the ChatbotSchema pair. Restored; re-ran → exit 0.

Leg 2 — remove the ten TS declaration fields only, keep the Zod mirror.
Predicted: packages/types type-check fails on the new test file's @ts-expect-error becoming an unused directive (TS2578) — without the declared type, the wrong-typed value no longer errors (swallowed by BaseSchema's index signature as any), so the pin has nothing to catch. Also predicted: packages/plugin-chatbot type-check stays green, because renderer.tsx's reads of schema.showTimestamp etc. fall through the same inherited index signature to any either way — the only place this regression is caught is the dedicated pin test, not the renderer itself.
Observed: packages/types type-check → exit 2, chatbot-authoring-face-keys.test.ts(93,7): error TS2578: Unused '@ts-expect-error' directive. (plus a cascading TS7006 on the now-untyped onSend callback params). packages/plugin-chatbot type-checkexit 0, confirming the prediction: this ablation leg has no independent guard other than the new test file. Restored; re-ran → exit 0.

(One process note, not a code issue: my first pass at leg 2's type-check run accidentally executed from the shared primary checkout instead of this worktree — caught immediately via the repo-root guard added to the second script, confirmed the shared checkout was untouched/clean afterward, and re-ran correctly from the worktree. No shared state was affected.)

Docs

One sentence added above the ## Properties table, naming ChatbotSchema as the type these keys now come from — the direct answer to the #6086 gap this card's parent card recorded. No table rows edited or deleted, per the fence.

Clause-② — published type surface, at contract-review tier

Yes, this changes published type surface, per the ruling. What becomes published: ChatbotSchema (@object-ui/types) gains ten optional keys with real types (previously unreachable outside renderer.tsx); @object-ui/types/zod's ChatbotSchema gains the same ten as validated Zod fields (previously silently passed through). An external consumer can now import type { ChatbotSchema } from '@object-ui/types' and get autoResponseDelay: number, etc. with real type-checking, and can ChatbotSchema.safeParse(node) from @object-ui/types/zod and have a wrong value at any of these ten keys actually refused. No needs:contract-review label applied (phantom label, zero readers, per this lane's standing order).

Out of scope, deliberately

  • chatbot-enhanced / chatbot-floating's own anonymous intersections — different key sets (enableMarkdown, enableFileUpload, onClear, …), a decision for a separate card in the same family, per the fence.
  • The docs table's surface row, which names a key no chatbot* registration reads anywhere (a different, pre-existing defect, structurally the body/requestBody sibling but with zero read points rather than a naming collision) — filed as finding(plugin-chatbot): the docs surface row names a key no chatbot* registration reads — zero read points across all three #6687, not touched here.
  • ChatbotSchema's existing userAvatar / assistantAvatar / showAvatars / markdown / height / onSendMessage / loading fields, which I measured have zero read points anywhere in packages/plugin-chatbot (an older, unrelated naming generation, already declared before this PR). Not this card's subject — the ruling named the anonymous intersection, not an audit of ChatbotSchema's pre-existing declared surface — but flagging it here since a future ADR-0049 pass over this exact interface will want it.

Gate table (final commit 036da7c06)

gate command result
build (deps + types + plugin-chatbot) pnpm --filter '@object-ui/plugin-chatbot^...' build, --filter @object-ui/types build, --filter @object-ui/plugin-chatbot build exit 0 each
type-check: types pnpm --filter @object-ui/types type-check (chains tsconfig.json + tsconfig.examples.json + tsconfig.test.json) exit 0
type-check: plugin-chatbot pnpm --filter @object-ui/plugin-chatbot type-check exit 0
vitest (types + plugin-chatbot, root-level per objectui#3378) pnpm exec vitest run packages/types/ packages/plugin-chatbot/ 96 files / 1176 tests passed
zod-mirror-parity (population half) pnpm exec vitest run .../zod-mirror-parity.test.ts 5/5 passed (drift half is type-level; see ablation leg 1)
check:control-bytes node scripts/check-control-bytes.mjs ✅ 5530 scanned
check:doc-fences node scripts/check-doc-fence-languages.mjs
check:doc-types node scripts/check-doc-component-types.mjs
check:doc-snippets --build-filter build (32 tasks) then node scripts/check-doc-snippet-types.mjs; dist/complex.d.ts re-verified on disk to carry the new fields before trusting the green ✅ 267/267 blocks
docs:check-links node scripts/check-doc-links.mjs ✅ 17 scan roots
changeset:check (fixed-group + no-major) check-changeset-fixed.mjs, check-changeset-no-major.mjs ✅ both
changeset presence node scripts/check-changeset-presence.mjs ✅ 4 source files / 2 released packages / 1 changeset
eslint, scoped to the 4 touched .ts files, --format json 0 errors, 13 warnings, all pre-existing @typescript-eslint/no-explicit-any — counts diffed against origin/main's versions of the same files line-for-line (3/3 renderer.tsx, 8/8 complex.ts, 2/2 complex.zod.ts): zero new

Lint scope note: eslint.config.js uses tseslint.configs.recommended (not recommendedTypeChecked/strictTypeChecked, no parserOptions.project) — non-type-aware, so this diff cannot move any untouched file's verdict; scoping to the touched files is a complete measurement, not a sample.

Generated by Claude Code


Generated by Claude Code

Lift the 11-key anonymous inline intersection at
packages/plugin-chatbot/src/renderer.tsx:62 (the `chatbot` registration)
into ChatbotSchema, per the maintainer's 2026-08-25 #6172 family ruling:
every component node has exactly one named, importable authoring-face
type. Each key was read-site-censused first; all ten net-new keys have
live readers (renderer.tsx and/or useObjectChat.ts), so none took the
ADR-0049 retirement route. `disabled` is not redeclared -- it is already
BaseSchema.disabled (boolean | string), inherited, read generically.

The Zod mirror (complex.zod.ts) gains the same ten keys in lockstep, so
these move straight from unmirrored-and-passthrough to mirrored, never
through an interim drift window that would fail the parity ratchet.

Part of #6169.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3236.9 KB 3266.6 KB
Main entry chunk (gzip) 157.3 KB 350 KB
Entry file index-Dz3m13RI.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) 11.89KB 4.50KB
app-shell (runtime-config.js) 20.61KB 7.35KB
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) 509.24KB 115.61KB
core (index.js) 5.30KB 2.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 173.10KB 47.96KB
fields (index.js) 239.05KB 60.06KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
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.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.95KB 10.97KB
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.55KB 0.62KB
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 (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.53KB 3.38KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.64KB 1.50KB
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) 1.93KB 0.88KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.85KB 12.89KB
plugin-charts (index.js) 64.66KB 18.32KB
plugin-chatbot (index.js) 190.33KB 45.10KB
plugin-dashboard (index.js) 133.43KB 34.48KB
plugin-designer (index.js) 212.80KB 43.15KB
plugin-detail (index.js) 245.29KB 62.39KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.01KB 32.23KB
plugin-gantt (index.js) 165.16KB 40.33KB
plugin-grid (index.js) 201.51KB 54.54KB
plugin-kanban (index.js) 53.11KB 14.62KB
plugin-list (index.js) 113.01KB 27.57KB
plugin-map (index.js) 20.09KB 6.62KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 26.44KB 7.59KB
plugin-tree (index.js) 9.26KB 3.13KB
plugin-view (index.js) 85.87KB 21.12KB
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) 65.97KB 21.98KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 2.44KB 1.21KB
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 0.99KB
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.72KB 2.24KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (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) 3.40KB 1.71KB
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-sales
os-sales marked this pull request as ready for review August 28, 2026 15:01
@os-sales
os-sales added this pull request to the merge queue Aug 28, 2026
Merged via the queue into main with commit 52a43de Aug 28, 2026
30 checks passed
@os-sales
os-sales deleted the claude/issue-6169-chatbot-authoring-face-type branch August 28, 2026 15:28
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.

finding(plugin-chatbot): the chatbot authoring surface is an unnamed inline intersection, and its docs table names a key the node does not carry

2 participants