You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 5b5a5c3
Browse filesBrowse the repository at this point in the historyBrowse files
feat(types)!: mirror eight ObjectViewSchema keys (three by spec reference) and retire viewTabBar (objectui#7779) (#7922)
`ObjectViewSchema`'s TypeScript face declared ten keys its Zod mirror never
did (objectui#7279's `UnmirroredDeclared` reading), so a document authoring
any of them passed the validator unexamined through `BaseSchema`'s
passthrough. Maintainer ruling B (2026-09-06): liveness first, then
mirror-or-retire per key.
- objectql.zod.ts: `navigation` / `searchableFields` / `filterableFields` are
`SpecListViewSchema.shape.*` by reference (identity-pinned);
`allowCreateView` / `viewActions` are the sibling `ViewSwitcherSchema`
slots by reference (the renderer forwards both verbatim into the
`view-switcher` node); `defaultViewType` (the declaration's seven-value
union) / `defaultListView` / `showViewSwitcher` are local literals after a
reader census on `plugin-view/src/ObjectView.tsx`; `viewTabBar` is a
`retirementTombstone()` (zero reads — the tab-bar config is `ViewTabBar`'s
`config` prop from the host, never a node key)
- objectql.ts: `viewTabBar?: never` with the RETIRED docblock (the #7322 shape)
- `listViews` stays unmirrored on the ruling's own fallback clause, with the
measurement pinned against the spec: the declaration's `NamedListView`
(about 52 members, seven read) and the spec's strict `ObjectListViewSchema`
(requires `columns`, refuses `options`, tuple filters and `default`) are
incompatible, and the spec value refuses the named views the docs teach
- zod-mirror-parity: the entry shrinks to `listViews`; `ObjectViewSchema`
joins `SPEC_DERIVED_PAIRS` by a real code reference; header figures
14 / 96 -> 14 / 87, split 1 / 2 + 13 / 94 -> 2 / 3 + 12 / 84, with the
history sentences
- object-view-spec-parity: `TS_ONLY_BACKLOG` shrinks to `listViews` +
`onNavigate`; the audit figures move 11 / 13 -> 20 / 22 with the arithmetic
- new pin object-view-unmirrored-keys-7779: reads derived off disk with
controls, membership, spec/sibling identity, accept-and-survive plus
wrong-typed refusal at the key per mirrored key, the tombstone on both
faces, the `listViews` measurement, neighbour and passthrough controls,
the two doc tables
- docs: the plugin-view README and plugin-view.mdx import tables no longer
teach `viewTabBar` as a node key
- changeset: `@object-ui/types` minor, "Breaking for authored metadata"
Claude-Session: https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s
Co-authored-by: Claude <noreply@anthropic.com>
**Breaking for authored metadata:**`ObjectViewSchema.viewTabBar` is RETIRED
6
+
(objectui#7779, maintainer ruling B of 2026-09-06; ADR-0049 enforce-or-remove).
7
+
An `object-view` node that authors `viewTabBar` no longer validates: the parse
8
+
fails loudly on the `viewTabBar` path with the explanation in the message, and
9
+
the TS member is a `?: never` tombstone so the same document is refused at
10
+
compile time. Nothing ever read the key off the node — the tab-bar UX config
11
+
(`ViewTabBarConfig`, still exported) is the `config` prop of the `ViewTabBar`
12
+
component, composed by the host, not authored metadata. Remove the key.
13
+
14
+
In the same change, eight `ObjectViewSchema` keys the TypeScript interface
15
+
declared and the Zod mirror never did are now ENFORCED. Until now a document
16
+
authoring any of them passed the validator unexamined through `BaseSchema`'s
17
+
passthrough while the published type invited the author to write it — declared
18
+
but not enforced (objectui#7279's `UnmirroredDeclared` reading). Each key now
19
+
admits exactly what the declaration promises and refuses a wrong-typed value at
20
+
its own path; a correctly typed document is untouched.
21
+
22
+
| key | disposition | how |
23
+
| --- | --- | --- |
24
+
|`navigation`| mirrored | the spec's `ListViewSchema.navigation` slot by reference (`NavigationConfigSchema`, optional) |
25
+
|`searchableFields`| mirrored | the spec's `ListViewSchema.searchableFields` slot by reference (`string[]`, optional) |
26
+
|`filterableFields`| mirrored | the spec's `ListViewSchema.filterableFields` slot by reference (`string[]`, optional; the spec marks it a legacy shorthand for `userFilters.fields`) |
27
+
|`allowCreateView`| mirrored | the sibling `ViewSwitcherSchema.allowCreateView` slot by reference — the renderer forwards the value verbatim into the `view-switcher` node it composes |
28
+
|`viewActions`| mirrored | the sibling `ViewSwitcherSchema.viewActions` slot by reference, for the same reason |
29
+
|`defaultViewType`| mirrored | local literal: the declaration's seven-value union (`grid`, `kanban`, `gallery`, `calendar`, `timeline`, `gantt`, `map`) — read as `schema.defaultViewType \|\| 'grid'`|
30
+
|`defaultListView`| mirrored | local literal: `string` — read as `namedListViews?.[schema.defaultListView]`|
31
+
|`showViewSwitcher`| mirrored | local literal: `boolean` — read as `schema.showViewSwitcher === true`|
32
+
|`viewTabBar`| RETIRED |`?: never` + `retirementTombstone()` — zero reads on the node |
33
+
|`listViews`| unchanged (still unmirrored) | see below |
34
+
35
+
**What was measured.** Every reading was taken on the `object-view` node
36
+
renderer (`packages/plugin-view/src/ObjectView.tsx`, registered by
37
+
`plugin-view/src/index.tsx`) with `schema.objectName` / `schema.layout` as the
38
+
positive controls of the same `schema.KEY` query, so each zero is a reading;
39
+
the repo-wide census of `viewTabBar` finds the key in no source file outside
40
+
`@object-ui/types` (two doc tables listed it as authorable and are corrected
41
+
here). The spec side was read through the installed pin
|`ViewTabBarConfig`|the `config` prop of `ViewTabBar`— tab-bar UX (inline add, overflow, indicators), composed by the host; not an `object-view` node key|
|`ViewTabBarConfig`|the `config` prop of `ViewTabBar`— tab-bar UX (inline add, overflow, indicators), composed by the host; not an `object-view` node key|
description: {schema: SpecListViewSchema,key: 'description',note: 'same name, same i18n type difference'},
269
273
layout: {schema: SpecNavigationConfigSchema,key: 'mode',note: 'spec is a superset; fold into `navigation`, do not keep a parallel three-value enum'},
270
274
defaultViewType: {schema: SpecListViewSchema,key: 'type',note: 'spec is a superset (adds chart, tree); ListViewSchema already imports this enum by reference'},
271
-
navigation: {schema: SpecListViewSchema,key: 'navigation',note: 'objectui ViewNavigationConfig is a hand-copied duplicate — re-export by reference'},
272
-
searchableFields: {schema: SpecListViewSchema,key: 'searchableFields',note: 'identical string[] — re-export by reference'},
273
-
filterableFields: {schema: SpecListViewSchema,key: 'filterableFields',note: 'identical; inherits the spec\'s "legacy shorthand for userFilters.fields" deprecation'},
275
+
navigation: {schema: SpecListViewSchema,key: 'navigation',note: 'DONE (objectui#7779): the zod mirror is this very slot by reference (`SpecListViewSchema.shape.navigation`); the TS face is the spec\'s `NavigationConfig` (objectui#4588)'},
276
+
searchableFields: {schema: SpecListViewSchema,key: 'searchableFields',note: 'DONE (objectui#7779): the zod mirror is this slot by reference'},
277
+
filterableFields: {schema: SpecListViewSchema,key: 'filterableFields',note: 'DONE (objectui#7779): the zod mirror is this slot by reference, and so inherits the spec\'s "legacy shorthand for userFilters.fields" deprecation'},
274
278
showSearch: {schema: SpecUserActionsConfigSchema,key: 'search',note: 'scope A step 3, same fold'},
275
279
showFilters: {schema: SpecUserActionsConfigSchema,key: 'filter',note: 'scope A step 3, same fold'},
276
280
showSort: {schema: SpecUserActionsConfigSchema,key: 'sort',note: 'scope A step 3, same fold'},
@@ -304,7 +308,12 @@ const SANCTIONED_LOCAL = new Set<string>([
304
308
'operations',
305
309
// View-management chrome. No spec counterpart —
306
310
// `UserActionsConfigSchema.buttons` is a string[] of action ids, a different
307
-
// shape.
311
+
// shape. `allowCreateView` / `viewActions` are READ (forwarded verbatim into
312
+
// the `view-switcher` node) and mirrored by reference to that sibling's slots
313
+
// (objectui#7779); `viewTabBar` is RETIRED by the same card — a `?: never` /
314
+
// `retirementTombstone()` twin, still DECLARED on both faces so an authored
315
+
// value is refused by name, which is why it stays in this set: the spec models
316
+
// no such key, and the tombstone is objectui-only surface.
0 commit comments