发现于 #5013 的实施(plugin-grid README 标识符漂移)。Filed unassigned, not claiming. 观察类:今天没有用户会撞到,记录的是声明面与运行时读取面的分叉 。
事实 ObjectGridSchema.columns 的声明类型是 string[] | ListColumn[](packages/types/src/objectql.ts),而 ListColumnSchema(@objectstack/spec/ui)是 strict Zod 对象:field 必填,accessorKey / header 不在其中,会被拒绝 而不是忽略。
运行时另有一条支路接受那个未声明的拼写 —— packages/plugin-grid/src/ObjectGrid.tsx:1361-1379:
// Check if columns are already in data-table format (have 'accessorKey')
// vs ListColumn format (have 'field')
if ('accessorKey' in firstCol) {
…
const syntheticCol: ListColumn = { field: col.accessorKey, label: col.header, type: col.type };
于是同一个键有两种拼法:声明的一种(field / label),和只在运行时成立的一种(accessorKey / header)。accessorKey 是 @object-ui/components data-table(TanStack)的内部列键,不是 ObjectStack 的元数据身份。
为什么现在记下来 它正是 plugin-grid README 教 gridComponents 手动注册与 GridSchema / GridColumn 类型 —— 前两者不存在,GridSchema 这个名字在 types 里是 CSS Grid #5013 那处文档漂移看起来可信的原因。 README 的虚构 interface GridColumn { header; accessorKey; … } 形状照抄进 object-grid 的 columns 里确实会渲染 ,所以没有任何信号说它错了 —— 声明面拒绝、运行时接受,读者只能看到后者。[fields] grid columns have two incompatible key spellings: declared type says name, GridField reads field — spec-compliant grid metadata renders empty cells #3951 是同族且已按另一方向结案 :fields 的 grid 列曾有 name(声明)/ field(运行时读取)两种拼写,处置是在生产端统一为一种、不留容忍别名 (AGENTS.md #0.1)。本处是同一形状,尚未处置。既有门禁看不见它。 packages/core/src/utils/__tests__/column-identity.ratchet.test.ts(列身份双读家族:field ?? name 两种优先序并存于 15+ 处——ingestion 归一 + 单键消费 + 禁新增闸门(objectstack#4115) #3104 )专门盯列身份的双读,但它的检测器要求同一行 上出现两个以上身份键的 || / ?? 交替链;这里是 if ('accessorKey' in firstCol) 的分支 + 合成,结构上不进它的扫描面。accessorKey 在那份清单里还被显式列为 COMPANION_KEYS(「单独出现不构成本族」)。出处 这条支路本身早于 #902 ;#902 (已关闭,「accessorKey-format columns bypass type inference」)是在已有的容忍 之上补了类型推断,让这条支路也能渲染徽章/日期。所以本卡问的不是「#902 的修复对不对」,而是:这个未声明的拼写应当被声明,还是应当像 #3951 那样退役?
两条路各自的代价值得一并定级:
复核方式 sed -n '1357,1380p' packages/plugin-grid/src/ObjectGrid.tsx # 容忍支路
grep -rn "field: z.ZodString" node_modules/@objectstack/spec/dist/view.zod-*.d.ts | head # ListColumnSchema,strict
grep -n "COMPANION_KEYS" packages/core/src/utils/__tests__/column-identity.ratchet.test.ts
分级 finding,不入 pm:queue。要不要动、往哪边动,取决于维护者对上面那个二选一的裁定;本卡只把分叉与门禁盲区记在案。
发现于 #5013 的实施(plugin-grid README 标识符漂移)。Filed unassigned, not claiming. 观察类:今天没有用户会撞到,记录的是声明面与运行时读取面的分叉。
事实
ObjectGridSchema.columns的声明类型是string[] | ListColumn[](packages/types/src/objectql.ts),而ListColumnSchema(@objectstack/spec/ui)是 strict Zod 对象:field必填,accessorKey/header不在其中,会被拒绝而不是忽略。运行时另有一条支路接受那个未声明的拼写 ——
packages/plugin-grid/src/ObjectGrid.tsx:1361-1379:于是同一个键有两种拼法:声明的一种(
field/label),和只在运行时成立的一种(accessorKey/header)。accessorKey是@object-ui/componentsdata-table(TanStack)的内部列键,不是 ObjectStack 的元数据身份。为什么现在记下来
gridComponents手动注册与GridSchema/GridColumn类型 —— 前两者不存在,GridSchema这个名字在 types 里是 CSS Grid #5013 那处文档漂移看起来可信的原因。 README 的虚构interface GridColumn { header; accessorKey; … }形状照抄进object-grid的columns里确实会渲染,所以没有任何信号说它错了 —— 声明面拒绝、运行时接受,读者只能看到后者。name, GridField readsfield— spec-compliant grid metadata renders empty cells #3951 是同族且已按另一方向结案:fields的 grid 列曾有name(声明)/field(运行时读取)两种拼写,处置是在生产端统一为一种、不留容忍别名(AGENTS.md #0.1)。本处是同一形状,尚未处置。packages/core/src/utils/__tests__/column-identity.ratchet.test.ts(列身份双读家族:field ?? name 两种优先序并存于 15+ 处——ingestion 归一 + 单键消费 + 禁新增闸门(objectstack#4115) #3104)专门盯列身份的双读,但它的检测器要求同一行上出现两个以上身份键的||/??交替链;这里是if ('accessorKey' in firstCol)的分支 + 合成,结构上不进它的扫描面。accessorKey在那份清单里还被显式列为COMPANION_KEYS(「单独出现不构成本族」)。出处
这条支路本身早于 #902;#902(已关闭,「accessorKey-format columns bypass type inference」)是在已有的容忍之上补了类型推断,让这条支路也能渲染徽章/日期。所以本卡问的不是「#902 的修复对不对」,而是:这个未声明的拼写应当被声明,还是应当像 #3951 那样退役?
两条路各自的代价值得一并定级:
field):契约收敛为一种拼法,但会改变今天能渲染的输入 —— 需要先量 in-the-wild 用量(仓内accessorKey的命中全在@object-ui/components的 data-table 及其测试,那是另一个组件的内部形状,不是经ObjectGridSchema授权的用法)。accessorKey加进 spec):把 TanStack 的内部键提升为 ObjectStack 的授权面,与 [fields] grid columns have two incompatible key spellings: declared type saysname, GridField readsfield— spec-compliant grid metadata renders empty cells #3951 的裁定方向相反。复核方式
分级
finding,不入pm:queue。要不要动、往哪边动,取决于维护者对上面那个二选一的裁定;本卡只把分叉与门禁盲区记在案。