Skip to content

A spec-valid type: 'page' view silently renders as a grid — ViewType in packages/types/src/views.ts never gained page, so six total maps over it compile green while being incomplete #8127

Description

@os-sam

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 (viewTypeGridviewTypeTree 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:416UnifiedViewType
  • packages/types/src/zod/views.zod.ts:26ViewTypeSchema

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanedomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seati18npm:queuepriority:p2

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions