Skip to content

fix(types): the 58 on* handler keys declared z.function() refuse BY NAME (objectui#6124) - #7339

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-6124-on-handler-keys-refusal-arm
Sep 2, 2026
Merged

os-litant merged 1 commit into
mainfrom
claude/issue-6124-on-handler-keys-refusal-arm

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes #6124

What this PR does

The @object-ui/types zod mirrors declared 58 on* handler keys (26 distinct) across 8 files as z.function() — a declaration no JSON document can satisfy on a JSON-authored vocabulary. A JSON author who wrote onClick: { "action": "toast" } was refused with zod's bare invalid_type … expected function, received object, which names the key and nothing else.

This PR executes the maintainer ruling of 2026-08-30 (director seat, batch #8, verbatim 「批次 #8 同意」, comment 5469346437 on #6124): Q1 → A, Q2 → A with C, Q3 → A, Q4 → B.

Clause ②: yes — the published validators' accept set moves (a function value that parsed green is now refused). Contract-review tier: this PR stays draft and carries needs:contract-review.

Census on the base (c93b4d5f3, re-run, not copied)

Anchored ^\s*on[A-Z][A-Za-z]*: z\.function\( over packages/types/src/zod/*.ts: 58 sites, 26 distinct keys, 8 files (complex 10, data-display 8, disclosure 3, feedback 1, form 20, layout 2, navigation 4, overlay 10). All key: z.function( sites: 62 = 58 + the four non-on* (cell, custom, renderCellEditor, validate). Raw z.function( occurrences: 64 = 62 + EventHandlersSchema + one mention inside a describe string. The card's 28/60 and the ruling's 29/61 are both stale; the close condition is this table.

Schema (file) Key TS twin Runtime consumer measured Disposition
KanbanSchema (complex) onCardMove, onCardClick complex.ts plugin-kanban/index.tsx:193-194 forwards schema.onCardMove / schema.onCardClick runtime slot
KanbanSchema (complex) onColumnAdd, onCardAdd complex.ts renderer takes ({ schema }), never reads them retired → ?: never
CalendarViewSchema (complex) onViewChange complex.ts calendar-view-renderer.tsx:319 pickHostCallbacks(rest) (function values only) → CalendarView runtime slot
FilterBuilderSchema (complex) onChange complex.ts filter-builder.tsx:26 calls props.onChange runtime slot
CarouselSchema (complex) onSlideChange complex.ts {...props} onto a div; CarouselProps has no such prop retired
ChatbotSchema (complex) onError, onSend complex.ts plugin-chatbot/renderer.tsx:89,94 → useObjectChat runtime slot
ChatbotSchema (complex) onSendMessage complex.ts renderer wires its own handleSendMessage; toDomProps drops the key retired
AlertSchema (data-display) onDismiss data-display.ts {...props} spread onto the Alert element (no such event) retired
ListItemSchema (data-display) onClick ListItem list.tsx renders each item as a plain li with its content retired
DataTableSchema (data-display) onRowEdit, onRowDelete, onSelectionChange, onColumnsReorder data-display.ts data-table.tsx:551,576,1341,1494 call them off schema.* runtime slot
TreeViewSchema (data-display) onSelectChange, onExpandChange data-display.ts {...props} onto a div; nodes get only onNodeClick retired
AccordionSchema / CollapsibleSchema / ToggleGroupSchema (disclosure) onValueChange / onOpenChange / onValueChange disclosure.ts leftover props spread onto the Radix root, where the prop is real runtime slot
ToastSchema (feedback) onDismiss feedback.ts toast.tsx takes ({ schema }), never reads it retired
ButtonSchema (form) onClick form.ts button.tsx:67 toFormControlDomProps forwards it (onClick ∈ SDUI_DOM_PASS_THROUGH_KEYS) runtime slot
InputSchema, TextareaSchema, SelectSchema, CheckboxSchema, FileUploadSchema, DatePickerSchema, InputOTPSchema (form) onChange form.ts each renderer destructures onChange from props and calls it runtime slot
RadioGroupSchema, SwitchSchema, ToggleSchema, SliderSchema, ComboboxSchema (form) onChange form.ts only toFormControlDomProps's whitelist is forwarded; no by-name read retired
CalendarSchema (form) onChange form.ts {...props} onto DayPicker, whose callback is onSelect retired
CommandSchema (form) onChange form.ts lands on cmdk's root div as a DOM change listener (SyntheticEvent) — not the declared (value: string) => void retired (leak noted)
InputOTPSchema (form) onComplete form.ts whitelist drops it retired
FormSchema (form) onSubmit, onChange, onCancel form.ts form.tsx:1002-1005 destructures off schema; called at :2008, :1869, :2096 runtime slot
CodeEditorSchema (form) onChange form.ts plugin-editor/index.tsx:48 onChange ?? schema.onChange runtime slot
CardSchema (layout) onClick layout.ts card.tsx:35,46 reads props.onClick, spreads onto the Card element runtime slot
TabsSchema (layout) onValueChange layout.ts tabs.tsx:45 {...tabsProps} after its own onValueChange runtime slot
BreadcrumbItemSchema (navigation) onClick BreadcrumbItem breadcrumb.tsx renders links; never reads it retired
SidebarSchema (navigation) onCollapsedChange navigation.ts {...props} spread onto the Sidebar component; no such prop retired
PaginationSchema (navigation) onPageChange navigation.ts pagination.tsx:32 calls props.onPageChange runtime slot
ButtonGroupButtonSchema (navigation) onClick ButtonGroupButton button-group.tsx renders each Button without a click handler retired
DialogSchema, SheetSchema, DrawerSchema, PopoverSchema, HoverCardSchema, DropdownMenuSchema, AlertDialogSchema (overlay) onOpenChange overlay.ts {...props} onto the Radix / vaul root, where onOpenChange is real runtime slot
AlertDialogSchema (overlay) onConfirm, onCancel overlay.ts footer wired to schema.onAction and AlertDialogCancel; never read retired
MenuItemSchema (overlay) onClick MenuCommandItem dropdown-menu.tsx:77, context-menu.tsx:75, menubar.tsx:53,67 call item.onClick?.() runtime slot

Totals: 36 runtime slots + 22 retired = 58.

Parity ledger (zod-mirror-parity.test.ts) — every changed row

The mirror's z.input for a refusal arm is undefined, so a runtime-slot key (TS keeps the function) is a measured TYPE drift and lands in KnownDrift; a retired key (?: never) meets undefined on both sides and does not drift. 28 pairs, 35 keys (the 36th, MenuItemSchema.onClick, is a lazy union in EXCLUSIONS). Header counts re-read: 12 → 36 entries, 17 → 52 keys, 146 → 122 pairs with no entry; a class note above ButtonSchema explains the deliberate two-face divergence.

Extended (4): complex.zod.ts#ChatbotSchema: 'body' | 'onError' | 'onSend' · complex.zod.ts#FilterBuilderSchema: 'fields' | 'onChange' · data-display.zod.ts#DataTableSchema: 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder' · form.zod.ts#FormSchema: 'fields' | 'mode' | 'onSubmit' | 'onChange' | 'onCancel'.

New (24): complex.zod.ts#CalendarViewSchema: 'onViewChange' · complex.zod.ts#KanbanSchema: 'onCardMove' | 'onCardClick' · disclosure.zod.ts#AccordionSchema: 'onValueChange' · disclosure.zod.ts#CollapsibleSchema: 'onOpenChange' · disclosure.zod.ts#ToggleGroupSchema: 'onValueChange' · form.zod.ts#ButtonSchema: 'onClick' · form.zod.ts#CheckboxSchema: 'onChange' · form.zod.ts#CodeEditorSchema: 'onChange' · form.zod.ts#DatePickerSchema: 'onChange' · form.zod.ts#FileUploadSchema: 'onChange' · form.zod.ts#InputOTPSchema: 'onChange' · form.zod.ts#InputSchema: 'onChange' · form.zod.ts#SelectSchema: 'onChange' · form.zod.ts#TextareaSchema: 'onChange' · layout.zod.ts#CardSchema: 'onClick' · layout.zod.ts#TabsSchema: 'onValueChange' · navigation.zod.ts#PaginationSchema: 'onPageChange' · overlay.zod.ts#AlertDialogSchema: 'onOpenChange' · overlay.zod.ts#DialogSchema: 'onOpenChange' · overlay.zod.ts#DrawerSchema: 'onOpenChange' · overlay.zod.ts#DropdownMenuSchema: 'onOpenChange' · overlay.zod.ts#HoverCardSchema: 'onOpenChange' · overlay.zod.ts#PopoverSchema: 'onOpenChange' · overlay.zod.ts#SheetSchema: 'onOpenChange'.

Tests

New pin packages/types/src/__tests__/handler-keys-json-refusal-6124.test.ts (240 cases): anchored source census (0 on* sites; the 4 non-on* sites pinned by name), 58 × {declared on the shape with the #6124 guidance · authored object refused with code: 'custom', path [key], message naming the key, "type" and action:button, message === describe · live function refused · isolated shape green without the key}, wording split (RUNTIME SLOT vs RETIRED), the passthrough counter-probe (omit → green with the object kept), arm ≠ tombstone (custom vs invalid_type), and type-level pins: 22 × RetiredIsNever, 36 × KeepsFunction, both helpers shown able to fail. Compiled by tsconfig.test.json — tsc --listFiles lists the file (544 files).

Three existing tests restated for the accept-set change (verdicts unchanged, messages moved): component-fixture-declared-keys.test.ts counter-probe (still RED, now custom + named guidance), menu-item-union.test.ts (function parses on the TS face only; union arm errors dug), chatbot-authoring-face-keys.test.ts (onSend refused by name on the mirror).

Red-first on the unmodified tree (c93b4d5f3, pin file only): vitest Tests 178 failed | 62 passed (240) — census found 58 sites; object refused with invalid_type not custom; functions parsed GREEN; tsc -p tsconfig.test.json: 22 × TS2344 on RetiredIsNever exactly as predicted.

Union on the final commit 11fd53500 (all through os-verify-lock.sh, verdict lines quoted): pnpm --filter @object-ui/types run type-check (echoed tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json) type-check-exit=0 · pnpm exec vitest run packages/types/ examples/schema-catalog/test/component-fixture-declared-keys.test.ts --maxWorkers=2 → Test Files 88 passed (88) / Tests 1455 passed (1455) · pnpm --filter @object-ui/types build → ✓ dist completeness: 1 package(s) complete (118 emitted files verified) · downstream pnpm --filter @object-ui/core run type-check against the rebuilt dist (core's only workspace dep is @object-ui/types) → exit 0.

Ablation (after the commit; trap … EXIT INT TERM; absolute paths): ButtonSchema.onClick reverted to z.function().optional().describe('Click handler') — on-disk proof arm-before=1 mut-before=0 → arm-after=0 mut-after=1, git diff --stat 1/1; no build needed (the pin imports ../zod/form.zod from SOURCE). Result: Tests 6 failed | 234 passed (240) — census (1 site), Button declared/describe, Button object (invalid_type), Button function (GREEN), wording, arm-vs-tombstone — exactly the Button rows. Restore: git checkout HEAD -- ABS_PATH, git diff HEAD empty, git hash-object = git rev-parse HEAD:… = 23a63abe…, git status clean.

Gates (unlocked check:*, verdict lines): check-changeset-presence ✅ 21 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) · check-changeset-no-major ✅ No changeset declares a major bump · check:control-bytes ✅ OK (scanned 6023 tracked text file(s)) · check:spec-symbols ✅ spec symbol derivation: 1333 files scanned · check:doc-types ✅ Every documented component type is registered · check:vi-mock-inherit / check:vi-mock-specifiers ✅ OK.

Lint, narrowed and measured: eslint --no-inline-config --format json over the 22 staged .ts files → population from eslint's own json output = 22 files, 0 errors, 54 warnings; every warning is @typescript-eslint/no-explicit-any and each warned file's merge-base blob lints to the identical E/W count (complex.ts 0/8, data-display.ts 0/30, form.ts 0/10, complex.zod.ts 0/2, data-display.zod.ts 0/1, navigation.zod.ts 0/2, overlay.zod.ts 0/1) — pre-existing; config invariance: eslint.config.js has no projectService / parserOptions.project, so this diff cannot move an untouched file's verdict. Repo-wide pnpm lint left to CI.

Downstream type-check, narrowed and declared: the only breaking TS change is the 22 ?: never members. A read census over packages/*/src, apps/*/src, examples, scripts for files naming both an affected interface and its retired key found three, none of which assigns the key on that type (AppHeader.tsx builds BreadcrumbItem[] segments without onClick; ObjectCalendar.tsx declares its own local CalendarSchema; chatbot renderer.tsx intersects ChatbotSchema with onSend/onClear, and onSendMessage there is the Chatbot component's own prop). @object-ui/core was type-checked for real against the rebuilt dist; the full turbo run type-check --filter='...@object-ui/types' needs the whole repo built and exceeds the foreground cap on a shared box — CI runs it.

Changeset

.changeset/6124-handler-keys-json-refusal.md — @object-ui/types: minor (objectui refuses major); states the accept-set change plainly.

Not in this PR (for the PM)

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…BY NAME (objectui#6124)

The zod mirrors declared 58 `on*` keys (26 distinct) across eight files as
`z.function()` — a declaration no JSON document can satisfy on a JSON-authored
vocabulary. A JSON author who wrote `onClick: { "action": "toast" }` got zod's
bare `invalid_type … expected function`, naming the key and nothing else.

Ruled shape (maintainer 2026-08-30, batch #8: Q1→A, Q2→A+C, Q3→A, Q4→B):
every site keeps its declaration and becomes a NAMED refusal arm in the #5099
`z.custom` + guidance shape (`handlerKeyRefusal()` in `zod/tombstone.zod.ts`):
the message names the key, says why JSON cannot author it, and points at the
node-type spelling PR #6498 established. The same string is the `.describe()`.
Deletion was measured and refused: under `BaseSchema.passthrough()` an
undeclared key is KEPT, and `onClick` rides `SDUI_DOM_PASS_THROUGH_KEYS` into
the DOM listener slot where React throws at click.

Accept-set change (Clause ②): a live function is refused on the mirror too.
Measured on this tree, the only runtime `safeParse` doors into these mirrors
are the CLI validators and `validateSchema` / `safeValidateSchema`;
`SchemaRenderer` validates through `@object-ui/core`'s structural validator.

TypeScript face, measured per key: `SchemaRenderer` spreads every non-metadata
schema key as a React prop, so 36 keys whose function value reaches a renderer
(read off `schema.*`, called as `props.onX`, or spread onto a Radix root / DOM
listener slot) keep their function type; 22 keys nothing reads carry the
`?: never` tombstone (ADR-0049). The parity ledger records the 35 registered
runtime-slot keys as deliberate drift (28 pairs; `MenuItemSchema` is a lazy
union outside the registry).

Out of scope per the ruling: `cell` / `custom` / `validate` /
`renderCellEditor` stay `z.function()`; `EventHandlersSchema` is objectui#6910.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 48 chunks) 3160.3 KB 3191.4 KB
Main entry chunk (gzip) 142.6 KB 350 KB
Entry file index-CljwiCD4.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.33KB 5.59KB
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) 514.48KB 117.36KB
core (index.js) 5.55KB 2.23KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 244.25KB 61.73KB
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.98KB 10.98KB
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) 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.75KB 3.80KB
plugin-calendar (index.js) 46.92KB 12.93KB
plugin-charts (index.js) 70.02KB 19.44KB
plugin-chatbot (index.js) 190.53KB 45.18KB
plugin-dashboard (index.js) 132.63KB 34.56KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 250.63KB 63.90KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.78KB 32.58KB
plugin-gantt (index.js) 166.77KB 40.76KB
plugin-grid (index.js) 208.88KB 56.59KB
plugin-kanban (index.js) 53.21KB 14.66KB
plugin-list (index.js) 113.51KB 27.67KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 29.34KB 8.47KB
plugin-tree (index.js) 8.98KB 3.08KB
plugin-view (index.js) 85.90KB 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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
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-litant
os-litant marked this pull request as ready for review September 2, 2026 08:49
@os-litant
os-litant added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit a8d5c71 Sep 2, 2026
32 checks passed
@os-litant
os-litant deleted the claude/issue-6124-on-handler-keys-refusal-arm branch September 2, 2026 09:04
os-litant added a commit that referenced this pull request Sep 2, 2026
…rable props

objectui#6124 (PR #7339) split the `on*` handler keys in two: 36 runtime
slots keep their function type, and 22 keys nothing reads became `?: never`
tombstones with named refusal arms on the zod mirror. Ten rows across eight
`content/docs` pages went on listing some of the retired 22 as callable
props — a key that is now a `tsc` error to assign and a refusal by name at
authoring time.

The card named three pages (`api/schema-reference.md`,
`data-display/tree-view.mdx`, `form/input-otp.mdx`, five rows); a whole-docs
census resolving each row's `(interface, key)` pair against the shipped
declaration found five more rows on five more pages —
`basic/button-group.mdx` (`ButtonGroupButton.onClick`), and `.onChange` on
`form/calendar.mdx`, `form/combobox.mdx`, `form/command.mdx`,
`form/radio-group.mdx`. A key NAME cannot decide this: `onChange` is retired
on 8 schemas and live on 14, and `input-otp.mdx` carries both dispositions
one line apart.

Seven pages have the retired rows REMOVED. `basic/button-group.mdx` is the
one "marked retired" page and not by taste: `button-group-doc-surface-6347`
asserts set equality between that page's `ButtonGroupButton` block and the
mirror's `.shape`, and a refusal arm is still a key of that shape, so the row
must stay — spelled `never`, with the node-type pointer the tombstone JSDoc
uses. That sibling pin's `onClick` case is updated to read the mirror's
refusal instead of restating the now-stale `() => void`.

The new pin measures the retired population off `packages/types/src` rather
than hand-listing it, flags rows (not prose), and carries six runtime-slot
rows as the blanket-sweep control — two of them on pages this change edits.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
os-project-manager pushed a commit that referenced this pull request Sep 3, 2026
…fuse BY NAME

The objectui#6182 ruling (2026-08-25, option A): the handler-expression string
dialect is not a supported authoring form, on either face. Executed in the
objectui#6124 shape (PR #7339) over the eight sites that PR's `z.function(`
census could not see: four `z.string()` mirrors, three `z.any()` mirrors and
one multi-line `z.function()`.

Per key, measured on origin/main d88e20f:

  runtime slot (callable kept, mirror refuses by name):
    views.zod.ts   DetailViewSchema.onBack     — detail-view spreads onto DetailView,
                                                 handleBack CALLS onBack(); TS twin
                                                 declared `string`, now `() => void`
    crud.zod.ts    DetailSchema.onBack         — register('detail', DetailView)
    crud.zod.ts    ActionSchema.onClick        — ActionRunner awaits action.onClick()
    complex.zod.ts CalendarViewSchema.onEventClick — pickHostCallbacks
  retired (`?: never`, mirror refuses by name):
    app.zod.ts     AppActionSchema.onClick     — nothing reads AppComponentSchema.actions[]
    reports.zod.ts ReportBuilderSchema.onSave / onCancel — no report-builder renderer
    crud.zod.ts    CRUDDialogSchema.onClose    — no crud-dialog renderer

No key was read as a string and dispatched (the card's STOP class did not occur).
The three views.zod.ts event-NAME keys (PR #6899) are untouched; the new pin reads
their describe text as the reason they survive the census.

zod-mirror-parity.test.ts gains the three runtime-slot drift rows (39 entries /
56 keys, counted); component-docs-retired-handler-keys-7340.test.ts records the
ruled move of the `?: never` census (22 → 26) and the two names now retired
everywhere (`onClose`, `onSave`); phase2-schemas.test.ts keeps its old
string-callback fixture as the negative reading. content/docs/core/app-schema.mdx
carries the two edits the #7340 pin and check:doc-snippets require.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
os-project-manager pushed a commit that referenced this pull request Sep 4, 2026
…led`, corpus stops authoring 29 unread keys

Executes the maintainer ruling of 2026-09-04 (decision batch #25, option 2):
retire the authored surface, keep `button-group` presentational, and wire the
declared key the renderer never read.

Renderer: `ButtonGroupButton` declares `disabled?: boolean` and the renderer
forwarded nothing — it passed `variant`, `size`, `className` and `label` only,
so a button declaring `disabled: true` rendered live. It now forwards the
value, matching every sibling that declares item-level `disabled` (`tabs`,
`select`, `dropdown-menu`, `menubar`, `context-menu`, `toggle-group` since
objectui#4632). No new key, no new type member, no accept/refuse change.

Corpus: all six catalog fixtures authored four undeclared keys across 29
occurrences — `buttons[].value` x17, `buttons[].icon` x8, group `value` x2,
`selectionMode` x2 — every one parsing green through `BaseSchema.passthrough()`.
All 29 are gone. `with-icons.json` is removed rather than emptied: with `icon`
retired it authored no icons and duplicated `basic-button-group.json`.

`onClick` is deliberately NOT wired. objectui#6124 (PR #7339, landed
2026-09-02) retired it — `onClick?: never` plus a named zod refusal — so the
ruling's second key no longer exists to forward.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 9, 2026
… keys as authorable props (objectstack-ai#7350)

objectui#6124 (PR objectstack-ai#7339) split the `on*` handler keys in two: 36 runtime
slots keep their function type, and 22 keys nothing reads became `?: never`
tombstones with named refusal arms on the zod mirror. Ten rows across eight
`content/docs` pages went on listing some of the retired 22 as callable
props — a key that is now a `tsc` error to assign and a refusal by name at
authoring time.

The card named three pages (`api/schema-reference.md`,
`data-display/tree-view.mdx`, `form/input-otp.mdx`, five rows); a whole-docs
census resolving each row's `(interface, key)` pair against the shipped
declaration found five more rows on five more pages —
`basic/button-group.mdx` (`ButtonGroupButton.onClick`), and `.onChange` on
`form/calendar.mdx`, `form/combobox.mdx`, `form/command.mdx`,
`form/radio-group.mdx`. A key NAME cannot decide this: `onChange` is retired
on 8 schemas and live on 14, and `input-otp.mdx` carries both dispositions
one line apart.

Seven pages have the retired rows REMOVED. `basic/button-group.mdx` is the
one "marked retired" page and not by taste: `button-group-doc-surface-6347`
asserts set equality between that page's `ButtonGroupButton` block and the
mirror's `.shape`, and a refusal arm is still a key of that shape, so the row
must stay — spelled `never`, with the node-type pointer the tombstone JSDoc
uses. That sibling pin's `onClick` case is updated to read the mirror's
refusal instead of restating the now-stale `() => void`.

The new pin measures the retired population off `packages/types/src` rather
than hand-listing it, flags rows (not prose), and carries six runtime-slot
rows as the blanket-sweep control — two of them on pages this change edits.


Claude-Session: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 9, 2026
…fuse BY NAME (objectstack-ai#7468)

The objectui#6182 ruling (2026-08-25, option A): the handler-expression string
dialect is not a supported authoring form, on either face. Executed in the
objectui#6124 shape (PR objectstack-ai#7339) over the eight sites that PR's `z.function(`
census could not see: four `z.string()` mirrors, three `z.any()` mirrors and
one multi-line `z.function()`.

Per key, measured on origin/main d88e20f:

  runtime slot (callable kept, mirror refuses by name):
    views.zod.ts   DetailViewSchema.onBack     — detail-view spreads onto DetailView,
                                                 handleBack CALLS onBack(); TS twin
                                                 declared `string`, now `() => void`
    crud.zod.ts    DetailSchema.onBack         — register('detail', DetailView)
    crud.zod.ts    ActionSchema.onClick        — ActionRunner awaits action.onClick()
    complex.zod.ts CalendarViewSchema.onEventClick — pickHostCallbacks
  retired (`?: never`, mirror refuses by name):
    app.zod.ts     AppActionSchema.onClick     — nothing reads AppComponentSchema.actions[]
    reports.zod.ts ReportBuilderSchema.onSave / onCancel — no report-builder renderer
    crud.zod.ts    CRUDDialogSchema.onClose    — no crud-dialog renderer

No key was read as a string and dispatched (the card's STOP class did not occur).
The three views.zod.ts event-NAME keys (PR objectstack-ai#6899) are untouched; the new pin reads
their describe text as the reason they survive the census.

zod-mirror-parity.test.ts gains the three runtime-slot drift rows (39 entries /
56 keys, counted); component-docs-retired-handler-keys-7340.test.ts records the
ruled move of the `?: never` census (22 → 26) and the two names now retired
everywhere (`onClose`, `onSave`); phase2-schemas.test.ts keeps its old
string-callback fixture as the negative reading. content/docs/core/app-schema.mdx
carries the two edits the objectstack-ai#7340 pin and check:doc-snippets require.


Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 9, 2026
…led`, corpus stops authoring 29 unread keys (objectstack-ai#7596)

Executes the maintainer ruling of 2026-09-04 (decision batch objectstack-ai#25, option 2):
retire the authored surface, keep `button-group` presentational, and wire the
declared key the renderer never read.

Renderer: `ButtonGroupButton` declares `disabled?: boolean` and the renderer
forwarded nothing — it passed `variant`, `size`, `className` and `label` only,
so a button declaring `disabled: true` rendered live. It now forwards the
value, matching every sibling that declares item-level `disabled` (`tabs`,
`select`, `dropdown-menu`, `menubar`, `context-menu`, `toggle-group` since
objectui#4632). No new key, no new type member, no accept/refuse change.

Corpus: all six catalog fixtures authored four undeclared keys across 29
occurrences — `buttons[].value` x17, `buttons[].icon` x8, group `value` x2,
`selectionMode` x2 — every one parsing green through `BaseSchema.passthrough()`.
All 29 are gone. `with-icons.json` is removed rather than emptied: with `icon`
retired it authored no icons and duplicated `basic-button-group.json`.

`onClick` is deliberately NOT wired. objectui#6124 (PR objectstack-ai#7339, landed
2026-09-02) retired it — `onClick?: never` plus a named zod refusal — so the
ruling's second key no longer exists to forward.


Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants