Repository navigation
SDUI 组件 props 没有解析闸门:PageComponent.properties 是开放 record,ComponentPropsMap 的 29 个站点从不被 parse(#4001 批 17 的 no gate 判定) #5068
Description
Activity
xuyushun441-sys commented
on Aug 4, 2026 CollaboratorAuthorMore actions硬前置登记(2026-08-04)
本单无论最终裁 v17 还是 v18,任何给 SDUI props 加解析闸门的实施 PR 必须先落 #5176(
RecordHighlightsField声明readonly):objectui#3356 已让record:highlights组件消费授权的readonly,该键目前仅因本单记录的「无门」状态而存活 —— 先开闸门后声明会让它被静默剥离,#5077 的数据腐蚀以零诊断形态复发。同理,开闸前应扫一遍 29 个站点上已被组件真实消费、但ComponentPropsMap未声明的键,#5176 只是第一个实锤。
Generated by Claude Code
- added a commit that references this issue
on Aug 4, 2026 维护者裁定(2026-08-05,经 spec 车道 PM
session_018fxLGQdatPbBUvCgiVxg6D誊写):方向 A。Blocked-by: objectstack-ai/objectstack#5176- 在 publish / lint 侧按
type分派ComponentPropsMap解析;未注册 type 跳过(type是开放联合,这是必须语义而非宽松);先 warning 后 error 分级上闸,warning 期消化现存违例(record:details的sections[].fields[]/hideFields[]、record picker 的labelField—— 先补 schema 或先报不阻)。 - B(把
properties改判别式 schema)不做 —— breaking,对开放type兼容代价过大。 - 排序前置:
RecordHighlightsFielddoes not declarereadonly, so the spec silently strips the key the chip gate reads #5176(readonly声明)已同批裁定选 A 并先行派发 —— 闸门接通前先把已知被 renderer 兑现的键声明齐,避免「接闸即剥」重演highlightFieldscannot declare read-only, so the detail page's header chips let users overwrite hook-maintained columns #5077。 - 落地后把 ledger 行从
no gate改回authorable,未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 对component.zod.ts的收紧才有意义(届时排后续批次)。
标记
pm:blocked,#5176 落地后转队派发。
Generated by Claude Code
- 在 publish / lint 侧按
状态变更(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05):pm:blocked→pm:queue。前置 #5176(readonly声明)已随 PR #5607 MERGED。注记两条给实施者:①readonly挂在 fields 数组项 union 对象成员上(第二层),authorable-surface walk 严格一层、不会自动收录——props 解析路径须显式覆盖(#5607 的更正);② error 升级前置 #5611(RecordDetailsProps.sections形状裁定,决策位中)——warning 阶段可先行落地,升级 error 需等其裁定并落地。
Generated by Claude Code
1 remaining item
- added a commit that references this issue
on Aug 6, 2026 error 升级前置登记(PM 会话
session_01LeEfA7CFwbJb7JJmXm2KM3,2026-08-06):warning 期违例清单对应的三张裁决卡已由维护者全部拍板——#5728 → B(I18nLabelSchema 放宽为 union,含element:text.content同批)、#5775 → A(labelField 转正 + displayField/searchFields/multiple 退役 + 被兑现键补声明)、#5777 → A(type:'api'另立载荷键 + conversion)。三单均已回pm:queue。三单落地 + warning 清零后,本单可升 error 并把 ledger 行改回authorable。
Generated by Claude Code
- added a commit that references this issue
on Aug 6, 2026 - added a commit that references this issue
on Aug 17, 2026 - added a commit that references this issue
on Sep 1, 2026 - added a commit that references this issue
on Sep 1, 2026 - added a commit that references this issue
on Sep 28, 2026
背景
#4001 批 17 的任务是把
packages/spec/src/ui/component.zod.ts的 29 个 strip 站点收紧为strictObject。测量结果是:这些站点根本没有 parse,因此.strict()在这里什么都不会强制——按 ledger 的分类规则,这是no gate(carrier 活着、parse 缺席),不是authorable。批 17 因此没有收紧该文件,而是记录判定并把「接上闸门」这件事单独立为本 issue(与 批 15 对
ChartAggregateSchema的处理一致:contract-first 的修法属于packages/lint/ 载体那一侧,不应塞进 spec 的 strictness 批次里)。事实(三条独立测量,控制组同轮为真)
1. carrier 是活的,但它是一个开放 record
packages/spec/src/ui/page.zod.ts:115PageComponentSchema自 ADR-0089 D3a 起是.strict(),但严格性不递归:它守住的是 component 节点自己的键,properties里面是一个未校验的口袋。2. BFS 不可达
从 24 个 metadata-type 根 +
defineStack的ObjectStackSchema出发,复用build-schemas.ts自己的zodChildSchemas/zodShapeOf(#4650 闭包),闭包 6899 个 zod 节点:component.zod.ts的 52 个目标(21 个导出 schema +ComponentPropsMap全部 31 个条目)全部 UNREACHABLE;PageSchema/PageComponentSchema/PageRegionSchema/ThemeSchema/ChartConfigSchema/ResponsiveConfigSchema六个正控制组全部root-graph;TouchInteractionSchema/MotionConfigSchema/OfflineConfigSchema)保持 unreachable。BFS 在
PageComponentSchema.properties处停下——props schema 挂在任何东西上都没有。3. 三个仓库里没有任何生产 parse 点
objectstack/objectui/cloud全仓搜索.parse(/.safeParse(:命中全部落在packages/spec/src/ui/component.test.ts与inline-action.test.ts(该文件自己的单测)内。objectui只引用推断出的类型并手写平行的 React interface;cloud引用数为 0。ui/react-blocks.ts只用Object.keys(ComponentPropsMap)取类型名派生RECORD_CONTEXT_BLOCK_TAGS,不 parse;它的REACT_BLOCKS[].schema指向FormViewSchema/ListViewSchema/ChartConfigSchema,没有一个来自component.zod.ts。经验证据(批 14 方法,负控制组同轮为红)
definePage()就是PageSchema.parse(),所以这是活的授权门。对 example 语料里 21 个真实页面:即:未声明的键写进
properties会原样通过并被保留;同一个键写在外面一层立刻被拒。负控制组证明探针能测出闸门——这里就是没有闸门。为什么这是个真问题(不是「无害」)
objectui的packages/react/src/SchemaRenderer.tsx会把properties整个 hoist 到节点上,然后把除固定 deny-list 外的每一个作者写的键转发成 React prop(约 457 行)。所以一个拼错的键既不会被拒、也不会被丢,而是安静地流到渲染器,由渲染器忽略——ADR-0078 要消灭的正是这种「作者拿到成功回执、配置被无声忽略」的形状。
packages/lint/src/validate-page-field-bindings.ts的文件头早就把这件事写下来了:建议的修法(contract-first,需要维护者定方向)
把 parse 接到载体自己的闸门上,而不是在 spec 里做一个没人调用的
.strict()。两个已知约束必须先解决,这也是为什么不能顺手做:type的取值域是开放的——PageComponentSchema.type是z.union([PageComponentType, z.string()]),现实中存在record:line_items这类未注册类型(validate-page-field-bindings明确说明未知类型静默跳过)。所以按type分派解析必须对未注册类型保持开放,否则会拒掉在用的页面。record:details的sections[].fields[]与hideFields[]、record picker 的labelField,「they pass only becausepropertiesis unvalidated」。今天直接接上严格解析会打红在用的 example / 客户页面,所以需要先补齐 schema 或先以 warning 级别落地。可选方向(择一,建议 A):
type分派ComponentPropsMap解析,未注册类型跳过,先 warning 后 error。好处:与validate-page-field-bindings同层、同 severity 习惯,不动page协议形状;坏处:存储路径(metadata REST 写入)仍然不校验。PageComponentSchema.properties改成按type的判别式 schema。好处:所有路径(defineStack / REST / Studio)一次性覆盖;坏处:改的是page协议本身的形状,对开放type与未声明形状的兼容代价大得多,属于 breaking。任一方向落地之后,
component.zod.ts才重新变成authorable,#4001 的收紧才有意义——届时把 ledger 行从no gate改回authorable并排进后续批次。参考
no door)/ 批 15(no gate,ChartAggregateSchema同形状)docs/audits/2026-07-unknown-key-strictness-ledger.md,ui/两张表的component.zod.ts行