Filed by the domain:spec @ objectui execution seat (session session_0114Ytxr5sM1vdW19Y9WAx6E) from a dev's out-of-scope findings on the #7122 chain. ⛔ Unassigned; domain:* and grading are triage's. Measured on /home/user/objectui-issue-7122 at a31c2e511 against the installed @objectstack/spec@17.3.0.
The defect
@objectstack/spec 17.3.0 added page to the list-view type union. packages/types/src/views.ts:27 declares a hand-written local ViewType:
'list' | 'detail' | 'grid' | 'kanban' | 'calendar' | 'timeline' | 'map' | 'gallery' | 'gantt' | 'chart' | 'tree'
It has not gained page. Six maps are total over that union, so each compiles green while being exactly as incomplete as the doc snippet that exposed this:
| site |
map |
packages/core/src/utils/normalize-list-view.ts:145 |
LIST_VIEW_KINDS |
packages/plugin-list/src/ViewSwitcher.tsx:65 · :92 |
VIEW_ICONS, VIEW_LABEL_KEYS |
packages/plugin-view/src/ViewSwitcher.tsx:58 · :85 |
DEFAULT_VIEW_LABELS, DEFAULT_VIEW_ICONS |
packages/plugin-view/src/ObjectView.tsx:1147 |
iconMap |
⭐ Why this is a bug and not a tidy-up: one of them has a runtime consequence
LIST_VIEW_KINDS's own doc comment promises:
a kind added to the union fails the build HERE instead of silently staying unreadable authored input
That promise is now false. The map is total over the stale local union, so nothing fails the build. A spec-valid type: 'page' view is left unresolved by readListViewKind and falls back to the caller's grid default.
⇒ A published page mounted as a view renders as a grid, silently. No error, no warning, no console line — the author sees a grid where they authored a page. That is the failure class this repo's contract-first rule exists to prevent, and the guard that was supposed to catch it is the thing that broke.
How it surfaced — worth recording, because the instrument matters
It did not surface from any of the six sites. It surfaced from a documentation snippet: packages/plugin-list/README.md (added to main by e23cfb000 / #8110) builds a Record keyed on the spec's union rather than the local one, so Doc Snippet Type Check reported TS2741: Property 'page' is missing the moment a branch pinned spec at 17.3.0.
⇒ The one map keyed on the real contract found the drift; the six keyed on a hand-copied union could not. ⭐ That is the generalisable point: a total map is only as honest as the union it is total over, and a locally re-declared union silently converts an exhaustiveness guarantee into a no-op.
Also drifted — same root, no type-checking at all
These are plain arrays or separate declarations, so they were never even compared against the union:
packages/plugin-list/src/index.tsx:45 and :86 — the SDUI registry viewType enum (nine entries)
packages/app-shell/src/views/CreateViewDialog.tsx:88–96 — the create-view picker (nine entries), with its i18n strings at packages/i18n/src/locales/en.ts:2153–2170 (viewTypeGrid…viewTypeTree plus their *Desc siblings — no page entry in any of the ten locale packs)
packages/plugin-list/src/ListView.tsx:2224 — a hardcoded allowlist
packages/types/src/designer.ts:416 — UnifiedViewType
packages/types/src/zod/views.zod.ts:26 — ViewTypeSchema
Suggested direction — offered, ⛔ not a ruling
The fix is not "add page in twelve places". It is to stop re-declaring the union: derive ViewType from the spec's own member list so the six total maps become genuinely total again and the next spec addition fails the build where LIST_VIEW_KINDS promises it will. Adding page by hand restores today's correctness and leaves the mechanism that produced it intact.
⚠️ The i18n half is real work regardless — ten locale packs, a label and a description each.
Related
#7122 / PR #7685 (the 17.3.0 pin that exposed it) · #8110 (added the README map on main) · the same "17.3.0 gained a member, a total structure is short by one" shape already handled this round in the capability set and in ObjectFieldGroup.visibleWhen — this is the third instance, and the first with a runtime consequence.
Filed by the
domain:spec@ objectui execution seat (sessionsession_0114Ytxr5sM1vdW19Y9WAx6E) from a dev's out-of-scope findings on the #7122 chain. ⛔ Unassigned;domain:*and grading are triage's. Measured on/home/user/objectui-issue-7122ata31c2e511against the installed@objectstack/spec@17.3.0.The defect
@objectstack/spec17.3.0 addedpageto the list-view type union.packages/types/src/views.ts:27declares a hand-written localViewType:It has not gained
page. Six maps are total over that union, so each compiles green while being exactly as incomplete as the doc snippet that exposed this:packages/core/src/utils/normalize-list-view.ts:145LIST_VIEW_KINDSpackages/plugin-list/src/ViewSwitcher.tsx:65·:92VIEW_ICONS,VIEW_LABEL_KEYSpackages/plugin-view/src/ViewSwitcher.tsx:58·:85DEFAULT_VIEW_LABELS,DEFAULT_VIEW_ICONSpackages/plugin-view/src/ObjectView.tsx:1147iconMap⭐ Why this is a bug and not a tidy-up: one of them has a runtime consequence
LIST_VIEW_KINDS's own doc comment promises:That promise is now false. The map is total over the stale local union, so nothing fails the build. A spec-valid
type: 'page'view is left unresolved byreadListViewKindand falls back to the caller'sgriddefault.⇒ A published page mounted as a view renders as a grid, silently. No error, no warning, no console line — the author sees a grid where they authored a page. That is the failure class this repo's contract-first rule exists to prevent, and the guard that was supposed to catch it is the thing that broke.
How it surfaced — worth recording, because the instrument matters
It did not surface from any of the six sites. It surfaced from a documentation snippet:
packages/plugin-list/README.md(added tomainbye23cfb000/ #8110) builds aRecordkeyed on the spec's union rather than the local one, soDoc Snippet Type CheckreportedTS2741: Property 'page' is missingthe moment a branch pinned spec at 17.3.0.⇒ The one map keyed on the real contract found the drift; the six keyed on a hand-copied union could not. ⭐ That is the generalisable point: a total map is only as honest as the union it is total over, and a locally re-declared union silently converts an exhaustiveness guarantee into a no-op.
Also drifted — same root, no type-checking at all
These are plain arrays or separate declarations, so they were never even compared against the union:
packages/plugin-list/src/index.tsx:45and:86— the SDUI registryviewTypeenum (nine entries)packages/app-shell/src/views/CreateViewDialog.tsx:88–96— the create-view picker (nine entries), with its i18n strings atpackages/i18n/src/locales/en.ts:2153–2170(viewTypeGrid…viewTypeTreeplus their*Descsiblings — nopageentry in any of the ten locale packs)packages/plugin-list/src/ListView.tsx:2224— a hardcoded allowlistpackages/types/src/designer.ts:416—UnifiedViewTypepackages/types/src/zod/views.zod.ts:26—ViewTypeSchemaSuggested direction — offered, ⛔ not a ruling
The fix is not "add
pagein twelve places". It is to stop re-declaring the union: deriveViewTypefrom the spec's own member list so the six total maps become genuinely total again and the next spec addition fails the build whereLIST_VIEW_KINDSpromises it will. Addingpageby hand restores today's correctness and leaves the mechanism that produced it intact.Related
#7122 / PR #7685 (the 17.3.0 pin that exposed it) · #8110 (added the README map on
main) · the same "17.3.0 gained a member, a total structure is short by one" shape already handled this round in the capability set and inObjectFieldGroup.visibleWhen— this is the third instance, and the first with a runtime consequence.