Uh oh!
There was an error while loading. Please reload this page.
fix(layout): page-header 的 registration inputs 不再宣告 description - #3265
Merged
Conversation
…s an input `registerLayout()` declared `inputs: [title, description]` for the legacy kebab alias, while `@objectstack/spec/ui`'s `PageHeaderProps` — and the canonical `page:header` renderer in `@object-ui/components` — declare `subtitle`. `inputs` is a DECLARATION surface (the designer palette, the framework's `check:react-declaration-parity`), so this did not tolerate a legacy spelling, it published a second dialect and taught authors a non-spec key. Metadata that took the offer renders a subtitle under `page-header` and loses it under `page:header`. The runtime `subtitle ?? description` read in `PageHeader.tsx` stays on purpose: the alias exists for out-of-repo consumer schemas, so zero in-repo hits is not evidence nobody writes it, and dropping the read would silently delete an external page's second line. It is retired with the ADR-0087 D2 conversion entry `page-header-subtitle-alias`, which lives in the framework repo. Narrowing the declaration is unconditional and changes no runtime behaviour. New tests pin both halves: the registration may not declare `description`, must declare `subtitle`, and may declare nothing the spec's own shape does not; the runtime fallback is pinned as a sequencing guard, to be deleted together with the conversion entry. Refs #3226 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa
The latest updates on your projects. Learn more about Vercel for GitHub. |
xuyushun441-sys
marked this pull request as ready for review
August 3, 2026 09:06
Uh oh!
There was an error while loading. Please reload this page.
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Aug 8, 2026
Closed
akarma-synetal pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…bjectstack-ai#3785) * docs(layout): page-header 两处文档按组件实读收敛到 subtitle,删掉不存在的 breadcrumbs `content/docs/guide/layout.md` 的 authored JSON 示例与 Schema API 教的是非 spec 的 `description`,还声明了 `breadcrumbs?: Array<{label,href?,icon?}>` 与 `actions?: ComponentSchema[]` —— 前者 PageHeader.tsx 一处都不读,后者类型是错的。 `content/docs/layout/page-header.mdx` 的 Component Props 块只列 `description`、 一字未提 `subtitle`,并漏了 icon/actions/showBack/schema 四个真实 prop。 objectstack-ai#3226 / PR objectstack-ai#3265 只收窄了机器读的 registration `inputs`;这一单让人和 AI 作者读的 文档面跟上,每条声明都从 origin/main 的代码读出: - layout.md:示例与 Schema API 改 `subtitle`;`actions` 改为 action id / ActionDef 列表(交给 `record:quick_actions`,不是 ComponentSchema);补 `showBack`/`children`; 两段 blockquote 写清「写 subtitle 不写 description」与「没有 breadcrumbs 数组」 (spec 的 `breadcrumb` 是单数 boolean,canonical `page:header` 的开关)。 - page-header.mdx:Component Props 块补齐真实 prop 全集;`description` 保留但标为 legacy alias,注明随上游 ADR-0087 D2 conversion `page-header-subtitle-alias` 一并移除(现在删会让仓外页面静默丢副标题)。同文件 PageHeader 示例的 prop 拼写 一并改 `subtitle`,否则新加的注解会被下面 5 个 `description=` 示例当场推翻; `<Page description=…>` 那一处**不动** —— 它是 `page` renderer 自己的真 prop, 并就地加注防止后来者误改。 无 changeset:`scripts/check-changeset-presence.mjs` 判定「0 of them under the src/ of a package the release covers … no changeset is owed」,与近 15 个 docs-only PR 的 先例一致。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt * docs(layout): 按上游实读改写 description 退役段 —— conversion 已 live,退役改由 objectstack-ai#3789 门控 PM 复核代查后指出:conversion `page-header-subtitle-alias` 已在 objectstack main 落地并标 live,因此上一版 page-header.mdx 里「It is scheduled for removal … Until that conversion ships」是落地即陈旧的事实断言 —— 正是本单要消灭的缺陷类。 独立复核(objectstack origin/main = d42a92fc6): - docs/protocol-upgrade-guide.md:273 —— `page-header-subtitle-alias` | `page.component.page-header.description` | 'description' → 'subtitle' | **live — protocol 17 loader accepts the old shape** - packages/spec/src/conversions/registry.ts:4616-4633 —— toMajor: 17, apply() 对 PAGE_HEADER_COMPONENT_TYPES 的节点 renameKey(properties, 'description', 'subtitle') 并 emit ConversionNotice - registry.ts:4607-4612 —— canonical 优先:`subtitle` 在场时不改写,被遮蔽的 `description` 原样留下;只动 header 节点(element:text_input 的 description 是它自己的活属性) - packages/spec/CHANGELOG.md:3499(089767f)—— 加载期为 defineStack / validate / lint,以及 applyConversionsToStoredItem 覆盖的 sys_metadata 存量行 - packages/lint/src/authoring-rules.ts:550 已按该改写实现 page-header.mdx:退役段改为如实三段式 —— (a) 上游 conversion 已 live 且做什么; (b) 本仓 prop 退役由 objectui#3789 门控,门槛是核实每条作者路径都经过执行改写的 loader;(c) objectstack-ai#3789 落地前 prop 保留、新页面写 subtitle。不预告时间,不替 objectstack-ai#3789 下结论。 layout.md:PM 说该文件未做「未落地」断言、不用动 —— 确认无landing-state 断言, 但按 PM 要求自查两文件一致性时发现另一处同类失真:原 blockquote 断言「写 description 的 metadata 在 canonical page:header 下**什么都不出**」。conversion 落地后该结论变成路径相关 —— 经 loader 的元数据会被改写成 subtitle 而正常渲染 (registry fixture:4645-4647 明确把 type: 'page:header' 也一并转换),只有绕过 loader 的裸 JSON 才仍然不出。故按真实情况改写为「两处仍接受 description(加载期 改写 + 本渲染器直读),但 subtitle 是唯一在每条路径上都渲染的拼写」,并指向 objectstack-ai#3789。这是 PM 点 2 自查要抓的东西,不是扩范围。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
objectstack-ai#3797) (objectstack-ai#3806) 把 PR objectstack-ai#3795 在 `record:highlights` 上落的单块检查(「不得声明 spec 不接受的顶层 input」)推广到所有「`ComponentPropsMap` 有条目且本仓注册了非空 `inputs`」的 block, 期望值运行时从 spec 自身 shape 推导(不复述键表),现有偏离全部进**显式豁免名单**, 每条带理由 + 追踪单号。 门放在 `apps/console/src/__tests__/`,因为它需要的是产出发布物那份完整注册图: `dev/manifest-dump.tsx` 用 `src/register-plugins.ts` + `@object-ui/components` 搭 manifest,本文件就导入这一对(与隔壁 `public-contract.test.ts` 同法),不自己攒一份 会自说自话的清单。覆盖面不限于 public tier —— `renderers/layout/page.tsx:462` 用 `getKnownTypes()` 现搭运行时 JSX 页面校验 manifest,所以非 public 的 block(如 `element:record_picker`)的 `inputs` 同样是活的 prop 白名单。 四个 OFF-SPEC block 逐块以代码判定,结论都落在 spec 一侧,因此**本仓四个 block 的 `inputs` 一个字节都没动**: - `page:header` 的 `recordChrome`/`showStar`/`showCopyId` —— 渲染器实读 (`containers.tsx:979/980/981`),消费于 `:1453` 的布局分支与 `:1531/:1532` 的 RecordTitleChip;作者可达且非宿主注入(宿主注入走 RecordContext 且故意不声明), `preview-samples.ts:68` 与 `buildDefaultPageSchema.ts:413` 今天就在写。 (issue 猜「可能是 objectstack-ai#3226/PR objectstack-ai#3265 有意保留的 renderer-only prop」已证否:objectstack-ai#3265 动的是 `packages/layout` 的遗留别名 `page-header`,canonical 这个没被碰过。) - `page:accordion.variant` —— 渲染器实读 `containers.tsx:734`,`:735` 据此改每个 面板的边框类,spec 完全没有对应键。 - `page:tabs.tabStyle` —— spec 已用 `type` 声明同一概念,所以是「一义两拼」;但两条 本地动作都被证否:撤掉它会删掉扁平 SDUI 载体唯一能表达的拼法 (`SchemaRenderer.tsx:251-270` 故意不上提 `properties.type`),改成发布 `type` 又 会声明一个自家 parser 结构上无法校验的键(`validate.ts` 的 `BASE_PROPS` 含 `'type'`)。收敛方向是 spec 侧的契约决定,不在这里猜。 - `element:record_picker` 的 `labelField`/`valueField`/`label` —— 上游 objectstack#5775 已经解决,且正是往本仓渲染器实读的 `labelField` 收敛 (`displayField`/`searchFields`/`multiple` 已转 `retiredKey()` 墓碑)。本仓这三条 flag 纯粹是 pin 落后:npm 上最新已发布的 `@objectstack/spec` 是 `17.0.0-rc.5`, 早于 #5775。 前三项转 objectstack#6776(跨仓转移协议,`pm:queue`,带 `Part of objectui#3797`); 八条豁免全部挂单号。豁免会自己过期:一旦 spec 声明了某个被豁免的键, `carries no stale exemption` 就红,强制删条目,名单不会烂成永久 allowlist。 Fixesobjectstack-ai#3797 Co-authored-by: Claude <noreply@anthropic.com>
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 freeto 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#3226
按 issue 上两条 PM 裁定(
#3226的 comment):本单走路线 B(ADR-0087 D2 conversion 条目)而非直接删除,而 conversion 机制在 objectstack 仓,不在这里。因此本 PR 的范围被明确收窄为无条件成立、且跨仓依赖为零的那一半:本 PR 做的:
registerLayout()里page-header的inputs从[title, description]收窄为[title, subtitle]。本 PR 刻意不做的:不删
PageHeader.tsx的subtitle ?? description,不删descriptionprop。这两项被本 PR 的测试主动钉住了(见下),删它们等于走回被否决的删除路线。剩余部分(阻塞在上游):conversion 条目
page-header-subtitle-alias(description→subtitle,加载时改写成 canonical 键)+ 随后删除消费端裸??与descriptionprop。这部分归主 backlog PM,在 objectstack 立单;本单挂Blocked-by。为什么这一半可以现在就做,而且必须现在做
inputs是声明面,不是文档。设计器用它生成属性面板,framework 的check:react-declaration-parity拿它跟 spec schema 对差。所以宣告description不是「容忍一个遗留拼写」,而是对外发布了第二套方言,并且在教作者(尤其是读 registry 学形状的 AI 作者)写一个 spec 里根本不存在的键:@objectstack/spec/ui的PageHeaderProps的 shape 是title / subtitle / icon / breadcrumb / actions / aria—— 没有description(已实测)。page:header(@object-ui/componentscontainers.tsx)声明的是subtitle。description在page-header下渲染出副标题,换到page:header下静默丢失。同一份 JSON,两个结果 —— 这正是「单一契约」要消灭的东西。收窄声明面不改变任何运行时行为(
??还在),因此对仓外消费者零破坏;而把声明面留在错的状态,只会持续生产出将来 conversion 层还得去吸收的错误 metadata。为什么运行时的
??现在不能删这个别名存在的全部理由就是仓外的消费者 schema(佐证:
registerLayout()在本仓没有任何调用点,它纯粹是@object-ui/layout对外的公开导出)。所以「仓内 grep 零命中」(我复核过,确实为零)不构成「没人在写」的证据。按删除路线走,外部写description的页面会静默丢副标题 —— 标题照常渲染,只是第二行没了,是最难被报障的失效形态。预警的门禁冲突:核实过,不存在
PM 预警了
check:react-declaration-parity可能校验「声明的 inputs ↔ 组件实际读取的 props」。查了 framework 侧实现,不会冲突,两个独立理由:inputs),其文件头明确写着「It never looks at a renderer」。组件读不读description完全不在它视野内。description恰恰就是一个 registry-only input,删掉它只会减少 divergence,不可能触发 ratchet。REACT_BLOCKS(ObjectForm/ListView/ObjectChart/Block),baseline 里也只有这三个 block;page-header根本不在其中。测试:两半都钉住
新增
packages/layout/src/__tests__/page-header-authorable-keys.test.tsx(7 个用例):page-header与layout:page-header两个键都不得含description;必须含subtitle;并且 —— 这条是从 spec 自己的 shape 推出来的,不是手写白名单 —— 声明的每一个 input 都必须是PageHeaderProps的键。将来再往这里加一个 spec 没有的 input,会因为和description完全相同的理由变红,不需要谁记得这个 issue。description渲染成副标题、且subtitle在两者都在时胜出 —— 这不是为别名背书,而是钉住顺序:在 conversion 条目落地前删掉这个读取,就是被否决的删除路线。注释里写明:conversion 落地时,同一个 commit 里删掉 fallback 和这两个用例。验证
Changeset
有 ——
patch(@object-ui/layout)。虽然运行时行为不变,但声明面是对外发布的:设计器属性面板会从 "Description" 变成 "Subtitle",生成的sdui.manifest.json也随之改变,对使用设计器的人是可见的。按 AGENTS.md 未标major。顺带发现(未在本 PR 修,已另开 issue)
文档里同样在教
description,而且比inputs教得更直接 —— 但它在content/docs/,超出本单的packages/layout范围,且牵扯到别的 doc drift(breadcrumbs这个 prop 组件根本不读),不适合搭车:content/docs/guide/layout.md「PageHeader Component」的 authored JSON 示例写"type": "page-header"+"description",Schema API 块声明description?: stringcontent/docs/layout/page-header.mdx「Component Props」块列description?: string,且完全没提subtitlehttps://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa
Generated by Claude Code