feat(spec): RecordHighlightsField 声明 readonly —— 渲染器已强制的键不再被静默剥离 (#5176) - #5607
Merged
Conversation
`record:highlights` 条目的对象形式新增可选 `readonly: boolean`,用于把高亮
chip 标记为不可行内编辑(hook / 自动化维护的列)。
此前该键未被声明,而对象成员不是 `.strict()`,因此被**静默剥离**:
input { fields: [ { name: 'supply_share', readonly: true, type: 'number' } ] }
parsed { fields: [ { name: 'supply_share', type: 'number' } ] }
今天之所以端到端可用,只是因为逐组件 props 尚未在装载路径上解析
(`PageComponentSchema.properties` 是 `z.record(z.string(), z.unknown())`)。
一旦该闸门接通,授权的 `readonly` 要么静默丢失(机器维护列重新可编辑、
零诊断),要么硬解析失败。声明它使"授权的声明"与"被强制的行为"成为
同一事实 —— 落地即满足 ADR-0049 enforce-or-remove,而非声明即惰性。
纯增量:`readonly` 可选且不物化默认值,未授权该键的条目解析结果与此前
完全一致;bare-string 形式不变。对象级 `highlightFields: string[]` 不在
本次范围内。
不翻 `.strict()` —— 那属于 #5068 的闸门专项。
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
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 109 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Contributor
Author
|
PM 预记( Generated by Claude Code |
This was referenced Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5176
按维护者 2026-08-05 裁定的 Option A 实施,仅 spec 侧。
改了什么
packages/spec/src/ui/component.zod.ts中RecordHighlightsField的对象成员新增一个已声明键:union 形状本身不动(bare-string 成员不变),不翻
.strict()—— 那属于 #5068 的闸门专项,明确不在本单范围。对象级的highlightFields: string[]同样未触碰(见 issue 的范围边界)。同时把两处
.describe()里已经过时的形状说明补齐({name,label?,icon?,type?}→{name,label?,icon?,type?,readonly?}),否则生成的组件参考文档会描述一个与 schema 不符的形状。前提复核(rule 6):issue 前提成立,已实测复现
在
origin/main(e900015cd)上按 issue 所述逐条核对:union 为[string, object]、对象成员非.strict()、readonly缺席且被静默剥离。剥离实测(改动前跑新增的保留测试):即
{ name: 'supply_share', readonly: true, type: 'number' }解析后readonly为undefined—— 与 issue 正文给出的 input/parsed 对照完全一致。先证红 → 后转绿
按"先写测试证红"的要求,先写保留测试并在未修改的 schema 上跑红,再改 schema。事前预判的方向是常规红(删掉
readonly这条 limb,新增的 pin 测试转红),实际与预判一致:改动前(5 个新增用例中 3 红 / 2 绿):
另外 2 个用例(
不物化默认值、bare-string 与其它对象形式仍被接受)是回归护栏,改动前后都应绿,实测也确实都绿 —— 它们不承担证红职责,如实标注。改动后(spec 全量):
其中
should reject a non-boolean readonly这一条值得单说:改动前readonly: 'yes'会被静默剥离从而通过解析,改动后 union 两个成员都失配、解析响亮失败 —— 这正是裁定里"拼写/类型错误在闸门接通后响亮失败"的那一半,现在在 schema 层就已兑现。消费半径清扫
record:highlights的下游读取方是packages/lint的validate-page-field-bindings.ts(COMPONENT_FIELD_SPECS['record:highlights'] = { props: ['fields'] })与validate-react-page-props.ts,两者只从条目中取name;本次是纯增量、未删除任何 limb,因此不存在需要重判的 fixture(fixture 三分类处置在此无适用项)。全仓 grep 也确认没有任何 fixture 拼写过readonly。packages/lint全量:59 passed (59) / 1284 passed | 4 skipped。生成物审查
content/docs/references/ui/component.mdx—— 本 PR 内唯一应当移动的生成物,三处改动逐行可由这一个键解释:union 描述行、对象成员表新增readonly行、fields行内类型因生成器满 4 个属性后截断而显示省略号。api-surface.json—— 未移动,已实测确认(public API surface + factory signatures unchanged ✓)。该文件只记录"名字 (kind)"字符串,不含类型文本,而本次未新增导出,因此结构上不可能移动。authorable-surface.base.json—— 已git checkout排除,不入本 PR(check:authorable-surface在--check模式下也会重写authorable-surface.base.json—— 一次纯核验会改工作区,且任何无关 PR 都能因此静默推进删除门的锚点 #5358 纪律)。gen:schema会把它刷新到新 baseRev 并顺带吸收 3 个与本单无关的键(system/EmailServiceConfig:appName/defaultTemplateContext/queueDelivery,来自其它已合入 issue)。注意:它不含本次新增的readonly。check:spec-changes/upgrade-guide/skill-refs/skill-docs/skill-examples/react-blocks/react-declaration-parity/variant-docs/empty-state/strictness-ledger/liveness/doc-authoring/docs-audit-scope/nul-bytes)全绿,未新增任何check:/gen:脚本,故无需登记 check-generated 台账。一处对派发预设的更正
派发说明预期"
component.zod.ts在 #5068 中实测 BFS 不可达,故 authorable-surface 不应移动"。结论(不移动)对,但理由需更正:ui/RecordHighlightsProps:fields本身在该 walk 里。真正的原因是这个 walk 是严格一层的 —— 全表 1080 个ui/键中带嵌套路径的为 0(RecordRelatedListProps:add同样只记容器、不下钻add.picker.object)。readonly挂在fields数组项 union 的对象成员上,属于第二层,因此不产生新的 authorable-surface 键。对 #5068 实施者而言这是个有意义的差别:该键不会自动进入 authorable 面,闸门接通时需按 props 解析路径单独覆盖。验证命令
check:api-surface与check:skill-examples在新工作树里初次为红,原因是尚未build出dist/(闸门自身即提示 "this gate reads the BUILT dist ... the removals above are phantoms");建包后两者均转绿,与本改动无关。Changeset
@objectstack/specminor(新增已声明协议键,additive)。后续项(不在本 PR)
record:highlightsblock 的inputs中声明readonly,使其进入sdui.manifest.json—— 裁定中明确可另 PR,由 objectui 车道承接。在此之前,AI 作者仍无法从 manifest 学到该键(docs/audits/2026-06-react-blocks-conformance.md记为 "zero inputs")。readonly)按裁定保留为后续便利层,未并入。Generated by Claude Code