Skip to content

fix(types): anchor ObjectGalleryProps / ObjectDataTableProps schema to exported schema types that extend BaseSchema (#6576, #6914) - #7355

Merged
os-litant merged 2 commits into
mainfrom
claude/issue-6576-widget-schema-anchors
Sep 2, 2026
Merged

os-litant merged 2 commits into
mainfrom
claude/issue-6576-widget-schema-anchors

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes #6576
Fixes #6914
Clause-②: yes — contract review required before release

Maintainer ruling 2026-08-31 (director seat, batch #10, option A, verbatim 「同意」), executed as written: two new exported schema types in @object-ui/types, each extends BaseSchema, and the two widget prop types that declared their schema as a hand-rolled inline literal are anchored to them. #6914 (the two keys the data-table reads and declared nowhere) is closed in the same stroke, per the erratum: drillDown and onRowClick.

Session for this implementation: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho (kept in prose because a PR body edit strips the footer form).

What changed

No runtime behaviour changes; both widgets render exactly as before (792 plugin-list and 805 plugin-dashboard tests unchanged and green).

Contract-review pack — accept-set before / after

Real tsc --noEmit against the real prop types, one labelled statement per line, diagnostics mapped by line number (origin/main at 2956d7af8, then this head). Each row is the exact literal assigned to the prop's schema type. The origin/main column repeats the prior report's probe and re-measures it on the same instrument.

ObjectGalleryProps.schema — PUBLISHED (plugin-list/src/index.tsx exports the type)

case literal origin/main this head movement
G1 { objectName, visibleWhen } (no type) REJECTED — TS2353 visibleWhen does not exist REJECTED — TS2741 type is missing reason changed, see G6
G6 { type: 'object-gallery', objectName, visibleWhen } REJECTED — TS2353 type does not exist ACCEPTED WIDENS — a real base member is writable
G2 { type: 'object-gallery', objectName } REJECTED — TS2353 type does not exist ACCEPTED WIDENS — the node's own type key can be spelled
G4 { objectName } (minimal) ACCEPTED REJECTED — TS2741 type is missing NARROWS — type is required (the prior report's G6)
G12 { type: 'gallery', objectName } REJECTED — TS2353 type does not exist REJECTED — TS2322 "gallery" not assignable to "object-gallery" NARROWS for the right reason — the literal is the registry key
G3 / G7 { [type,] objectName, data: 'not-an-array' } REJECTED — TS2322 string not assignable to array of records REJECTED — same TS2322 UNCHANGED — the prior report predicted data would collapse to any under a BaseSchema INTERSECTION; extends overrides the member instead, so the widening does not happen
G11 { type, objectName, data: [{ id: 1 }] } REJECTED — TS2353 type ACCEPTED WIDENS (via type)
G9 { objectName, visible: 42 } (no type) REJECTED — TS2353 visible does not exist REJECTED — TS2322 number not assignable to string or boolean reason changed: unknown key → wrong-typed declared member
G8 { type, objectName, visible: 42 } REJECTED — TS2353 type REJECTED — TS2322 same as G9 with type present
G10 { type, objectName, bind: 42 } REJECTED — TS2322 (local bind?: string) REJECTED — TS2322 (inherited bind?: string) UNCHANGED — inherited now
G5 { type, objectName, visibleWhn: 'typo' } REJECTED — TS2353 type (the unknown key would also have been refused) ACCEPTED WIDENS — the ceiling below

Net for the published type: WIDENS to every BaseSchema member (21 members gained; visibleWhen, visible, id, hidden, disabled, testId, ariaLabel, label, style, … are writable and type-checked), NARROWS in one place (type required and pinned to 'object-gallery'). data, bind, filter, gallery, navigation, grouping, imageField, titleField keep their exact types.

ObjectDataTableProps.schema — NOT exported from plugin-dashboard/src/index.tsx (reaches consumers structurally as the exported component's prop)

case literal origin/main this head movement
D1 { type: 'object-data-table', objectName, bogusKey: 1 } ACCEPTED ACCEPTED UNCHANGED — the ceiling below
D2 { type, objectName, visible: 42 } ACCEPTED (absorbed by the literal's index signature) REJECTED — TS2322 number not assignable to string or boolean NARROWS — a wrong-typed base member is refused
D3 { type, objectName, drillDown: { enabled: true, mode: 'record' } } ACCEPTED (as any) ACCEPTED (as DrillDownConfig) UNCHANGED in verdict; DECLARED now (#6914)
D4 { type, objectName, drillDown: { enabled: 'yes' } } ACCEPTED (absorbed) REJECTED — TS2322 string not assignable to boolean NARROWS — a wrong-shaped drillDown is refused
D5 { type, objectName, onRowClick: an arrow function of one untyped parameter } REJECTED — TS7006 row implicitly any (the key resolved to any, so nothing typed the parameter) ACCEPTED — row is contextually typed WIDENS in declaration — the handler can be written without an annotation (#6914)
D10 { type, objectName, onRowClick: 'not-a-function' } ACCEPTED (absorbed) REJECTED — TS2322 NARROWS
D6 { type, objectName, data: 'not-an-array' } REJECTED — TS2322 string not assignable to any[] REJECTED — same UNCHANGED
D7 { type: 'data-table', objectName } ACCEPTED (type: string) REJECTED — TS2322 "data-table" not assignable to "object-data-table" NARROWS — the literal is the registry key
D8 { type: 'object-data-table' } ACCEPTED ACCEPTED UNCHANGED
D9 { objectName } (no type) REJECTED — TS2741 type missing REJECTED — TS2741 UNCHANGED (type was already required)
D11 { type, objectName, bind: 42 } REJECTED — TS2322 (local bind?: string) REJECTED — TS2322 (inherited) UNCHANGED
D12 { type, objectName, visibleWhen: 'ready' } ACCEPTED (absorbed as any) ACCEPTED (declared string) UNCHANGED in verdict; typed now

Net for the unpublished type: NARROWS (the literal's own index signature is gone, so declared base members and the two #6914 keys are type-checked; type is the registry key). In-repo call sites re-checked: every plugin-dashboard test and DrillDownDrawer.tsx compile unchanged — the tests that write other spellings already carry as any.

The ceiling, stated plainly

BaseSchema still carries [key: string]: any (#5155 remains open). Anchoring inherits it, so an UNKNOWN key (visibleWhn, bogusKey) remains ACCEPTED on BOTH prop types after this PR — on the data-table it always was; on the gallery it is new (G5). The ruling accepted that cost; the pins record it as a counter-probe rather than hiding it.

Verification (on the committed heads f84c14c17 — the implementation — and the final head named below, tree clean)

Every heavy command ran under scripts/pm/os-verify-lock.sh (slot dev-6576); verdict lines quoted from the tools, not from a bare exit status.

  • Dependency closure built first (turbo run build for the three plugins' dependency closures, then every packages/*: Tasks: 39 successful, 39 total).
  • pnpm --filter @object-ui/types run build → tsc && node ../../scripts/check-dist-completeness.mjs, exit 0; dist/index.d.ts carries both new names.
  • pnpm --filter … run type-check — script echoed for each: @object-ui/types@17.6.0 type-check, @object-ui/plugin-list@17.6.0 type-check, @object-ui/plugin-dashboard@17.6.0 type-check, @object-ui/plugin-view@17.6.0 type-check — 0 errors each (each script is tsc --noEmit plus the package's test project, so every pin and both ledgers are compiled). plugin-view is the other producer of object-gallery nodes; it builds a plain node and never passes through ObjectGalleryProps, so nothing there moved.
  • pnpm --filter … run lint (types / plugin-list / plugin-dashboard): 0 errors each (warnings only, pre-existing no-explicit-any in files that already carried it; the new ObjectDataTableSchema transcribes the literal's any spellings unchanged).
  • Vitest (root-relative paths, no --), on the final head e5b24a3bb: packages/types/ → Test Files 88 passed (88), Tests 1434 passed (1434); packages/plugin-list/ → Test Files 63 passed (63), Tests 792 passed (792); the six ledger and pin files together (zod-mirror-parity, handler-keys-json-refusal-6124, base-bind-declared, widget-schema-anchors-6576, both schemaAnchor-6576 pins) → Test Files 6 passed (6), Tests 286 passed (286). packages/plugin-dashboard/ on f84c14c17 (the second commit touches only a plugin-list test file) → Test Files 86 passed (86), Tests 805 passed (805). The second commit exists because the first post-commit run of base-bind-declared went RED: its git grep scan covers tracked files, and the plugin-list pin's synthetic control spelled the pre-finding(types): two widget prop types declare their schema as a hand-rolled inline object with no BaseSchema in its ancestry #6576 literal with a bind member — the ratchet did exactly its job; the control now uses className.
  • Red-first, on origin/main with the pins added and nothing else: tsc -p tsconfig.test.json — types 17 errors (all in the new pin: TS2724 the two names do not exist, TS2344 on every type assertion), plugin-list 9 errors, plugin-dashboard 10 errors (TS2724, TS2344, four TS2578 unused directives where the literal absorbed the wrong value); vitest on the types pin: 6 failed. GREEN here on all three.
  • Ablation (each leg on the committed tree; mutation proven by blob hash equal to the origin/main blob, restore proven by blob hash equal to HEAD and git diff HEAD empty; a trap restored both files on every exit path): reverting only ObjectGallery.tsx → the types pin fails exactly one test naming packages/plugin-list/src/ObjectGallery.tsx, and plugin-list's test project reports 8 errors in ObjectGallery.schemaAnchor-6576.test.ts (plus the two gallery test literals); reverting only ObjectDataTable.tsx → the types pin fails exactly two tests naming packages/plugin-dashboard/src/ObjectDataTable.tsx (anchor shape; the finding(plugin-dashboard): ObjectDataTableProps.schema reads drillDown and onRowClick and declares neither — both absorbed by its [key: string]: any #6914 casts are back), and plugin-dashboard's test project reports 9 errors, all in ObjectDataTable.schemaAnchor-6576.test.ts.
  • Drift census on this head (read keys off schema, cast-aware, minus the members the checker resolves on the built dist declaration; both widgets in one run): ObjectGallery read-not-declared [] (10 reads, 28 declared), ObjectDataTable read-not-declared [] (7 reads, 29 declared); synthetic control with drillDown removed from the declared set reports ["drillDown"], so the instrument sees a missing key. On origin/main the same census read ['drillDown','onRowClick'] for the data-table (prior report).
  • Gates: check-changeset-presence (11 published source files changed, changeset present), check-changeset-no-major (✅ No changeset declares a major bump), check-changeset-fixed (✅ All workspace packages are in the changeset fixed group), check:readme-exports (✅ OK … 406 self-imports judged (406 real, 0 wrong-path, 0 fabricated) — the @object-ui/types README lists no object-* schema types, so no README edit is required by the gate), check:spec-symbols (exit 0 — neither new name is a spec export; control GalleryConfigSchema found in the spec api-surface), check:control-bytes OK, check:phantom-deps OK, check:self-import OK, check:esm-specifiers OK, check:published-dist OK, check:side-effects-array OK, check:element-data-source-declaration OK, check:spec-floors OK, check:doc-types OK. NOT MEASURED locally: check:sdui-registration-pins (❌ No console build to weigh at apps/console/dist/assets — a prerequisite, not a verdict; registrations are untouched) and check:eager-closure (needs the console bundle). CI owns the repo-wide farm.
  • Governed-surface check (from ../objectstack, final file list): governed-surface predicate: 0 of 15 path(s) hit the register → ✅ NOT governed.

Deviations from the dispatch, declared

  1. drillDown on the zod mirror: ledgered instead of mirrored. The ruling admits either route (「新类型入镜或按台账既有词表申报」); mirroring it needs a paired DrillDownConfigSchema — a third new export the ruling did not name, whose wiring into ChartSchema would move that published validator — and an inline unpaired restatement would strip nested keys silently (z.object drops unknown keys), the very drift class the ledger exists for. So the new pair is SEEDED in UnmirroredDeclared with the ChartSchema.drillDown vocabulary (LOCAL), its doc states why this is a seed and not growth on an existing pair, and the repair is filed as finding(types): DrillDownConfig has no zod mirror — drillDown is declared on ChartSchema and ObjectDataTableSchema and validated on neither #7352. The reviewer should weigh this: the ledger's shrink-only rule is written for keys that appear on registered pairs.
  2. onRowClick on the mirror is a handlerKeyRefusal arm (as the dispatch said), which also means the 6124 census and its 36 / 58 pins moved to 37 / 59 and its file list to nine. The alternative — no arm, RuntimeOnlyDeclared (the pre-finding(types): 28 zod-mirror keys are declared z.function(), which NO JSON document can satisfy — 14 in-repo corpus files author them and are silently invalid #6124 objectql policy group) — was not taken because the 2026-08-30 ruling's rationale (an undeclared key is a silent accept that forwards the value) applies to a new mirror exactly as to an old one.
  3. Fresh gallery literals: TWO test files needed the type key (cells and coverValue), not one — the dispatch assumed only cells. Both are tests; no producer changed.
  4. ObjectDataTableProps.schema is the bare named type, not NamedSchema & {…}: with a dedicated schema type there are no widget-only extras to intersect, which is the ObjectMapProps shape; PivotTable / Timeline intersect because their schemas are generic nodes.
  5. The prior report's G7 prediction (gallery data collapses to any) did not hold: it was measured on a BaseSchema & {…} intersection; an extends member overrides. Reported as UNCHANGED above and pinned.
  6. Not extended: the ObjectQLComponentSchema unions (TS and zod) and AnyComponentSchema do not include the two new types. Adding them changes what validateSchema accepts for object-gallery / object-data-table nodes — a third published surface the ruling did not name. Flagged for the reviewer rather than decided.

Not in this PR (for the PM)

🤖 Generated with Claude Code

https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho


Generated by Claude Code

… to exported schema types that extend BaseSchema (objectui#6576, #6914)

Two widget prop types declared their `schema` as a hand-rolled inline object
literal with no `BaseSchema` in its ancestry — the only two `Object*Props` in
the repo whose `schema` anchored to no named type. Maintainer ruling
2026-08-31 (option A): mint `ObjectGallerySchema` and `ObjectDataTableSchema`
in `@object-ui/types`, each `extends BaseSchema`, and anchor both.

- `ObjectGalleryProps.schema` (published) WIDENS to every `BaseSchema` member
  (`visibleWhen` was a compile error on the literal) and NARROWS in one place:
  `type` is required and pinned to `'object-gallery'`. `data` stays typed —
  an interface member overrides, it does not intersect.
- `ObjectDataTableProps.schema` (not exported) loses the literal's own
  `[key: string]: any` and declares the two keys the widget read behind casts,
  `drillDown: DrillDownConfig` and `onRowClick` (objectui#6914); the casts go.
  A wrong-typed base member and a wrong-shaped `drillDown` are refused now.
- Zod mirrors of both under `@object-ui/types/zod`, registered in the parity
  ledger: `onRowClick` is a `handlerKeyRefusal` runtime-slot arm (the first on
  an objectql mirror; `KnownDrift` + the 6124 census updated), `drillDown` is
  seeded in `UnmirroredDeclared` — `DrillDownConfig` has no zod mirror and
  minting one is outside this ruling.
- The objectui#6574 `bind` ratchet entries for both files are removed and the
  ratchet's prose rewritten: the defect was #6576's, not #5155 / #6269's.
- Red-first pins: `widget-schema-anchors-6576.test.ts` (types), and one
  compile-time pin beside each widget. Two gallery test literals gain the now
  required `type` key.

Unchanged and stated: an unknown key still compiles on both, because
`BaseSchema`'s index signature is inherited (objectui#5155, open).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
… `bind` member (objectui#6576)

`base-bind-declared.test.ts` scans every TRACKED file for a schema-side
`bind?:` re-declaration. The new pin's "this assertion can fail" control was
written as the pre-#6576 literal shape — `{ objectName?: string; bind?: string }`
— which read as exactly such a re-declaration the moment the file was
committed (green while untracked, red on the first post-commit run). The
control keeps its job with `className` in that slot; nothing else moves.

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) 3166.7 KB 3191.4 KB
Main entry chunk (gzip) 142.7 KB 350 KB
Entry file index-CbDKJ2sq.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.59KB 117.40KB
core (index.js) 5.80KB 2.32KB
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) 47.00KB 12.97KB
plugin-charts (index.js) 70.02KB 19.44KB
plugin-chatbot (index.js) 194.82KB 46.02KB
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.93KB 40.82KB
plugin-grid (index.js) 208.92KB 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.15KB 8.40KB
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 13:22
@os-litant
os-litant added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit 4ca30d0 Sep 2, 2026
32 checks passed
@os-litant
os-litant deleted the claude/issue-6576-widget-schema-anchors branch September 2, 2026 13:38
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 9, 2026
…and the DrillDownConfig mirror (objectui#7363, objectui#7352) (objectstack-ai#7701)

* feat(types): add ObjectGallerySchema and ObjectDataTableSchema to the ObjectQL union on both faces (objectui#7363)

PR objectstack-ai#7355 (objectui#6576) minted both schemas and deliberately left
`ObjectQLComponentSchema` alone, so `AnyComponentSchema` had no arm for an
`object-gallery` or `object-data-table` node: `validateSchema` /
`safeValidateSchema` / `objectui validate` refused such a document as matching
no arm, exactly as before the schemas existed, and a wrong-typed declared key
on it could never be diagnosed by name.

Both unions gain the two members, in the same order. The pin
(`objectql-union-arms-7363.test.ts`) is two-legged per node type: a minimal
document validates, and a wrong-typed DECLARED key is refused BY NAME — the
by-name half is the load-bearing one, since the documents were refused before
too, for the wrong reason. Measured on the ten-arm tree first: 9 of its
assertions red (`data` and `className` probes dropped because a sibling arm
already named them there).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E

* wip(types): DrillDownConfig mirror in progress — INCOMPLETE AND UNREVIEWED

Rescue commit made by the PM seat after the dispatched dev was terminated
mid-task by an account session limit (HTTP 429, resets 2026-09-05T06:40Z).
Committed so the work survives container reclaim; it is NOT a claim that the
change is correct or finished.

Observed state at rescue time, and nothing beyond it:
  M packages/types/src/zod/data-display.zod.ts
  M packages/types/src/zod/index.zod.ts
  M packages/types/src/zod/objectql.zod.ts
  ?? packages/types/src/__tests__/drill-down-config-mirror-7352.test.ts

The dev's last recorded status was that the instrument was written and the
gap-proof run on BASE (both files, expected red) had not yet been executed.
No gate was run against this content.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E

* feat(types): mirror DrillDownConfig in zod and wire it into both declaring mirrors (objectui#7352)

Completes the rescued WIP commit f7a428f, which carried the mirror, the barrel
export and the behaviour pin, and had never been run through a gate. What that
content was missing, measured rather than assumed:

  - `zod-mirror-parity.test.ts` did not know the new export. Its population census
    was RED ("New zod export(s) in packages/types/src/zod/ that this guard does not
    cover: data-display.zod.ts#DrillDownConfigSchema"), and `tsc -p tsconfig.test.json`
    was RED on the staleness ratchet at line 1478 — both `drillDown` rows in
    `UnmirroredDeclared` had become stale the moment the mirror wired in. The pair
    is now registered in MIRRORS / Declared against the LOCAL `DrillDownConfig`
    (the `ObjectMapConfigSchema` precedent), both rows are retired with the movement
    recorded, and every count the change moves is updated: 160 → 161 pairs,
    UnmirroredDeclared 15 / 96 → 13 / 94 (LOCAL 12 / 83 → 11 / 82), the
    "no entry in either" population 144 → 147. The stale "143 of the 160" beside
    `assertionRatchetAcceptsCleanPair` — already wrong before this change — reads
    147 / 161 with the rest.
  - One assertion in the pin was wrong about the DECLARATION, not about the mirror:
    it expected `report: { name, columns }` (an inline report missing `objectName`)
    to be refused. `DrillDownConfig`'s second arm is `{ name: string }`, which is
    not exact, so TypeScript accepts that value — refusing it would make the mirror
    NARROWER than the declaration, the drift class this ledger exists to stop. It
    moved to the accepted table, annotated `DrillDownConfig` so
    `tsc -p tsconfig.test.json` is the standing witness.
  - `widget-schema-anchors-6576.test.ts` listed `drillDown` as a ledgered
    unmirrored read. The mirror declares it now, so the exception is retired; a
    stale one widens `declared` for a key nothing checks any more.

Changeset added: the new `/zod` export and the accept-set narrowing are published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E

* docs(types): re-derive the parity ledger's absolute counts — 155 pairs, not 161 (objectui#7352)

Comment-only. Contract review on PR objectstack-ai#7701 caught that the pair count this card
moved was stale before it: I added 1 to a number that had been wrong twice, and
rewrote one wrong derived figure into a different wrong one.

Re-derived by two independent methods that agree, rather than by stepping a delta:
a TypeScript AST walk counting `PropertyAssignment` nodes in the `MIRRORS`
initializer and `PropertySignature` members in each ledger interface, and a
line-oriented parse of the same blocks.

  MIRRORS pairs                      155   (prose said 161)
  KnownDrift                     40 / 56   (unchanged, and correct)
  UnmirroredDeclared             13 / 94   (unchanged, and correct)
  RuntimeOnlyDeclared             7 / 24   (unchanged, and correct)
  union of the two unmirrored ledgers 14   (unchanged, and correct)
  clean population                   141   (prose said 147)
  pairs with no KnownDrift entry     115   (prose said 121)

The same walk run over this file at three revisions confirms how it drifted, so
the correction is recorded rather than just applied: `4ca30d044` wrote "160" when
`MIRRORS` held 163; `d88e20f55` (objectui#7432) took the registry to 154 without
touching the sentence; this branch then added its 1 to the stale baseline.

Two consequences beyond the numbers:

  - The running chain of per-card deltas under the population bullet (141 -> 142
    -> 143 -> 144 -> 147) was arithmetic on the wrong base and is not
    re-derivable from this file. It is replaced by the measurement plus a note
    saying so, with an explicit instruction to subtract the union from the
    registry count rather than restart the chain.
  - `RuntimeOnlyDeclared`'s docblock restated the same debt as "97"; it was stale
    before this branch too (the ledger held 96) and now reads 94, with the
    equation balanced the way the other ledger states it.

Also in this commit, from the same review, both optional and both measured:

  - The accepted-table comment in `drill-down-config-mirror-7352.test.ts` said a
    string `name` satisfies the reference arm "whatever else it carries". That
    overstates it: the acceptance is shape-specific, and — measured —
    `{ name, columns: [] }` PARSES TO `{ name }`, because the reference arm is a
    plain object that strips what it does not declare, while the inline arm keeps
    extras through `.catchall`. The comment now says both halves.
  - The changeset gains that parsed-output difference, since consumers read
    `report` after parsing.

Ratchet unaffected: no MIRRORS entry, ledger row or type moved in this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E

---------

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