Skip to content

kind:'react' 页面上 record:* 四个 block 全部失效——契约 publish 的 objectName/recordId 渲染器根本不读(#4340 后续) #4413

Description

@os-zhuang

#4340(由 #4353 关闭)settle 了 <RecordRelatedList objectName> 的语义。为了确认那个判定,我把 objectui(HEAD c785740)拉下来读了渲染器——判定是对的,但顺带发现一个更靠下的问题record:* 这一族 block 在 kind:'react' 页面上根本不工作,而 react-tier 契约照样 publish 着它们的 objectName / recordId 绑定。

这是 AGENTS.md Prime Directive #10 的推论——「never advertise or demo a capability the runtime doesn't actually deliver」——的一个活实例。

证据链

  1. react-page 的 scope 把 JSX props 摊进 SDUI schema bag,但只包了 SchemaRendererProvider,没有 RecordContextProvider
    objectui packages/components/src/renderers/layout/react-page.tsxbuildComponentScope

    React.createElement(SchemaRenderer, {
      schema: { dataSource, ...props, type: tag },
    })

    全文件 grep RecordContextProvider / RecordContext 零命中

  2. RecordContextProvider 只由 record 详情路径挂载——app-shellRecordDetailView,以及 metadata-admin 的 PagePreview。react 页面不经过任何一个。

  3. useRecordContext() 在 provider 外返回 null,这是有意设计(objectui packages/react/src/context/RecordContext.tsx:190-198 的注释写明:让 record:* 渲染器在 Studio 设计器里能静态渲染而不抛错)。

  4. 于是在 react 页面上:

    block 渲染器取 objectName 取父记录 实际结果
    <RecordDetails> ctx?.objectName || '' (record-details.tsx:53) ctx 作者写的两个 prop 都到不了 → 空
    <RecordHighlights> ctx?.objectName || '' (record-highlights.tsx:37) ctx(data/objectSchema 也来自 ctx) 同上 → 空
    <RecordPath> ctx ctx 同上 → 空
    <RecordRelatedList> schema.objectName(record-related-list.tsx:64) ctx?.recordId parentLinkValue = null拒绝 fetch

    最后一行有 objectui 自己的测试钉着:packages/plugin-detail/src/__tests__/RelatedList.parentscope.test.tsx:58"refuses to fetch when parentId is missing"

    注意四个 block 里只有 <RecordRelatedList> 从 schema 读 objectName;另外三个连对象名都走 context。也就是说这一族内部本身就不一致

活体影响

examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts<RecordHighlights><RecordRelatedList> 现在渲染出来是空的——#4353 之前和之后都是#4353 修正的是元数据的正确性(objectName 该写子对象),它没有、也不可能在 framework 侧让渲染器去读它。

需要更正 #4353 的一句话

那个 PR 的正文写了「the parent record stays bound by recordId」。那是契约这么说,渲染器并不读 schema.recordId#4353relationshipValueField 被 ledger 为 skipped-with-reason,理由写的是「react 面绑父记录但不绑父对象」——实际情况更糟:父记录也没绑上

#4340objectName 判定本身是确认无误的,不受此影响:

<RelatedList
  api={objectName}                           // 被查询/列出的对象 = 子对象
  referenceField={schema.relationshipField}   // 该对象上的外键
  parentId={parentLinkValue}                  // 父值,另走 RecordContext
  columns={filteredColumns}                   // objectName 的列
/>

columns/sort 确实解析到 objectName 命名的那个对象,所以 #4353 里的 lint 校验的是正确的对象。

修的方向(以及它的真实成本)

直觉方案是:让 react-page 的 scope 对 record:* 类型用 block 自己的 objectName/recordId props 包一层 RecordContextProvider。但 RecordContextProviderRecordContext.tsx:167)是个纯值 provider,自己不取数——它要 data / objectSchemaRecordDetailView 是自己 fetch 完再喂进去的。所以真正要加的是一个会取数的 wrapper,这不是接线,是新组件。

分档看成本:

  • <RecordRelatedList> 在默认 relationshipValueField: 'id'只需要 recordId,不需要 data —— 这一档最便宜,单独修就能让 showcase 那个页面活过来。
  • <RecordDetails> / <RecordHighlights> / <RecordPath> 需要 data + objectSchema,必须真取数。

另一条路是承认这四个 block 本就是 record-detail 语境的组件,把它们从 react-tier 契约里摘掉REACT_BLOCKS),而不是让 react 面去伪造一个 record context。ADR-0080「capability ≠ contract」支持这种取舍。两条路都是产品决策,不是纯实现问题。

不论选哪条,当前状态不能留:契约 publish 了四个 block 的绑定 prop,渲染器一个都不读(related-list 只读一半),而 os validate 现在还会认真校验这些 prop 指向的字段——lint 在为一个不会执行的绑定把关。

验收

record:* 的每个 block 要么在 react 页面上真的按自己的 props 绑定并渲染,要么从 react-tier 契约里移除;showcase 的 Renewals Pipeline 页面能真的渲染出 highlights 和 invoice 关联列表(或者不再声称能)。

/cc 需要 objectui 侧改动(objectstack-ai/objectui)。

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions