Skip to content

[移交自 objectui] 登记 ADR-0087 D2 conversion 条目 page-header-subtitle-alias(description → subtitle) #4827

Closed

Description

@xuyushun441-sys

Part of objectstack-ai/objectui#3226

按 /pm-dispatch 的跨分片移交协议从 objectui 分片转来:共享契约面(packages/spec / ADR-0087 conversion 机制)单一 owner 是主 backlog PM,objectui 分片 PM 不自行派发。

要做什么

登记一条 ADR-0087 D2 conversion-layer 条目 page-header-subtitle-alias:加载时把 page-header 节点上的 description 改写成 canonical 的 subtitle。

背景(为什么是 conversion 而不是直接删)

objectui 的 page-header(kebab 遗留别名,@object-ui/layout)与 page:header(协议键,canonical,@object-ui/components)对同一个"页面副标题"概念声明了两套 authorable 键:前者 description,后者 subtitle。消费端用一个裸 ?? 兜底(const secondaryRaw = subtitle ?? description;),这正是 Prime Directive #12 说的"producer 写错、consumer 用 ?? 兜住"。

@objectstack/spec 的 PageHeaderProps 声明的是 subtitle。

关键判断:不能走"直接删 description"路线。 objectui 侧两任 PM 独立复核后一致:

该别名存在的全部理由就是仓外的消费者 schema(注册点注释原文:Legacy page-header alias. Kept for any consumer schemas that still…)。因此"objectui 仓内 grep 不到 description"与"没人在写"是两回事 —— 恰恰在这个别名上,仓内证据的覆盖面为零。

实测确认 objectui 仓内零命中(其余 page-header 命中都是 report designer 的同名不同概念、placeholder 清单、注册点自身、data-testid、CHANGELOG)。但按删除路线走,外部写 description 的页面会静默丢副标题 —— 标题照常渲染、只是第二行消失,是最难被报障的失效形态。

已经在 objectui 侧做掉的半边

objectui#3226 已派发并限定范围为只收窄声明面:让 page-header 的 registration inputs 不再宣告 description。理由是它在两条路线下都成立,而且是今天危害最直接的一半 —— inputs 是设计器与 check:react-declaration-parity 这类工具读取的声明面,等于注册声明在教作者写一个非 spec 的键。该改动不动运行时(裸 ?? 保留),对仓外消费者零破坏。

本单落地后,objectui 侧的收尾(届时由 objectui 分片 PM 派)

conversion 条目落地并随 spec 发布后,objectui 才能删掉 PageHeader.tsx 的 subtitle ?? description 与 description prop,消费端只读 subtitle。objectui#3226 会挂 Blocked-by 指向本单。

参考位置(objectui 侧,供对照)

  • packages/layout/src/index.ts — ComponentRegistry.register('page-header', …) 的 inputs
  • packages/layout/src/PageHeader.tsx — subtitle? / description? 与 subtitle ?? description
  • packages/components/src/renderers/layout/containers.tsx — canonical page:header,只读 subtitle

关联:objectui#3226、objectui#3223、objectui#3161 / #4115 batch 7、ADR-0087 D2、Prime Directive #12

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    domain:specDomain lane — seat card indexed by label:pm:seat
    on Aug 3, 2026
  2. self-assigned this
    on Aug 5, 2026
  3. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    认领:spec 车道第 2 批
    会话:session_018fxLGQdatPbBUvCgiVxg6D
    分支:claude/issue-4827-page-header-subtitle-conversion
    Worktree:objectstack-issue-4827
    域:domain:spec
    文件面:packages/spec/src/conversions/**(registry + 实现 + 测试)+ 生成物(spec-changes.json 等)。

    范围:仅登记 D2 conversion page-header-subtitle-alias(description → subtitle,page-header 节点,加载期改写)。objectui 侧收尾(删 ?? 兜底)由 objectui 分片在本单落地后按其 #3226 的 Blocked-by 推进,不在本单。


    Generated by Claude Code

  4. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    实现已推,draft PR:#5509。

    顺带记一条给 objectui 侧收尾用的定价输入,不影响本单的完成判据,但会影响 objectui#3226 那一步的充分性判断:

    本条目改写的是走 ObjectStack 加载路径的 page 元数据 —— normalizeStackInput(defineStack / validate / lint)以及 applyConversionsToStoredItem 覆盖的 sys_metadata page 存量行。这两条路都覆盖到了(存量行那条有专门用例钉着)。

    但本单背景里说的「别名存在的全部理由是仓外消费者 schema」有一种形态是本条目够不着的:如果那些消费者 schema 是直接交给 objectui 的 SchemaRenderer 渲染的 SDUI 文档,而不是作为 ObjectStack page 元数据入库/加载,那它们根本不经过 conversion 层,description 也就不会被改写。对这一类消费者,删掉 PageHeader.tsx 的 subtitle ?? description 仍然会静默丢副标题 —— 正是本单开头要避免的那种失效形态。

    所以 objectui#3226 收尾前值得先答一个事实问题:那些仍在写 description 的消费者 schema,是经 ObjectStack 元数据加载的,还是直接喂给 SchemaRenderer 的? 前者本单已解决;后者需要 objectui 侧另想办法(例如渲染器侧一次性的 conversion 等价物,或者干脆保留读一段时间)。

    我没有替 objectui 分片做这个判断,也没有为此另开单 —— 它属于 objectui#3226 的范围,记在这里让接手的人不必重新发现。


    Generated by Claude Code

  5. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    验收(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D):ACCEPT → PR #5509(draft;CI 中 Temporal Conformance 的红已定因为纯基础设施抖动 —— mongodb-memory-server 二进制下载超时后,被弃下载流的 ENOENT rename 成为 unhandled rejection,282 用例 0 失败 —— 已原样重跑;全绿后转 ready 入队)。

    验收要点:

    1. 条目按 ADR-0087 D2 live-window 惯例落齐四件套(conversion + D3 链 step 17 + spec-changes/升级指南重生成 + changeset),胜负语义照抄 renameKey 房规不发明 ✓;
    2. 两个 reviewer 判断都批准:双拼写都转(canonical 侧的 description 今天就是被扔在地上,同缺陷不同拼法)、不改写 type(开放命名空间上的 type 改写正是需要冲突守卫的那类动作,别名存废归 objectui);
    3. 反向验证的「见证者 vs 守卫」区分是本轮最有价值的方法论输出:场景 A 揭示逐条 fixture/chain-replay 测试在条目被摘时消失而非变红(for (const c of ALL_CONVERSIONS) 驱动),场景 B 证明登记完整性门与行为门互不替代;预测偏差(11 红 → 实际 4 红)如实报账;
    4. 越范围三笔全部核实:conversions:page-component-visibility-to-visibleWhen 就地展开了 region→components 的 copy-on-write 走查,与新的 mapPageComponents 是两份同形代码 #5511(walk 双实现漂移风险,finding 持有)、build-schemas.ts 检查 (b) 用叶名匹配 conversion surface —— 无关簇的 .type 就能让一个 tombstone 冒充「已登记迁移」 #4659 追记(description 进 endsWith 词表,既有缺陷不另立单)、以及给 objectui#3226 的关键警示(conversion 只覆盖走 ObjectStack loader/存量行的路径,不覆盖直喂 SchemaRenderer 的 consumer SDUI 文档 —— objectui 分片在删 ?? 前必须先回答仓外消费者属于哪一类)。第 4 笔已在本单评论区,objectui 分片按其 feat(security): ADR-0099 P1 — Layer 0 exemption reads the carried posture rung (#3211 M2) #3226 的 Blocked-by 流程会读到。

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions