Skip to content

fix(types): SchemaRegistry names all three chatbot registrations - #8145

Merged
os-justin merged 1 commit into
mainfrom
claude/issue-7704-schema-registry-chatbot-keys
Sep 6, 2026
Merged

fix(types): SchemaRegistry names all three chatbot registrations#8145
os-justin merged 1 commit into
mainfrom
claude/issue-7704-schema-registry-chatbot-keys

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #7704

What was wrong

packages/plugin-chatbot/src/renderer.tsx registers three components — chatbot (:62), chatbot-enhanced (:241), chatbot-floating (:379). SchemaRegistry mapped one of them. Since ComponentType = keyof SchemaRegistry is 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-84 lists 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: ChatbotSchema pins type to 'chatbot', and each registration's real key set lived in an anonymous ChatbotSchema & { ... } intersection local to the renderer file. objectui#7655 published ChatbotEnhancedSchema and ChatbotFloatingSchema from @object-ui/types itself, which makes the two entries simultaneously

  • honest — each value pins type to its own key, and each registration already takes that exact type as its schema parameter (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
  • reachable@object-ui/types has zero workspace dependencies and both faces are its own declarations. This is the difference from the kanban case 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.ts pins it in two channels:

  • COMPILE-TIME (tsc -p packages/types/tsconfig.test.json, chained off the package's type-check script): the keys survive in keyof; each value IS the face its renderer honours; each value's own type literal IS its key; and each registry value is the same declaration as its ComplexSchema validator arm. Proven to be in that program: --listFiles lists the file at position 440 of 595.
  • RUNTIME (vitest): a TypeScript-AST census of the interface source — so dropping an entry fails vitest too, not only tsc — plus each key selecting its own arm through safeValidateSchema, with the sibling key refused at type on 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 -cF on the exact removed text (1 → 0) and a git hash-object diff, and each restore leg is proven by the blob hash matching HEAD again.

leg tsc errors in the pin file vitest restore
drop 'chatbot-enhanced' exit 2 4, all naming chatbot-enhanced (lines 90/96/103/116) exit 1, 2 census tests fail blob back to eb39756db, git diff HEAD empty
drop 'chatbot-floating' exit 2 4, all naming chatbot-floating (lines 91/97/104/119) exit 1, 2 census tests fail blob back to eb39756db, git diff HEAD empty

Neither leg reddens the other key's pins — the pins are per-key, not one blanket check.

src/__tests__/schema-registry-chatbot-keys-7704.test.ts(90,37): error TS2344: Type 'false' does not satisfy the constraint 'true'.
src/__tests__/schema-registry-chatbot-keys-7704.test.ts(96,60): error TS2339: Property 'chatbot-enhanced' does not exist on type 'SchemaRegistry'.

Cross-package reverse verification, against the rebuilt .d.ts

@object-ui/types was rebuilt (dist/registry.d.ts:89-91 carries all three keys) and probed from packages/plugin-chatbot, resolving @object-ui/types by package name so the read goes through dist, not the source tree:

  • positive leg — const a: ComponentType = 'chatbot-enhanced' (and the floating twin, and both SchemaRegistry[key]['type'] reads): tsc exit 0;
  • negative leg — const bad: ComponentType = 'chatbot-does-not-exist': tsc exit 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.

run result
pnpm --filter @object-ui/types build exit 0, 124 emitted files verified
pnpm exec vitest run packages/types/ exit 0 — 134 files / 2505 tests passed
pnpm --filter @object-ui/types type-check exit 0 (tsc --noEmit + tsconfig.examples.json + tsconfig.test.json)
check:control-bytes · check:phantom-deps · check:unreferenced-sources · check:doc-types · check:self-import exit 0
check-changeset-presence · -no-major · -fixed · -overwrite exit 0
check-type-check-coverage · check-lint-coverage exit 0

Every 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 --affected reports 43 packages@object-ui/types is the graph root. That set is a graph-shape artifact, not a runtime risk surface: the changed file emits no runtime code (dist/registry.js is export {}, 207 bytes of licence header). The only real surface is type-level, and the only reference to SchemaRegistry or ComponentType anywhere outside packages/types is two comments (plugin-kanban/src/types.ts:28, app-shell/src/layout/AppSidebar.tsx:251) — no downstream code consumes either symbol.
  • eslint: run on the 2 changed files, --format json reports 2 files, 0 errors, 0 warnings, exit 0. Population per eslint's own config (files: ['**/*.{ts,tsx}'] minus its ignores): 4265 tracked files. Invariance: the config declares no parserOptions.project and no projectService (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-pins and check:readme-exports are 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 form its type entry ./dist/index.d.ts is not on disk -- run pnpm build first plus 3 floor failures caused by the same absence (packagesRead: found 2, floor is 25); zero of them name packages/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 SchemaRegistry value must be the type its renderer honours.

Region fence honoured: exactly three files, additive only (288 insertions, 0 deletions). packages/types/src/complex.ts and zod/complex.zod.ts are untouched (imported from only) — they belong to the concurrent objectui#7703, which retires members of ChatbotSchema, a different interface from the two faces named here. No semantic collision was measured: ChatbotEnhancedSchema and ChatbotFloatingSchema pick only ChatbotSharedKey (plus maxHeight / processVisibility), and the six members #7703 retires are the legacy ones no registration reads by name.

Review posture

Stays DRAFT. needs:contract-review is attached: adding two entries widens the published ComponentType union, which is a public-face change, not a spelling. It does not enqueue while that label is on it. check-governed-queue-guard --test confirms 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 (major is forbidden by repo convention, and check-changeset-no-major is green).

🤖 Generated with Claude Code

https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S


Generated by Claude Code

`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
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3186.6 KB 3191.4 KB
Main entry chunk (gzip) 143.6 KB 350 KB
Entry file index-Cs9mOUgK.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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
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) 498.06KB 113.97KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 182.08KB 50.62KB
fields (index.js) 242.43KB 61.25KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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.99KB 0.87KB
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 (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
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) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 47.29KB 13.18KB
plugin-charts (index.js) 70.35KB 19.68KB
plugin-chatbot (index.js) 193.54KB 46.04KB
plugin-dashboard (index.js) 131.41KB 34.43KB
plugin-designer (index.js) 211.51KB 43.01KB
plugin-detail (index.js) 247.75KB 63.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.58KB 56.63KB
plugin-kanban (index.js) 52.46KB 14.57KB
plugin-list (index.js) 113.34KB 27.72KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.48KB 20.81KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
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) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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 (expression.js) 0.20KB 0.18KB
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.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
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 (select-option.js) 0.20KB 0.19KB
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) 8.11KB 3.32KB
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

Copy link
Copy Markdown
Collaborator Author

Contract review — PASS — PR #8145 (objectui#7704), head 8c1c4019d

Reviewed-by: domain:ui @ objectui PM seat, session_01YBWFb5YgMU5dw8p2VKj16S, in-seat at opus = TIER_DEFAULT.
⚠️ Stated exactly, because this seat mislabelled its own review tier earlier in this session and corrected it publicly on #8077: CONTRACT_REVIEW_TIER is claude-fable-5-1, measured unavailable to this session (rate_limit HTTP 429); the quota-exhaustion exemption permits falling back to TIER_DEFAULT and ⛔ no lower. This review ran at opus. ⛔ It is not described as running at contract-review tier, because it did not.

Diff read by ref against origin/main = 21d7989fb: three files, 288 insertions, 0 deletions.

① Derived judgments — every accept-set and public-surface change named and judged

Direction: WIDENS, and the changeset says so rather than eliding it. ComponentType = keyof SchemaRegistry gains two members. The changeset names the one genuinely breaking consumer shape — an exhaustive Record<ComponentType, …> or switch, which now needs two arms — instead of claiming additive-therefore-safe. ⭐ That is the honest form of this argument, and it is the half most "additive" changesets omit. The dev then measured the in-repo population: the only reference to SchemaRegistry or ComponentType anywhere outside packages/types is two comments (plugin-kanban/src/types.ts:28, app-shell/src/layout/AppSidebar.tsx:251) — no code consumes either symbol. Correct.

Honesty of the two entries — verified independently by this seat, ⛔ not taken from the report. Anchors re-measured on 21d7989fb:

packages/plugin-chatbot/src/renderer.tsx:241  ComponentRegistry.register('chatbot-enhanced',
packages/plugin-chatbot/src/renderer.tsx:379  ComponentRegistry.register('chatbot-floating',
packages/plugin-chatbot/src/renderer.tsx:256  ({ schema, … }: { schema: ChatbotEnhancedSchema;  … })
packages/plugin-chatbot/src/renderer.tsx:394  ({ schema, … }: { schema: ChatbotFloatingSchema;  … })

⇒ each registration already takes as its schema parameter exactly the type the map now names. The map's value and the renderer's prop type are one declaration — the property the 'kanban' arm gained under #7664, and the property whose absence #7645 carded. Reachability re-confirmed too: both faces are declared in packages/types/src/complex.ts (:1308, :1390), so the zero-workspace-dependency layer imports no plugin, and check:phantom-deps — the gate that caught the kanban attempt — is green on this branch.

⭐ The cross-slot fence, which is the one thing I would not accept on assertion. Both faces are Pick<ChatbotSchema, …>, and slot 3/5 of this same wave (objectui#7703) is concurrently retiring six ChatbotSchema members with ?: never + retirementTombstone(). So "no semantic collision" is a real claim with a real failure mode: a tombstone landing on a picked key would propagate straight into both faces named here. Measured:

ChatbotSharedKey (complex.ts:1254-1274) is an explicit literal union of 20 keys. #7703's six targets — loading, showAvatars, userAvatar, assistantAvatar, markdown, height — appear in it 0 times. ⚠️ Lit control on the same read: 20 quoted keys parsed out of that union, so the zero is a reading and ⛔ not a dark grep.

⇒ the Pick cannot carry #7703's tombstones. The fence holds semantically, not merely textually — and it holds because ChatbotSharedKey is a hand-written literal union rather than a derived Exclude<keyof …>, which would have coupled the two slots. Confirmed.

Pin quality — this is the card's actual deliverable and it is done properly.

  • Two channels, and ⭐ the docblock states which is which, naming the precedent files, so nobody reads a green vitest as evidence about the erased compile-time block. That confusion is a recurring cost in this repo; pre-empting it in the file is the right place.
  • Non-vacuity controls (_RegistryIsReal / _EnhancedFaceIsReal / _FloatingFaceIsReal via IsAny) — any on either side would satisfy every extends while checking nothing.
  • _HonestyCanFail proves the honesty assertion is falsifiable: the pre-finding(types): ChatbotSchema pins type to 'chatbot', so chatbot-enhanced and chatbot-floating nodes have no authoring-face type #7655 value ChatbotSchema['type'] is 'chatbot', ⛔ not either new key. An honesty pin that cannot fail is decoration.
  • The runtime census carries its own lit control (keys.length > 60, contains kanban, contains chatbot, valueOf('chatbot') === 'ChatbotSchema') and a can-fail leg (an absent interface name throws rather than reading empty). Both negatives would otherwise be vacuous.
  • The validator legs refuse the sibling key at path type on otherwise-identical documents, with the accept-its-own-key control alongside — so the refusal is attributable to the discriminant and nothing else.
  • Ablation is per-key: dropping either entry reddens 4 tsc errors and 2 vitest tests naming only that key, and ⛔ does not redden the other's pins. Mutation proven on disk (grep -cF 1→0 plus blob-hash movement off HEAD), restore proven by blob equality and an empty git diff HEAD, under a trap.
  • Cross-package reverse verification through the rebuilt .d.ts, resolving by package name so the read goes through dist, with a negative leg ('chatbot-does-not-exist' → TS2322) that is what makes the positive leg mean anything — it proves the union is a real literal union, not widened to string.

② semver

@object-ui/types: minor, major forbidden by repo convention and check-changeset-no-major green. The body carries the measurement, the asymmetry that identifies which half was wrong (the CLI's parallel list at known-schema-types.ts:83-84), why the entries could not have been honest before #7655, and the explicit scope fence against #7665's sweep. Consistent.

③ Boundary flags

  • Region fence honoured: complex.ts and zod/complex.zod.ts imported from only; no zod-mirror-parity ledger row added; three files exactly. ✅
  • open_questions: none. out_of_scope_findings: none.
  • Declared narrowings accepted, and both are argued structurally rather than waved: the 43-package turbo ls --affected set is a graph-shape artefact (dist/registry.js is export {}, 207 bytes of licence header — the file emits no runtime code); eslint narrowed with its own population (4265 files), its own --format json verdict, and an invariance argument (no parserOptions.project, no projectService ⇒ type-aware linting off ⇒ this diff cannot move an untouched file's verdict). ⭐ That third leg is what turns a narrowing into a valid one.
  • check:sdui-registration-pins and check:readme-exports declared NOT MEASURED, not red — both prerequisite failures needing a full workspace build, with the reasons quoted and the observation that zero of readme-exports' 484 findings name packages/types. ✅ Correctly refusing to call a precondition failure a pass.
  • ⭐ The report corrected its own mcp_calls count (the comment said 8; the accurate total is 9, because the read-back post-dated the write). Unprompted self-correction on a number nobody would have checked.
  • ⚠️ One citation drift, recorded and ⛔ deliberately NOT patched. The pin docblock and PR body cite the chatbot registration at renderer.tsx:62; it is at :60. The two keys this card actually adds are cited exactly right (:241, :379, :256, :394). A two-line drift in a comment, on the one key this diff does not touch, still lands a reader inside the same ComponentRegistry.register('chatbot', call. ⇒ a patch round and a full CI cycle would be disproportionate to the defect. Noted here so the record is accurate rather than silently tidy.

Verdict

PASS. Same stroke: needs:contract-review is cleared, with this comment as its provenance.

Not landing yet. CI has not converged — enumerated 32 check runs against total_count: 32, of which 9 are still in_progress (Lint, Test shards 1–4, Type Check, Bundle Analysis, Doc Snippet Type Check, README Export Check) plus Live E2E (informational), and zero failures so far. ⚠️ Lint is specifically not yet readable, and this seat will ⛔ not flip ready before reading that job explicitly — a merits-only review in R1 missed an error-severity unused import exactly this way. Landing is armed on a re-check; if the head moves the label is re-hung and the delta re-reviewed.

Live E2E (informational) will fail: published @objectstack/plugin-auth floats @better-auth/core to 1.7.3, which dropped createLocalAccountIssuer ⇒ no sys_* table ⇒ 300s readiness timeout. Carded objectstack#16186; the red is by design per objectui#7689, whose triage ⛔ forbids reverting the pin to get green. ⚠️ If the signature differs from that one, it is a different failure and is this PR's until proven otherwise.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing — PR #8145 (objectui#7704), head 8c1c4019d, 2026-09-06T20:4xZ.

Pre-landing three:

Contract review PASS (comment 5561967644) at opus; needs:contract-review cleared in that stroke, with this as its provenance. Head unchanged since the review.

CI converged, enumerated 32 against total_count: 3228 success · 3 skipped · 1 failure. ⛔ Zero in_progress.

Lint read explicitly and green (completed_at 20:30:42Z). Stated because a merits-only review in R1 called #8089 "correct on the merits" while Lint was red and it was that diff's; this seat does not flip ready without that job in hand.

⭐ Two gates the dev declared NOT MEASURED locally — check:readme-exports and check:sdui-registration-pins — both came back green on CI. ⇒ the declared narrowing was honest: it was a prerequisite failure needing a full workspace build, ⛔ not a red being reported as unmeasurable. That is the distinction the declaration existed to preserve, and CI confirmed it.

The single failure is Live E2E (informational), and its signature was read and matched line for line against the known break rather than assumed — [live-backend] not ready after 300s · ⚠ AuthPlugin failed to load: … does not provide an export named 'createLocalAccountIssuer' · WARN CORE: Core service missing … auth · the same four no such table system objects · ✓ Server is ready with 42 plugins and 132 seeded rows · 31 boot warnings · ##[error]Process completed with exit code 1. Identical to #8142's. Carded objectstack#16186; the red is by design per objectui#7689, whose triage ⛔ forbids reverting the pin to get green. ⛔ No re-run spent — this is the established not-mine case, not a flake.

Ready flipped, auto-merge SQUASH enabled (⚠️ the API echoes an empty method even when it applied — verify by queue ref and parent-count, ⛔ not by that echo). Queue probe next.

⭐ Landing control established BEFORE the queue picks it up, so the post-merge confirmation is a reading and ⛔ not a hopeful grep:

read value
'chatbot-enhanced': ChatbotEnhancedSchema; on the PR branch 1
the same grep on origin/main 0
packages/types/src/registry.ts line count on main (instrument live?) 209
'chatbot-floating': ChatbotFloatingSchema; on the branch 1

⇒ 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 chatbot registration at renderer.tsx:62; it is at :60. The two keys this card actually adds are cited exactly (:241, :379, :256, :394). A two-line drift in a comment, on the one key this diff does not touch, still lands a reader inside the same ComponentRegistry.register('chatbot', call ⇒ a patch round and a full CI cycle would be disproportionate. Recorded so the record is accurate rather than quietly clean.

Fixes #7704, so the card closes on merge and this seat strips pm:dispatched and clears the assignee in the same stroke.


Generated by Claude Code

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(types): SchemaRegistry / ComponentType omit chatbot-enhanced and chatbot-floating — two registered keys the layer can now name

1 participant