Skip to content

fix(spec,lint): 把 record:* 从 react 档契约里撤回——它 publish 的 props 没有任何渲染器读 (#4413) - #4450

Merged
os-zhuang merged 3 commits into
mainfrom
claude/react-record-blocks-broken-wrk00g
Aug 1, 2026
Merged

fix(spec,lint): 把 record:* 从 react 档契约里撤回——它 publish 的 props 没有任何渲染器读 (#4413)#4450
os-zhuang merged 3 commits into
mainfrom
claude/react-record-blocks-broken-wrk00g

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4413.

react 档契约在 <RecordDetails> / <RecordHighlights> / <RecordRelatedList> / <RecordPath> 上 publish 了 objectName / recordId没有任何渲染器读这两个 prop

对着 objectui a8ad6c0 核实过:十个 record:* 渲染器全部从 useRecordContext() 取记录,而这个 context 只有记录路由(RecordDetailView.tsx:2033)和元数据编辑器预览(PagePreview.tsx:192)挂载;react-page.tsx 只把页面包进 SchemaRendererProvider。于是这些 block 在 react 页上渲染出 "bind a record to preview" 占位符——或者对 record:related_list(唯一读 schema.objectName 的那个)来说,因为父记录 id 永远拿不到而拒绝 fetch

完全照契约写出来的页面渲染成空,而且全链路没有一处报错——包括 os validate:它还在认真地把这些 prop 的字段名解析到它们命名的对象上,等于 lint 在为一个从不执行的绑定站岗。

为什么是撤回,不是去实现它

这份契约不只是"没实现",它的形状就是错的:每个 block 各带绑定,描述的是"四个 block 各取一次同一条记录",而这恰好是共享 record context 存在的目的所要防止的耦合方式——record:details 会把已挂载的 record:highlights 注册过的字段从明细网格里去重,记录级内联编辑保存栏用一个 ifMatch 版本号提交整份草稿。照着 props 去修渲染器,等于把这个错误形状固化进契约(Prime Directive #12)。

这个原语该不该有公开名字(一个作者包在外层的 record SCOPE,一次取数、共享 context),是一个独立的产品判断,已开 #4444 记录,含两个风险(失效总线一致性、别蚕食记录页)和框架侧的连带改动清单。

改了什么

  • spec —— 四个 block 出 REACT_BLOCKS,留下写明原因的 ledger 和每个类型的可用替代。族成员从 ComponentPropsMap 派生而非手写,所以以后新增的 record 组件当天就被门禁覆盖,包括从来没进过契约、但同样能通过注册表构建的 react scope 写出来的另外六个。
  • lint —— 新规则 react-block-needs-record-context(error),按 tag 和 <Block type="record:…"> 两条路都拦,报错里直接给出能用的写法。作者在页面里自己声明的同名组件会 shadow 注入的 scope,这种情况不拦——发布级 error 不该被一个重名撞死。顺带删掉了已经走不到的 record:related_list 分支;COMPONENT_FIELD_SPECS 重新变回元数据面独有。
  • showcase —— Renewals Pipeline 改用这一档真能跑的绑定:<ListView filters={['account','=',sel]}> 出发票,<ObjectForm mode="view"> 出账户摘要。两个都真的渲染。
  • 契约 / skill / 文档 —— 重新生成或订正,包括 SKILL.md 里那句"curated set 之外的注册表 block 在这儿照样能用"——那正是这次的漏洞本身。

验证

  • os validate 在 app-showcase 上通过;把一个 record block 放回 react 页面后确实被拦(走完整 CLI,不是只调函数):

    ✗ Reference integrity check failed (1 issue)
    • page "showcase_task_desk" › <RecordHighlights>: … renders empty no matter how it is bound
        rule: react-block-needs-record-context  at pages[24].source
    
  • spec 7234 / lint 727 / cli 639 / showcase 60 测试通过;两个包 typecheck 干净;check:api-surfacecheck:react-blocks 均已同步。

  • 已 merge 最新 main(5293114)后重跑上述全部。

一处需要知晓的取舍

<ObjectForm mode="view">record:details代餐,不是等价物——只读表单不是明细面板(FLS、分区、内联编辑、OCC)。这次撤回没有损失任何能跑的东西(它本来就是空的),但四个 block 里真正不可替代的就是这一个,#4444 的决策压力也集中在它身上。

🤖 Generated with Claude Code

https://claude.ai/code/session_015hz1t4rGsHvFTZYWRnRNPD


Generated by Claude Code

claude added 3 commits August 1, 2026 07:21
The react-tier contract published `objectName` / `recordId` on
`<RecordDetails>`, `<RecordHighlights>`, `<RecordRelatedList>` and
`<RecordPath>`, and no renderer read either prop. Confirmed against
objectui a8ad6c0: all ten `record:*` renderers take their record from
`useRecordContext()`, which only `RecordDetailView` (app-shell) and
`PagePreview` (metadata-admin) ever mount; `react-page.tsx` wraps the
page in `SchemaRendererProvider` alone. So the blocks rendered their
"bind a record to preview" placeholder — or, for `record:related_list`
(the one that does read `schema.objectName`), refused to fetch because
`ctx?.recordId` never arrives. A page authored exactly to contract came
back EMPTY with nothing reported anywhere, including by `os validate`,
which resolved those props' field names against the object they named:
lint standing guard over a binding that never ran.

Withdrawn rather than implemented. The contract was not merely
unimplemented, it was the wrong SHAPE: per-block bindings describe four
independent fetches of one record, which is exactly the coupling the
shared context exists to prevent (`record:details` drops the fields a
mounted `record:highlights` registered; one inline-edit save bar commits
them all under a single `ifMatch`). Honoring the props would have
fossilized that (Prime Directive #12). If the family is ever wanted on
this surface, the shape is a record SCOPE provider block an author wraps
around them — one fetch, shared context — not per-block props.

- spec: the four blocks leave REACT_BLOCKS, with the ledger for why and
  the working replacement per type. The family is derived from
  `ComponentPropsMap`, so a record component added later is gated the day
  it lands — including the six that were never in the contract but are
  just as reachable through the registry-built scope.
- lint: `react-block-needs-record-context` (error) rejects them on a
  react page, by tag and through `<Block type="record:…">` alike, quoting
  the block that does work. A locally-declared component of the same name
  shadows the injected scope and is left alone. The dead
  `record:related_list` branch goes with it; `COMPONENT_FIELD_SPECS` is
  the metadata surface's alone again.
- showcase: Renewals Pipeline binds the selected account by React state
  the way this tier can — `<ListView filters={['account','=',sel]}>` for
  the invoices, `<ObjectForm mode="view">` for the summary. Both render.
- contract, skill, and validating-metadata/pages docs regenerated or
  corrected to match, including the SKILL.md line that told authors every
  registry block outside the curated set still works here.

Verified: `os validate` passes on app-showcase and fails with this rule
when a record block is put back on a react page; spec 7231, lint 727,
cli 639 and showcase 60 tests pass; both packages typecheck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hz1t4rGsHvFTZYWRnRNPD
@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 1, 2026 8:02am

Request Review

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/lint, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentation Improvements or additions to documentation protocol:ui size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants