docs(layout): page-header 两处文档按组件实读收敛到 subtitle,删掉不存在的 breadcrumbs - #3785
Conversation
`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。
#3226 / PR #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
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
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) #3789 落地前 prop 保留、新页面写 subtitle。不预告时间,不替 #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 是唯一在每条路径上都渲染的拼写」,并指向 #3789。这是 PM 点 2 自查要抓的东西,不是扩范围。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
|
✅ 验收通过(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt)—— undraft + auto-merge。 round 1 核验( round 2 返工( 两轮远端 CI 均 0 失败;changeset 按判定脚本与先例确不需要。#3789 落地时应把本 PR 两处措辞列进 done-when(其正文已互相锚定)。 Generated by Claude Code |
CI 的 Changeset Declaration 门(scripts/check-changeset-presence.mjs)守的是 「fixed 组内任一包的 `<pkg>/src/**` 被改动 -> 必须有 `.changeset/*.md` 声明」, 本次三个改动文件全部落在 packages/app-shell/src 下,所以门要求声明。 先前照 #3666(bd04651)与 #3785(f9d70a7)判断「注释-only 不带 changeset」是 读错了先例:那两个 PR 早于这道门(#3387 引入),且 #3785 只动 content/docs/**, 根本不在守护面内。门自己写明了正确出口 —— 「If this change really should release nothing, say so — that is a pass, not a workaround」,即空 frontmatter,照 .changeset/registry-inputs-spec-parity-gate-3797.md 的先例写法。 空 frontmatter 而非 patch:AppContent.tsx 只改 JSX 注释,两条 LegacyMetadataRedirect 路由声明与其余每一行代码未动;两个测试文件断言与用例数 逐字节未变(前后同为 43 passed)。确无可发布的行为改动。 本地验证: node scripts/check-changeset-presence.mjs -> exit 0(识别为空 frontmatter 豁免) node scripts/check-changeset-no-major.mjs -> exit 0
* docs(app-shell): 两个 AppContent 路由测试的生产端叙述改写为「历史 + 现状」两段 (#3749) 三处注释以现在时枚举「侧边栏 / QuickActions 现在发哪些 URL」,而这些 URL 已被 #3660(`sys-datasources`)与 #3739(`sys-objects` + 首页 Manage Objects 卡片)改指 metadata-admin 引擎的规范路由。断言全部正确且全绿 —— 陈旧的只有叙述,以及两个以 生产端命名的 `it` 标题:它们实际量的是别名路由仍能解析且只跳一次。 手法照 #3666:每处拆成「#3610 当时如此」(过去时)与「#3660/#3739 之后如此」 (现在时)两段,现状段正面陈述两条别名今天的身份 —— 不再是任何导航的目标,而是 书签与外部链接的到达路径,这正是重定向必须继续工作的理由。按 #3656,现状段不靠 「不再指向 X」的否认句复述别名 URL。 - `AppContent.noAppComponentRoutes.test.tsx`:头注拆段;两个 `it` 标题从 `sys-datasources:` / `sys-objects:` 改为 `shell alias:` / `host alias:`,即两条 别名各自的改写者(shell 自己的 `LegacyMetadataRedirect` vs 宿主的 `MetadataRedirect`)—— 文件正文本来就画了这条区分。用例体内两处重申同一陈旧 断言的行内注释同步(`sys-datasources` item still points straight at this spelling / this case now measures what production actually does),否则改完标题 的文件会自相矛盾。顺带把同一段落里 `isMetadataRoute` 的现在时「substring test」 收敛为过去时 —— #3638 起它是 `pathSegments.includes('metadata')` 段测试。 - `AppContent.pseudoRouteSegments.test.tsx`:生产端表格按实读重写(逐条读自 `AppSidebar` / `UnifiedSidebar` / `QuickActions` / `HomePage` / `InboxPopover`), 并补一段说明两条旧拼写并未消失,只是从 `navigation` 行移到了上面两行的到达面。 `metadata` 两种拼写下都是完整路径段,所以本文件要证伪的论断不受影响。 - `AppContent.tsx`(#3610 那段):收尾句补上 #3739 也已重指,并注明 `sys-objects` 的链路早在 #3658 就离开了这两条路由。该段前半的过去时叙述保留。 零行为改动:两个测试文件跑前跑后同为 43 passed,断言与用例数未动。 无 changeset —— 注释-only,照 #3666(bd04651)与 #3785(f9d70a7)先例。 Fixes #3749 * chore(changeset): 为 #3749 注释改写声明「不发布任何东西」(空 frontmatter) CI 的 Changeset Declaration 门(scripts/check-changeset-presence.mjs)守的是 「fixed 组内任一包的 `<pkg>/src/**` 被改动 -> 必须有 `.changeset/*.md` 声明」, 本次三个改动文件全部落在 packages/app-shell/src 下,所以门要求声明。 先前照 #3666(bd04651)与 #3785(f9d70a7)判断「注释-only 不带 changeset」是 读错了先例:那两个 PR 早于这道门(#3387 引入),且 #3785 只动 content/docs/**, 根本不在守护面内。门自己写明了正确出口 —— 「If this change really should release nothing, say so — that is a pass, not a workaround」,即空 frontmatter,照 .changeset/registry-inputs-spec-parity-gate-3797.md 的先例写法。 空 frontmatter 而非 patch:AppContent.tsx 只改 JSX 注释,两条 LegacyMetadataRedirect 路由声明与其余每一行代码未动;两个测试文件断言与用例数 逐字节未变(前后同为 43 passed)。确无可发布的行为改动。 本地验证: node scripts/check-changeset-presence.mjs -> exit 0(识别为空 frontmatter 豁免) node scripts/check-changeset-no-major.mjs -> exit 0 --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #3267
背景
#3226 / PR #3265 只收窄了机器读的声明面(
packages/layout/src/index.ts里page-headerregistration 的inputs)。人和 AI 作者读的声明面 —— 文档 —— 还在教同一个非 spec 的description,而且教得比inputs直接得多:一份是复制粘贴即用的 authored JSON 示例。本 PR 让文档面跟上,纯文档单,不触packages/**,不触content/docs/releases/**。文档的每一句都从
origin/main的代码读出,不从 issue 正文抄 —— 下面是逐条对照表。改动
1.
content/docs/guide/layout.md(「PageHeader Component」小节)description改subtitle;删掉 6 行breadcrumbs数组(组件一处都不读);actions从 ComponentSchema 节点列表(type: "button")改成 spec 形状的 action id 列表["edit", "delete"]。subtitle,删breadcrumbs,actions类型改为 action id 或 ActionDef 列表,补showBack/children,description降级为标注「legacy alias,不要 author」的一行。subtitle不写description」与「没有breadcrumbs数组」(spec 的breadcrumb是单数 boolean,canonicalpage:header的显示开关,不是链接列表)。{field.path}插值与「未解析折叠为空串」的行为说明。2.
content/docs/layout/page-header.mdx(「Component Props」块)subtitle(标为规范键)、icon、actions、showBack、schema,并补上extends React.HTMLAttributes< HTMLDivElement >。description保留不删(见下「退役状态」),但标为 legacy alias、指向subtitle。subtitle的 spec 依据、description的退役状态与门控、actions的委派与右侧槽优先级、showBack的默认值推断。同文件里 5 处 PageHeader 示例的 prop 拼写一并改成
subtitle(含 Layout ASCII 图与 Styling 小节的 "Description" 标题)。这一步超出 issue 字面列的 L25-34,但不做会让新加的「description是 legacy,写subtitle」注解被紧随其后的 5 个description=示例当场推翻 —— 半个修法比不修更误导。(PM 复核已采纳。)唯一不动的
description:Usage with Page Component里 Page 组件的description=示例。那是pagerenderer 自己的真 prop(页面自身的正文,不是 header 的副标题),已就地加注说明,防止后来者顺手「修」错。description的退役状态(第二个 commit 修正了这一段)初版把退役写成「等上游 conversion 落地」。PM 复核代查后指出该前提已过期,独立复核确认(objectstack
origin/main=d42a92fc6):page-header-subtitle-alias已 livedocs/protocol-upgrade-guide.md:273—— "live — protocol 17 loader accepts the old shape"properties.description改写为properties.subtitlepackages/spec/src/conversions/registry.ts:4616-4633(toMajor: 17,renameKey+emitConversionNotice)subtitle在场则不改写,被遮蔽的description原样留下registry.ts:4607-4612element:text_input的description是它自己的活属性)registry.ts:4610-4612,fixture:4652defineStack/validate/lint+applyConversionsToStoredItem的sys_metadata存量行packages/spec/CHANGELOG.md:3499(089767f)packages/lint/src/authoring-rules.ts:550因此文档改为如实三段式:(a) 上游 conversion 已 live 且具体做什么;(b) 本仓
descriptionprop 的退役由 objectui#3789 门控,门槛是核实每条page-header作者路径都经过执行改写的 loader;(c) #3789 落地前 prop 保留、新页面写subtitle。不预告时间,不替 #3789 下结论。同时修掉
layout.md的一处同类失真(PM 点 2 的一致性自查抓到):原 blockquote 断言「写description的 metadata 在 canonicalpage:header下什么都不出」。conversion 落地后这个结论变成路径相关 —— 经 loader 的元数据会被改写成subtitle并正常渲染(registry fixture:4645-4647明确把type: 'page:header'也一并转换),只有绕过 loader 的裸 JSON 才仍然不出。已改写为「两处仍接受description(加载期改写 + 本渲染器直读),但subtitle是唯一在每条路径上都渲染的拼写」,并指向 #3789。文档声明 ↔ 代码行号对照表
origin/main=00b9451d8)title: string必填packages/layout/src/PageHeader.tsx:29subtitle?: string是规范键PageHeader.tsx:35;packages/layout/src/index.ts:56(收窄后的inputs)PageHeaderProps= title/subtitle/icon/breadcrumb/actions/aria,无descriptionpackages/spec/src/ui/component.zod.ts:224-232description?是 legacy alias,subtitle优先PageHeader.tsx:36+:143(const secondaryRaw = subtitle ?? description;)icon?接 Lucide 名或 React node,渲染在标题左侧 chipPageHeader.tsx:42、:224-228action?: React.ReactNode落右侧槽PageHeader.tsx:43、:207actions?交给record:quick_actions,location: 'record_header'PageHeader.tsx:58、:192-206actions条目是 action id 或 ActionDef,不是 ComponentSchemapackages/plugin-detail/src/renderers/record-quick-actions.tsx:60-93(全字符串走对象 metadata 查名,否则当 ActionDef)showBack?省略时按 record 上下文推断(有recordId且非 embedded 才为 true)PageHeader.tsx:50、:159-161schema?的形状PageHeader.tsx:65children落右侧槽,actions优先PageHeader.tsx:182-190、:207(action→children→actionsSlot→schemaChildren)className透传PageHeader.tsx:210{field.path}插值;未解析折叠为空串而非漏出原模板PageHeader.tsx:89-110、:136-140、:147-151PageHeader.tsx全文breadcrumb仅出现在 L16 的 doc 注释里,零处读取breadcrumb是单数 boolean,canonical 节点的显示开关component.zod.ts:228;渲染器packages/components/src/renderers/layout/containers.tsx:979(!== false)、:1495-1498、:1530-1531、:1560page:header渲染器只读subtitlecontainers.tsx:962(schema?.subtitle ?? schema?.properties?.subtitle);该文件description读取数 = 0(grep -c实测)page-header的inputs只声明 title/subtitlepackages/layout/src/index.ts:50-58description是pagerenderer 自己的真 proppackages/components/src/renderers/layout/page.tsx:605、:635、:672;注释:603-604明说「the page's own prose, not a duplicate of the header's subtitle」验证(返工后复跑)
Changeset
不需要。 依据是仓里的判定脚本本身,而非推断:
与先例一致:近 15 个 docs 提交(含多个改
content/docs/**的)全部零 changeset。顺手发现(未在本 PR 修,另开 issue)
page-header.mdx的 Styling/Container 两条数值与 PageHeader.tsx 不符(pb-8 on desktop、gap-4) #3786 —— 同文件 Styling/Container 两条数值失实(pb-8 on desktop/gap-4,代码是无条件pb-4与gap-3,且有未写进文档的border-b)。layout-page-header/pageheader-with-actions)手搓 div,完全不用page-header#3787 —— PageHeader 文档页唯一 live demo 手搓 div,全文不含page-header。content/docs/**.mdx仍 import 已不使用的{ ComponentDemo, DemoGrid }(文件里只有SchemaExample) #3788 —— 约 40 个 mdx 仍 import 已不用的{ ComponentDemo, DemoGrid }(根因在scripts/extract-mdx-demos.mjs的 import 替换正则),observation-class。均属另一类断言,故意不搭车。
🤖 Generated with Claude Code
https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt