feat(spec)!: RecordDetailsProps.sections 改为真实的对象形式,并补声明 hideFields (#5611) - #5734
Conversation
…orm + declare hideFields (#5611) `record:details` declared a `sections` shape nothing produced and nothing consumed, and omitted a key a published platform page depends on. - `sections`: `z.array(z.string())` (section IDs) -> an object array `{ name?, label?, columns?, fields }`. Measured: zero ID-list producers and zero ID-list read paths. objectui's RecordDetailsRenderer maps every entry as an object with no string branch, `@object-ui/types` already mirrored the object form, the Studio block designer can only author it, and all four real pages (3 showcase + sys_user) author it. Per the maintainer ruling on #5611: no union with string, no ADR-0087 conversion layer. - `hideFields`: declared `z.array(z.string()).optional()`. Authored by sys-user.page.ts and read by the renderer; undeclared, so a non-strict z.object silently stripped it. - `name` is declared because `packages/lint`'s translation-section-name-missing rule tells authors to add it — a key one rule demands must not be a key the schema rejects. - Section `columns` uses an int range, not a literal union: same accepted set, but the docs generator renders numeric literals as quoted strings (filed as a separate finding), which would misdocument the key. Aligns the two now-stale `packages/lint` comments that described these keys as undeclared. Regenerated references/ui/component.mdx, authorable-surface.json and the strictness-ledger counts (component.zod.ts gains one nested object site). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
…ord-details-sections
合并 origin/main 后按 os-regen 纪律整体重生成(生成物不做文本合并): - counts.md: 456 sites = 本分支 component.zod.ts 的 30 + main 新增 action-params.zod.ts 的 1。文本合并给不出这个联合结果,故由 gen:strictness-ledger 重算。 - authorable-surface.json: 重出后同时含本分支的 ui/RecordDetailsProps:hideFields 与 main 的 ui/ActionSession:* 三键。 - authorable-surface.base.json 的重锚(baseRev 前移 + api/Discovery:scoping) 与本 PR 无关,已剔除。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
📓 Docs Drift CheckThis PR changes 2 package(s): 109 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
合并 main 冲突消解(os-regen 四步)推送后 #5721(#5697)与 #5722(#5605)合入 main,
正文"验证"表格记的是合并前的数字,以本条为准。 Generated by Claude Code |
Fixes #5611
按维护者 2026-08-06 的裁定实施 A 收紧变体:
sections直改对象形式数组(不做 union、不做 ADR-0087 转换层),hideFields补声明。派发前置:ID-list 形式的读取点测量(结论:零)
开工第一步按要求在 objectui(只读)测量 ID-list 形式有无渲染器读取点。结论是零,四个独立证据互相印证:
plugin-detail/src/renderers/record-details.tsx:189-217schema.sections.map((s) =...)全程按对象读(s.name/s.label/s.title/s.fields),全仓无任何typeof s === 'string'分支。字符串条目会被...s展开成字符序列,渲染出的是垃圾@object-ui/typesRecordDetailsComponentProps.sectionsname?/label?/fields/collapsible?/collapsed?)app-shell/.../previews/block-config.ts:310-320record:details的sections是kind: 'array',itemFields恰为[label, columns, fields]—— 设计器根本产不出 ID 列表filteredSections一路走到DetailView)所以"破坏性"只存在于照着一份从无人兑现的声明写出的假想存量数据;且 schema 校验只作用于 publish 路径,不改写已存数据。测量为零 → 按裁定继续,未触发"停手回报"条件。
先证红(改动前实测,方向事先声明)
改之前先跑探针,预先声明两个键的失败方向应当不同,三条全部命中:
sectionssuccess: false,code: 'invalid_type',path: ['sections', 0]hideFieldssuccess: true且'hideFields' in data === falsesections(对照)success: true两个键的失败模式不同(一个响一个哑),这正是 issue 正文预判的;探针文件已删除,其断言以正式测试形式保留在
component.test.ts。分区键集:按证据测量,不按想象
{ name?, label?, columns?, fields },每个键都能指到兑现方:fields(必填) —— 4 个真实页面的全部 5 个 section 都有;objectui 类型镜像与DetailViewSection均为必填;lint 的nestedSections就走sections[].fields[]。label(可选) —— 5/5 都授权;渲染器对无标题分区有明确行为(无边框),故可选而非必填。类型取I18nLabelSchema,与本文件其余每一个 label/title 键保持一致(详见下方"刻意未做")。columns(可选) —— 3 个 showcase 页授权(2/1),平台页不授权,故可选且不给 default(给了会把渲染器的自动推导覆盖掉)。name(可选) —— 这条是测量出来的关键:仓内packages/lint/src/validate-translatable-sections.ts明确遍历record:details分区并报translation-section-name-missing,主动提示作者补name;i18n-extract.ts也按name出 key。一个规则要求作者写的键,不能是 schema 拒绝的键 —— 漏掉它就会在反方向上重演本 issue。消费半径
packages/lint的nestedSections模型本来就是按对象形式建的,无需改动;其测试夹具也早就是对象形式,全绿。只更新了两处现已过时的注释(源码 + 测试),它们还写着"这两个键不在 RecordDetailsProps 里"。packages/cli的 i18n 夹具(含一处写title、一处无fields的 section)不经过本 schema,已实测全绿。component.zod.ts顶部关于 SDUI 组件 props 没有解析闸门:PageComponent.properties是开放 record,ComponentPropsMap的 29 个站点从不被 parse(#4001 批 17 的 no gate 判定) #5068 的说明里,把record:details从"未声明形状"清单中移除(记录该分歧已关闭)。刻意未做(留给对应 issue)
title别名:仓内 5/5 真实分区写label,零处写title;objectui 类型镜像与设计器也只有label。三个消费方的title ?? label属消费方容忍,按 Prime Directive Add comprehensive test suite for Zod schema validation #12 不写进契约固化成第二套正名 —— 已另开 finding record:details 分区标题存在 title/label 双拼写:三个消费方都读title ?? label,但 spec 只声明 label #5730。sys-user.page.ts有 2 处 section label 写成{ en, zh-CN, ... },但这是先于本 PR 就存在的系统性问题 ——record:related_list.title/page:tabs的label早已声明为I18nLabelSchema(纯字符串),而 3 个平台页共 31 处这么写。本 PR 让sections[].label与同文件其余键保持一致,而不是为单个键分叉 label 契约;该问题已另开 component.zod.ts 的 I18nLabelSchema 键声明为纯字符串,但 3 个平台页在 31 处授权内联多语言 map #5728(需裁定,SDUI 组件 props 没有解析闸门:PageComponent.properties是开放 record,ComponentPropsMap的 29 个站点从不被 parse(#4001 批 17 的 no gate 判定) #5068 前置)。PageComponent.properties是开放 record,ComponentPropsMap的 29 个站点从不被 parse(#4001 批 17 的 no gate 判定) #5068);未动 conversions/*、data/hook.zod.ts、api routes/rest、kernel contracts。生成物(逐行可解释)
references/ui/component.mdx:3 行 ——sections类型 + 描述改写、新增hideFields行。authorable-surface.json:1 行 ——ui/RecordDetailsProps:hideFields。component.zod.ts的对象站点 29 → 30(新增的内嵌 section 对象),沿各级汇总一致传播。no gate判定本身未变,故手写的 ledger 正文未动。authorable-surface.base.json被 spec 构建顺带重锚(baseRev 前移 + 带进他人已合并的api/Discovery:scoping两键),已剔除,不随本 PR 提交。验证
pnpm --filter @objectstack/spec testpnpm --filter @objectstack/lint testpnpm --filter @objectstack/cli testpnpm --filter @objectstack/platform-objects testtypecheck(spec / lint / platform-objects)check:generatedcheck-nul-bytescomponent.test.ts:152原有夹具sections: ['sec-1','sec-2']钉的正是被删掉的那条形状,按夹具三分法作整体替换 —— 换成从真实页面逐字取来的对象形式夹具,并补一条"ID-list 现在被拒绝"的反向钉。Generated by Claude Code