Skip to content

Decision: what value type should ObjectViewSchema.listViews's mirror enforce — the spec record by reference, the local NamedListView, its seven read members with the rest retired, or stay ledgered? #7928

Description

@os-justin

Filed by the domain:spec @ objectui execution seat (session session_01BAZFhALsQsGqxui8sNqM8s) as the carrier for the one key ruling B could not disposition. ⛔ domain:* and grading are triage's.

Figures corrected 2026-09-06T04:0xZ — the counts first published here were wrong and are restated below from the measurement that re-derived them (see the correction note at the end). Anything quoting this card should quote the current numbers: 47 declared members, 41 unread, six read declared members (not "about 52 / ~45 / seven").

Why this card exists at all. PR #7922 (objectui#7779) dispositions nine of the ten unmirrored ObjectViewSchema keys and carries Fixes #7779, so that card closes. listViews stays in the UnmirroredDeclared ledger under the ruling's own fallback clause — measured, not deferred by preference. Without this card the remedy would be referenced in a closed thread and owned by nobody, which is the exact failure this lane已 paid for twice: objectui#5741's body records how the row-predicate spelling question sat as "#4075 step 3, parked in the decision inbox" for months while no card ever carried it. So it gets a carrier the moment it becomes homeless, not later.

What is measured

The two candidate value types disagree, and each loses something real:

  • NamedListView declares 47 top-level members. The renderer reads seven keys off a named view — label, type, columns, filter, sort, options, data — but only six of those are declared members: data is not declared on NamedListView at all and reaches the renderer through an as any cast (packages/plugin-view/src/ObjectView.tsx:1815, (currentNamedViewConfig as any)?.data). So 41 declared members are unread (47 − 6), and one read key is undeclared — the mirror image of this card's own defect.
  • The spec value (ViewSchema.listViewsObjectListViewSchema by identity, strict, columns its only required key) accepts the schema-catalog's { label, columns } but refuses: the README / plugin-view.mdx form { label: 'All Users' } (at columns); a type + filter view with no columns (at columns); content/docs/api/schema-reference.md's ObjectQL tuple filter [["owner", "=", "…"]] (at filter.0) and its default: true (unrecognized key); options (unrecognized key); a partial kanban: { groupByField } (at kanban.columns).
  • The writers disagree too: the README and the mdx author the local shape, schema-reference.md authors tuple filters, and app-shell's ViewPreview.tsx:113 spreads a spec-shaped view body into listViews.

So mirroring the spec value by reference refuses documented, taught forms; mirroring NamedListView key-for-key enforces 41 members nothing reads — the "enforced dead key" shape ruling B refused for the six local keys.

Options

option what it costs
A mirror the spec record by reference (ObjectListViewSchema) refuses the README / mdx / schema-reference forms above; the north-star shape, but the renderer does not honour a spec-shaped named view today, so it declares something that does not run. Breaks the docs; leaves app-shell's ViewPreview (which already writes the spec shape) alone
B mirror the local NamedListView key-for-key (47 members) every documented example stays green; fossilizes 41 unread members as contract — the shape ruling B rejected for the six. Breaks ViewPreview.tsx
C (dev's recommendation) mirror the six read declared members and retire the 41 unread ones (?: never + retirementTombstone(), the #7129 route), staged after #7924's per-member census; the seventh read key, data, has to be declared or its cast removed as part of it shrinks the surface to what runs; costs the census card first, and retiring published members is "Breaking for authored metadata". Also breaks ViewPreview.tsx. ⚠️ Note this is a local shape — choosing it decides that for this one key the local vocabulary is the contract and the spec slot is not, which differs from what was already ruled for navigation / searchableFields / filterableFields
D stay ledgered (what main carries after PR #7922) one key declared-not-enforced, pinned against the spec so a spec relaxation or a renderer change re-takes the measurement; honest interim, zero cost to keep

决策四维分析

  • 实际业务需求:实测只有六个已声明成员被渲染器读取(第七个 data 根本没声明,靠 as any 进去),写入方却分三派(README / mdx 写本地形、schema-reference.md 写元组过滤器、app-shell 的 ViewPreview 塞规范形)。今天没有任何一种写法能被完整兑现——A 会拒掉文档正在教的形状,B 把 41 个没人读的成员变成契约,C 把契约缩到真正跑得通的那六个并顺手处理掉 data 这个「读了但没声明」的洞。
  • 项目长远合理性:contract-first 要求镜像强制的是运行时兑现的东西。A 是北极星形状,但它要等渲染器先 honour 规范形命名视图,那是一张实现卡,⛔ 不能假设;B 把遗留词表固化;C 与本仓 [Decision] hideEmpty on record:details sections: the spec REFUSES the key, @object-ui/types declares it, the zod mirror omits it, and the renderer honours it — plus hideEmpty: false is not an override #7129 的退役路线一致,但它是本地形,与本卡另外三个键已裁的「按规范槽引用」方向不同,需要明知而选。
  • 防 AI 写元数据犯错:现状(D)之下 AI 写任何 listViews 内容都静默通过;A 会让 AI 写出的规范形视图校验通过却只渲染一部分——比拒绝更坏;B 让 41 个死键继续绿灯;只有 C 做到「错值在键上被拒、死成员按名被拒」,并让 data 要么被声明要么被去掉。
  • 创业阶段不扩散需求:C 缩面,B 扩面,A 需要配套的渲染器实现才成立。D 是零成本的诚实中间态,可以一直挂着直到裁决。

Seat recommendation: C, staged as #7924 (the per-member census, which must also decide data) → then the mirror. D is the status quo and costs nothing to keep while the census runs, so this card is not urgent — but it must not be uncarded.

⚠️ Note for whoever executes: option A only becomes correct together with a renderer change that honours a spec-shaped named view; ⛔ do not ship A as a type-only edit on the assumption that the renderer will follow.


Correction note (2026-09-06T04:0xZ). This card first said "about 52" members and "~45 unread", and cited the block as objectql.ts:1957-2134. All three were wrong or stale: a contract-review pass counted 47 top-level members with the pin's own regex at three refs (merge-base, old tip, new head — 47 at each; the looser regex that counts nested object-literal lines gives 59, which is where "about 52" came from), and the patch-round dev then found that 41, not 40 or 45, is the unread count, because data is read but was never a declared member. The line range moved 26 lines with an origin/main merge and is dropped rather than re-quoted — a member count and the interface name are the durable citation; a line range in a long-lived body goes stale by construction. The same wrong figures are in PR #7922's body and in three source comments on its branch; that PR is in the merge queue and cannot be updated, so the correction lands as a follow-up PR (recorded on objectui#7779).

Refs: objectui#7779 (ruling B, the nine dispositioned keys) · PR #7922 · #7924 (the NamedListView liveness census that option C needs, and where data's declared-or-cast question belongs) · #7923 (the schema-reference.md docs defect measured alongside) · #7129 / #7623 (the retirement route) · #6152 (the parity worklist).

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

    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneenhancementNew feature or requestneeds-user-decisionpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions