fix(spec,lint): 把 record:* 从 react 档契约里撤回——它 publish 的 props 没有任何渲染器读 (#4413) - #4450
Merged
Merged
Conversation
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
…locks-broken-wrk00g
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hz1t4rGsHvFTZYWRnRNPD
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 记录,含两个风险(失效总线一致性、别蚕食记录页)和框架侧的连带改动清单。
改了什么
REACT_BLOCKS,留下写明原因的 ledger 和每个类型的可用替代。族成员从ComponentPropsMap派生而非手写,所以以后新增的 record 组件当天就被门禁覆盖,包括从来没进过契约、但同样能通过注册表构建的 react scope 写出来的另外六个。react-block-needs-record-context(error),按 tag 和<Block type="record:…">两条路都拦,报错里直接给出能用的写法。作者在页面里自己声明的同名组件会 shadow 注入的 scope,这种情况不拦——发布级 error 不该被一个重名撞死。顺带删掉了已经走不到的record:related_list分支;COMPONENT_FIELD_SPECS重新变回元数据面独有。<ListView filters={['account','=',sel]}>出发票,<ObjectForm mode="view">出账户摘要。两个都真的渲染。验证
os validate在 app-showcase 上通过;把一个 record block 放回 react 页面后确实被拦(走完整 CLI,不是只调函数):spec 7234 / lint 727 / cli 639 / showcase 60 测试通过;两个包 typecheck 干净;
check:api-surface与check: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