test(console): record:* 家族第一次有了行为证据,抓到 record:activity 恒空与 useMetadata 的渲染死循环 (#3149) - #3166
Merged
Merged
Conversation
…a 的渲染死循环 (#3149) #3146 的 binding-reach 探针按设计看不见 record:* 家族——它们从 RecordContextProvider 取数,裸挂载什么都不做,"没有数据调用"因此不说明任何 问题。而那恰好是 objectstack#4413 待过的地方:四个 block 声明了没人读的 objectName/recordId,在真实记录页上渲染空白,全程门禁皆绿。 record-block-record-reach.test.tsx 把 11 个 public record:* block 挂在 record context 下,绑两条不同的同对象记录各挂一次,问 DOM 或数据调用有没有 变化。两道探针的行为覆盖 14 → 24 / 57。 几个刻意的设计决定: - 两条记录而不是"绑 vs 不绑":不绑的对照组少一层 provider,useId 整体偏移, 于是每个块都"有差异"——探针自己制造绿。 - 差分而不是"渲染非空":后者对无视全部输入的块也恒为真,正是 #3149 记下 不覆盖展示原语的理由;在这里加一道同样的东西是自毁。 - 仪器本身被断言:每个块用记录 A 再挂一次,要求逐字节相同。这条一旦失败, "A 和 B 不同"就不再等于"记录到达了输出",整个文件的绿就是噪声。 - 崩溃按崩溃报:SchemaRenderer 会把渲染期抛错画成错误卡片,而崩了的块对两条 记录渲染同一张卡片,不单独断言就会落进"无差异"档、被读成关于绑定的结论。 - 无外网:这家族有块直接调 fetch('/api/v1/security/explain'),happy-dom 下 打到 localhost:3000——原本"能跑"只是因为连接被拒。现在立即 reject,URL 进 同一份调用台账。 结果 8 个响应、3 个入 NO_RECORD_REACH 台账。台账条目必须写明宿主路径,因为 "宿主供数"只在真有宿主供数时才是理由:record:discussion(DiscussionContext, RecordDetailView 挂)和 record:reference_rail(entries 由 buildDefaultPageSchema 注入)都核得住,record:activity 核不住——见 #3165。 顺带修掉一个探针挂不起来的缺陷:useMetadata() 的"优雅兜底"每次调用都新建对象, provider 外每渲染一次 getItem 就换身份;useMetadataItem 把它放在 effect deps 里并 setState 一个全新对象 → 无限循环,同步到能把 render() 卡住。注释说它存在 是为了让 provider 外的单测能渲染,它恰恰让那些消费者挂不起来。改成冻结的模块级 单例,并让清空分支在已清空时 bail。 #3149 第 2 层同时落地,但原方案不存在:57 个 public block 没有任何一个声明 recordId 输入(全仓库仅 view:detail / detail-view 两处,都不 public)——和 objectstack#4472 方向 (d) 同一类结果。代之以 record:related_list / record:line_items 的 required relationshipField + childObject 必须进子查询、 且 scope 到绑定的父记录,两个都成立。 Closes #3149 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3cP1eY1novcNhQEDBrSZD
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
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.
#3149 的第 2 层和第 3a 层。第 1 层是 #3153,第 3b 层是明确的不做。除 3b 外无剩余项,本 PR 合并后 #3149 可关。
为什么需要第二道探针
#3146 的
public-block-binding-reach.test.tsx问的是:声明了objectName的 block 裸挂载后,有没有一次数据调用带上这个对象名。record:*家族按构造在这个问题之外——它们从RecordContextProvider取数,裸挂载正确地什么都不做,于是"没有数据调用"什么也不说明。而那恰好是 objectstack#4413 待过的地方:
record:details/record:highlights/record:path/record:related_list声明了没人读的objectName+recordId,四个 block 在真实记录页上渲染空白,全程门禁皆绿——因为框架侧那道门禁比的是两份声明(objectstack#4472),而 binding-reach 探针根本看不见这个家族。在本 PR 之前,"这家族在记录页下能工作"是个没有任何行为证据的假设。探针问什么
两道探针的行为覆盖 14 → 24 / 57(
record:related_list两边都在:binding-reach 把它记成"没有父记录就不该取数",本探针是第一个真让它取上数的地方——那条台账理由从声明变成了核过的事实)。几个刻意的设计决定
这道门禁很容易写成它自己要消灭的东西,所以:
React.useId整体偏移,于是每个块都"有差异"——探针自己制造绿。record:*家族与非 objectName 绑定的行为证据——binding-reach 覆盖 14→24/57,#4413 的原始形态不再是假设 #3149 记下不覆盖展示原语的理由。在这里加一道同样的东西是自毁。sections的通用样本值把record:details送进过这个陷阱。fetch('/api/v1/security/explain'),happy-dom 下相对路径打到localhost:3000——探针原本"能跑"只是因为连接被拒,每跑刷 24 行 ECONNREFUSED,谁本地起了 3000 端口结果就不一样。现在立即 reject,URL 进同一份调用台账(走裸 fetch 绑定记录的块照样算数)。结果
8 个响应绑定记录:details / highlights / related_list / path / line_items / history / quick_actions / alert。
3 个入
NO_RECORD_REACH台账,每条必须写明宿主路径——因为"宿主供数"只有在真有宿主在供的时候才是理由:record:discussionRecordDetailView挂record:reference_railentries由buildDefaultPageSchema注入,不是作者输入record:activityitems={[]}写死,没有任何路径在供数抓到的两个缺陷
1.
record:activity的 feed 结构上不可能有内容 → #3165(本 PR 只记台账,不修)useRecordContext()调了就丢,<RecordActivityTimeline items={[] as any}>写死;items是 prop,全文件没有任何取数;buildDefaultPageSchema:614发出的节点不带 props,而触发它的options.showActivity在生产代码里从没被设成true(每个调用点传的都是{ slots }或什么都不传);它自己的文件头写着 "Real data wiring (sys_activity / sys_comment polling) lives inside that component"——不在。那一句就是缺陷本身。
不在本 PR 修,是因为修法是个功能(镜像同目录
record:history已有的sys_activity自取数),不是 #3144 那种一行桥接。台账是双向断言的:它一旦开始响应记录,测试就会失败直到条目被删,所以销账不会忘。2.
useMetadata()的"优雅兜底"是个无限渲染循环 → 本 PR 修掉它每次调用都新建兜底对象,于是 provider 外每渲染一次
getItem就换一个身份;useMetadataItem把getItem放在 effect deps 里、并在无 name 分支setState一个全新对象 → effect 重跑 → 新 state 对象 → 重渲染 → 新身份,同步到能把render()卡住。注释里写它存在的理由是"让 provider 外的单测能正常渲染"——它恰恰让那些消费者根本挂不起来。
record:alert和record:quick_actions都无条件调useMetadataItem,各自打满一个核、涨到 8.6 GB 而一条断言都没跑到。两处改动,一处治本一处兜底:兜底对象改成冻结的模块级单例(身份稳定);清空分支在已清空时直接 bail,覆盖同一个循环换条路来的情况——任何每渲染重建 context value 的调用方,而这个接口的注释是明确邀请这种写法的("hand-rolled context values in tests keep working")。
第 2 层:原方案不存在,落地的是另一个
原提议是"
recordId在object-form/object-master-detail-form/embeddable-form三个块上"。这三个块都没声明recordId——57 个 public block 里没有任何一个声明。 全仓库只有view:detail和detail-view两处recordId/resourceId输入,都不 public。这和 objectstack#4472 方向 (d) 是同一类结果:一个从声明出发提的切片,等真去看声明的时候发现它不在。代之以同机制、同成本、但确实存在的切片:
record:related_list和record:line_items各自把 required 的relationshipField+childObject绑进子查询,且只有在 record context 下才观察得到。断言两次find()都带上声明的子对象名和关系字段,并 scope 到当前绑定的父记录 id。两个都成立:"同机制换断言"这个假设成立,成本是在本来就要发生的挂载上多加一条断言。是否继续外扩仍建议谨慎——噪声比现在是 2 个真缺陷 : 5 个探针自造假信号(第 3a 层新贡献了
sections那个崩溃)。边界
到达数据层 / 记录到达输出 ≠ 渲染正确。这条链上仍然没有任何环节看渲染结果(ADR-0082 addendum 已写明)。本 PR 的范围是"绑定接没接上"。
规格侧不动:ADR-0082 D1(spec 是协议)和 #3729 已经定过——
RecordActivityProps声明 11 个属性是协议正确,运行时跟不上是运行时的欠账,不是把协议削平的理由。验证
record-block-record-reach.test.tsx:13 passed,~27s,0 次外网连接record:activity的台账条目 → 测试失败并指名 objectstack#4413 的形状;说明这道门禁真的在检查东西vitest run(AGENTS.md [WIP] Enhance every detail of the designer #10,已在origin/maina889e31 上重跑):800 个文件 / 9327 passed / 29 skipped,exit 0变更
apps/console/src/__tests__/record-block-record-reach.test.tsxpackages/react/src/context/AppShellContext.tsxuseMetadata()兜底冻结成单例;useMetadataItem清空分支 bail.changeset/record-block-record-reach.md/.changeset/metadata-fallback-render-loop.mdCloses #3149