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)。

Activity

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

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