Skip to content

fix(types): declare CheckboxSchema.wrapperClass on both faces - #7723

Merged
os-justin merged 1 commit into
mainfrom
claude/issue-6938-checkbox-wrapperclass
Sep 5, 2026
Merged

fix(types): declare CheckboxSchema.wrapperClass on both faces#7723
os-justin merged 1 commit into
mainfrom
claude/issue-6938-checkbox-wrapperclass

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #6938

What

Declares wrapperClass?: string on CheckboxSchema, on both faces — the TypeScript interface in packages/types/src/form.ts (docblock citing the read site, in the form the objectui#6939 declarations use) and the zod mirror in packages/types/src/zod/form.zod.ts — plus one pin and an @object-ui/types patch changeset. Nothing else moves: no consolidation with className, no other schema block touched.

Why the card shrank to one key (measured, not inherited)

The card has two halves. Its context-menu half — ContextMenuSchema.triggerClassName / contentClassName / modal and the retirement of the dead required children — is already on main, landed with objectui#6939 group 1 (PR objectui#7456): re-verified on this branch's base f96a781 at packages/types/src/overlay.ts:617/624/630 and zod/overlay.zod.ts:237-246. It is not touched here.

The residue is CheckboxSchema.wrapperClass. On main the wrapperClass at form.ts:133 belongs to InputSchema and the one at :606 to FileUploadSchema; the CheckboxSchema interface (from :306) declared none, and the mirror (form.zod.ts:255) declared none — while packages/components/src/renderers/form/checkbox.tsx:36 reads cn("flex items-center space-x-2", schema.wrapperClass). The read compiled through BaseSchema's index signature and the value parsed through .passthrough(), admitted unexamined. This pulls the declaration back to delivered behaviour (Bug side, triage half 甲).

Contract review (Clause-② yes — a new declared key on a published payload type)

  • Accept set: only widens toward what already renders. The key is optional; no document that validated before stops validating.
  • Value dimension: the mirror now REFUSES a non-string wrapperClass it used to admit unexamined (wrapperClass: 42 — green before, refused at the key now). Enforcement of the declared type, not a new capability.
  • Business need, measured: in-repo authors of wrapperClass on a checkbox node — zero (examples/, apps/, content/, skills/ grepped). The read is the fact the declaration records; the same key on the same class of read is declared on InputSchema, FileUploadSchema and FilterBuilderSchema. The checkbox was left out only because its doc page's schema block is a six-line summary.
  • Changeset level patch, following the objectui#6939 group-1 precedent (.changeset/6939-overlay-trigger-mirror.md, same argument).

Pin — packages/types/src/__tests__/checkbox-wrapper-class-6938.test.ts (10 tests)

Form borrowed from undeclared-but-consumed-keys-6150.test.ts: membership on the mirror's own .shape (acceptance cannot tell declared from admitted-unexamined under passthrough); parse accepts the key and the value survives, through the mirror and through safeValidateSchema; refusal of a wrong-typed value AT the key; invariant type-level pins on the TS face (an undeclared key resolves to any through the index signature, so Equal fails); and a CONTROL key the renderer does not read — labelClass, checked against the read set derived from checkbox.tsx — that stays undeclared on both faces (absent from .shape, resolves to the index signature, still admitted unexamined).

Ablation (fix committed first; restore by trap, proven by bytes)

Mutation: git checkout BASE -- form.ts form.zod.ts (both faces lose the declaration; anchors objectui#6938 1 → 0 in each file). Observed direction: RED — vitest 2 failed | 8 passed (10) (mirror-shape membership, refusal at the key; the 8 that stay green are the passthrough before-state by design) and tsc -p tsconfig.test.json exit 2 with TS2344 on the two type-level pins (:75, :76). Restore: git checkout HEAD -- ..., git diff HEAD = 0 lines, on-disk blob hashes equal to HEAD's (12dd197…, 21ee145…), anchors back to 1. Restored leg: 10 passed (10), tsc exit 0. Resolution path is src on both legs (relative imports in the pin; tsconfig.test.json reads src), so no dist preflight applies.

Gates (exit captured before any pipe; at HEAD d54e1b9 unless noted)

command exit
pnpm exec vitest run --maxWorkers=2 packages/types/ 0 — Test Files 112 passed (112), Tests 1935 passed (1935)
pnpm --filter @object-ui/types type-check (three tsc projects incl. tsconfig.test.json) 0
pnpm --filter @object-ui/types lint 0 — 0 errors, 269 pre-existing warnings, none in the touched hunks or the new file (identical pre-commit tree)
pnpm --workspace-concurrency=2 --filter "@object-ui/components^..." build then pnpm --filter @object-ui/components type-check 0 / 0 — the consumer reads @object-ui/types from dist, rebuilt with this change (dist/form.d.ts:346 carries the key); identical pre-commit tree
node scripts/check-changeset-presence.mjs 0
node scripts/check-changeset-fixed.mjs 0
node scripts/check-changeset-no-major.mjs 0
node scripts/check-control-bytes.mjs 0 (re-run after staging — it scans tracked files only)
pnpm check:spec-symbols 0
check:doc-types not run — no docs touched; measured that it checks type literals only, and no doc gate requires a documented row for a declared key

zod-mirror-parity.test.ts stays green with the key on both faces (it is among the 112). Heavy runs went through the shared verify lock; repo-wide pnpm lint is CI's.

Docs

content/docs/components/form/checkbox.mdx untouched: its schema block is a six-line summary (the card's own diagnosis of the asymmetry) and no doc-parity gate asks for the row; adding one row there while checked, description, error and className are also absent from that summary is a docs card, not this one — handed to the PM in the report.

Out of scope, filed

objectui#7722 — five more renderers read schema.wrapperClass undeclared on either face (switch, textarea, date-picker, select, list); same class, same remedy; dedup done (21 search hits read, #6951 / #6952 read in full).

Serial

Base f96a781 = origin/main at PR time (re-fetched; nothing to merge). objectui#7530 edits the top region of form.zod.ts, a different region from the CheckboxSchema block; conflicts, if any, go to the merge queue. packages/types is not a governed surface; needs:contract-review attached; stays draft for the in-seat review at tier.

Dev of the domain:spec @ objectui dispatch, session session_01BAZFhALsQsGqxui8sNqM8s.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s


Generated by Claude Code

`packages/components/src/renderers/form/checkbox.tsx:36` reads
`cn("flex items-center space-x-2", schema.wrapperClass)` and neither the
TypeScript interface (`form.ts`) nor the zod mirror (`zod/form.zod.ts`)
declared the key: it compiled through `BaseSchema`'s index signature and
parsed through `.passthrough()`, admitted unexamined. The same key, on the
same class of read, is declared on `FileUploadSchema` and
`FilterBuilderSchema`; the checkbox was left out only because its doc
page's schema block is a six-line summary.

Pin: `__tests__/checkbox-wrapper-class-6938.test.ts` — mirror-shape
membership, parse acceptance with the value surviving (mirror and union
entry point), refusal of a wrong-typed value at the key, invariant
type-level pins on the TS face, and a control key the renderer does not
read (derived from the renderer source) that stays undeclared on both
faces.

Changeset: `@object-ui/types` patch — the accept set only widens toward
what already renders.

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

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3186.1 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-DWqx4_Ic.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) 510.70KB 116.21KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.27KB 61.22KB
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) 4.28KB 1.75KB
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.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.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.75KB 3.80KB
plugin-calendar (index.js) 47.87KB 13.31KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.37KB 46.41KB
plugin-dashboard (index.js) 132.87KB 34.68KB
plugin-designer (index.js) 212.86KB 43.19KB
plugin-detail (index.js) 250.55KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.26KB 41.00KB
plugin-grid (index.js) 209.29KB 56.78KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.44KB 6.78KB
plugin-markdown (index.js) 13.93KB 4.81KB
plugin-report (index.js) 43.59KB 11.97KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.20KB 3.19KB
plugin-view (index.js) 85.24KB 20.94KB
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) 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 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 (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

Provenance for clearing needs:contract-review on this PR (2026-09-05T11:57Z): in-seat contract review at CONTRACT_REVIEW_TIER returned PASS on the card — #6938 (comment) (Implemented-by: branch claude/issue-6938-checkbox-wrapperclass; Reviewed-by: session_01BAZFhALsQsGqxui8sNqM8s, served tier claude-fable-5-1 re-read at 11:55Z). Head reviewed: d54e1b9. The card side never carried the label, so the PR carrier is the only one to strip; the strip is a whole-set label write with read-back in the same stroke as this comment. If the head moves past d54e1b9, the label is re-hung and the review re-done.


Generated by Claude Code

@os-justin
os-justin marked this pull request as ready for review September 5, 2026 12:10
@os-justin
os-justin added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit b74a859 Sep 5, 2026
34 checks passed
@os-justin
os-justin deleted the claude/issue-6938-checkbox-wrapperclass branch September 5, 2026 12:26
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): 4 more genuinely-read undeclared keys the #6150 census could not see, plus one declared-but-dead key — all on the same 8 renderers

2 participants