Skip to content

fix(plugin-detail): 相关列表的 Add 门改问 add.picker.object,漏写 picker 不再把整块换成红卡 (#3838) - #3894

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3838-relatedlist-picker-guard
Aug 9, 2026
Merged

fix(plugin-detail): 相关列表的 Add 门改问 add.picker.object,漏写 picker 不再把整块换成红卡 (#3838)#3894
yinlianghui merged 1 commit into
mainfrom
claude/issue-3838-relatedlist-picker-guard

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3838

前提复核(先证后改)

  • 崩溃点确认在 :1299,按内容锚定复核过:grep -n "add\.picker" packages/plugin-detail/src/RelatedList.tsx 只有 :724 / :734 / :1299 三处裸取,前两处在 handleAddRecords 回调内、:723if (!add || !dataSource || …) return 前置,不在渲染路径 —— 未动
  • spec 侧确认 picker 必填:RecordRelatedListProps.safeParse({ objectName, relationshipField, add: { label: 'Add' } })invalid_type on path ["add","picker"]。也就是说触发者只能是不合规元数据;而渲染路径上没有任何一处解析 schema,所以它一路抵达裸取。
  • RelatedListProps.add.picker 在 TS 里被声明为必填(RelatedList.tsx:84)—— 这正是裸取当年能通过 type-check 的原因:类型声明可信,运行时拿到的是未校验 JSON。
  • 崩溃在钉子测试里实测复现(改代码前跑,见下「反向验证」的 before 一栏)。

改了什么

方向按分诊缺省 (a) 收紧门 + (b) 具名诊断:

  1. Add 按钮(:1121)与 picker 对话框(:1293)的门统一收紧add && pickerObject,:1299 改用文件里已有的 pickerObject(即 add?.picker?.object,原本就为 picker schema 预取而算过一次)。
    • 结果是渲染路径上最后一处 add.picker 裸取消失,而不是给它加可选链 —— 后者是消费端宽容(AGENTS.md #0.1),会让缺 pickeradd 变成一种"能用"的第二方言。现在它仍旧什么都不做,只是从「炸整块」变成「Add affordance 不渲染」,与同一路径上 :378/:390 既有的可选链同向。
    • 两处门一起收是必要的:只收 :1293 会留下一个点了没有对话框可开的死按钮。
  2. 具名诊断:缺 add.picker.objectconsole.warn 点名缺失的键(旧的报错文案里根本没有 picker 三个字)。
    • 诊断形态的取舍:renderers/record-related-list.tsx 的整块 dashed placeholder 先例,语义是「这个块什么都渲染不出来」(缺 objectName → 没有任何数据可取;同族的 record:path — no stagesrecord:quick_actions — no actions 同理)。这里不是那个情形 —— 列表主体、计数、分页、行操作全都正常,只有 Add 一个 affordance 缺配置,把一个能用的列表换成一行虚线提示是净损失。
    • 因此照的是同一文件里针对局部误配的开发者提示先例(:405-416,no referenceField/parentId — refusing to fetch all rows):同样是"降级一个行为、点名一个键"的 console.warn,放在 effect 里(每次挂载/配置变化告警一次,不随每次渲染刷屏)。
  3. 消费半径注释同步:apps/console 两个 reach 探针里把该崩溃与旧门形状写成现存事实的注释更正为过去时(parity 门的反方向没推到全仓:7 个 block 共 15 个 spec 已声明的顶层键没有任何 inputs 发布,其中 3 个渲染器实读(record:details.hideFields / record:related_list.relationshipValueField / element:text_input.defaultValue) #3808 留下的历史说明保留)。只改注释,fixture 样本与断言一行未动 —— add: { picker: { object } } 本来就是 spec 合法样本,与本 PR 无关地成立。

反向验证(方向先判后跑)

预判(写在跑之前):只还原 :1293 的旧门与裸取、保留新加的 warn effect 与收紧后的按钮门 → 缺 picker 的钉子在同一个 TypeError 上,且具名诊断依然不出现(render 先抛,effect 根本轮不到 flush),合法样本两例保持绿。

缺 picker 钉子 合法 add 无 add
改前(origin/main 原样) TypeError: Cannot read properties of undefined (reading 'object') @ RelatedList.tsx:1299:34
改后
单独还原 :1293(保留 warn) ❌ 同一个 TypeError @ 还原后的 :1331:34,warn 从未触发

预判与实测一致。第三行是这次真正的信息量:门才是承重的那条腿 —— warn effect 单独存在时是装饰性的,因为抛异常发生在 effect flush 之前;所以 (b) 必须叠在 (a) 上,不能替代它。

测试

  • pnpm exec vitest run packages/plugin-detail/src/__tests__/RelatedList.addPickerGuard.test.tsx → 3 passed(新钉子)
  • pnpm exec vitest run packages/plugin-detail60 files / 493 tests passed(消费半径:plugin-detail 全量)
  • pnpm exec vitest run apps/console/src/__tests__/record-block-record-reach.test.tsx apps/console/src/__tests__/public-block-binding-reach.test.tsx → 2 files / 29 passed
  • pnpm exec turbo run type-check --concurrency=278 successful, 78 total
  • pnpm run check:control-bytes → OK(3780 tracked files)
  • 全部重活跑在仓根 flock /tmp/os-heavy-verify.lock + NODE_OPTIONS=--max-old-space-size=4096 + --maxWorkers=2

不在本单

基线:origin/main @ 47f60785474ca68889a376bd0dff7aedec5d2725


Generated by Claude Code

…红卡 (#3838)

`record:related_list` 写了 `add` 却漏了 `add.picker` 时,`RelatedList.tsx:1293`
的门只判 `add` 真值,紧接着 `:1299` 裸取 `add.picker.object` 抛
TypeError,`SchemaRenderer` 捕获后把**整个相关列表**换成
「Component "record:related_list" failed to render」错误卡 —— 报错文案里
没有 `picker` 三个字。`picker` 在 spec(`RecordRelatedListProps.add`)里是必填,
`RelatedListProps` 也把它声明为必填(这正是裸取能通过 TS 的原因),但渲染路径上
没有任何一处解析 schema(sdui-parser 的 manifest 门只比顶层键名与粗类型),
所以不合规元数据一路抵达裸取。

- Add 按钮(`:1121`)与 picker 对话框(`:1293`)的门统一收紧为
  `add && pickerObject`,`:1299` 改用文件里已有的 `pickerObject`
  (即 `add?.picker?.object`)—— 渲染路径上最后一处 `add.picker` 裸取消失,
  而不是给它加可选链:缺 `picker` 的 `add` 仍旧什么都不做,不产生第二方言
  (AGENTS.md #0.1)。与 `:378`/`:390` 既有可选链同向。
- 具名诊断:缺 `add.picker.object` 时按文件内既有的「no referenceField/parentId」
  开发者提示先例,console.warn 点名缺失的键。`renderers/record-related-list.tsx`
  的整块 placeholder 先例适用于「整块无可渲染」(缺 objectName),这里列表主体
  完好、只有 Add 未配置,故不套用。
- 钉子测试三例:缺 picker → 主体照常渲染 + 无 Add + 诊断点名;合法 add →
  Add 照常且打开 picker 目标;无 add → 现状不变且不告警。
- apps/console 两个 reach 探针里描述该崩溃与旧门形状的注释同步更正(仅注释,
  样本与断言未动)。

(c) 产出端(save-gate / `os build`)真正跑一次 spec 解析仍是最正的方向,不在本单。
@vercel

vercel Bot commented Aug 9, 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)
objectui Ignored Ignored Aug 9, 2026 12:14am

Request Review

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-BlHIpaPf.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 482.39KB 106.34KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 139.61KB 35.99KB
fields (index.js) 230.82KB 56.70KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 117.21KB 30.27KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 236.49KB 58.96KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 112.10KB 27.10KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 187.63KB 49.66KB
plugin-kanban (index.js) 48.30KB 13.28KB
plugin-list (index.js) 105.12KB 25.48KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

✅ 验收(PM,session session_01GTRjn8xBqp75dk7kFupVRt)

实物核验:头 054b6b112,5 文件 +215/−11;RelatedList diff 全读 —— Add 按钮与 picker 对话框两门齐收add && pickerObject(只收一处会留死按钮的推理成立)、:1299 裸取换用已有 pickerObject 而非加可选链(缺 picker 的 add 仍什么都不做,无第二方言,AGENTS.md #0.1);trailer 0;reach 探针只动注释、#3808 的 fixture 与断言一行未动。
CI 终态(独立复核):20 检查全部 completed,18 success + 2 skipped,零失败。

裁定要点:

  • (b) 诊断形态判断正确:先例分辨(整块 placeholder = 「整块无可渲染」;这里列表主体全正常)→ 选同文件局部误配的 console.warn 先例,消息点名 add.picker.object 并给修法 —— 与派发词的判断点完全吻合。
  • 反向验证的额外发现有分量:「只留 warn 不收门 → render 先抛、effect 轮不到 flush、warn 从未触发」—— 用实验证明门是承重腿、warn 单独存在是装饰,(b) 只能叠加不能替代。
  • 修前钉子在 origin/main 原样实测复现崩溃于 :1299:34(复现先行);spec 侧 safeParse 复核确认只有不合规元数据可触发;消费半径 493 + 31 用例全绿。

转 ready 并挂 auto-merge。越界 #3895(Add 按钮不判 dataSource 的产品取舍:补门 vs disabled+说明)归分诊席。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 9, 2026 00:22
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit acc34c5 Aug 9, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3838-relatedlist-picker-guard branch August 9, 2026 00:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants