Skip to content

fix(components): 记录页头的动作谓词改走 CEL,与行菜单/批量栏同一入口 (#3521) - #3791

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-3521-header-predicate-cel
Aug 8, 2026
Merged

fix(components): 记录页头的动作谓词改走 CEL,与行菜单/批量栏同一入口 (#3521)#3791
yinlianghui merged 2 commits into
mainfrom
claude/issue-3521-header-predicate-cel

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3521

问题:同一条元数据,两种方言

记录页头(page:header)的动作谓词一直交给 objectui 的遗留 JS 求值器 —— packages/components/src/renderers/layout/containers.tsxnew ExpressionEvaluator(evalCtx) + evaluator.evaluateExpression(src) 的两处(visible/hiddenevalExpr,以及 disableddEvaluator)。而行 kebab、批量选择栏、条件格式自 #1584 / ADR-0058 起统一走 evalRowPredicate:裸字符串即 CEL —— 这正是 @objectstack/specActionSchema.visible 的类型,也是服务端执行的方言。

于是每一个 CEL 独有构造在列表里对、在详情页头抛错,而抛错是 fail-closed:按钮直接消失,页面上没有任何痕迹。

前后对照

谓词 修复前(页头) 修复后(页头) 行 kebab / 批量栏(一直如此)
record.f_tags.size() > 0 抛错 → 静默隐藏 正常求值 正常求值
record.f_textarea.contains("x") 抛错 → 静默隐藏 正常求值 正常求值
'"red" in record.f_multiselect' 解析失败(position 6)→ 隐藏 正常求值 正常求值
record.f_date < today() today is not a function → 隐藏 正常求值 正常求值
{ dialect: 'cel', source: '…' } 信封 拆出 .source 后仍按 JS 求值 权威 CEL 权威 CEL
${record.f_status === "open"} evaluateExpression 求不出模板 → 恒隐藏 经遗留引擎正确求值 经遗留引擎正确求值
record.f_status === "open" 遗留引擎(结论正确) 遗留引擎(结论不变)+ deprecation 警告

改动落在同一处的方言这一半 —— #3501 刚落的绑定那一半(toPredicateRecord)完整保留:关系字段仍按服务端存的外键绑定,只是现在由 evalRowPredicatefields 选项统一完成(record.* / 裸字段 / data.* 三路绑定都过同一次归一化),页头本地那份只留给引擎不构造的 ctx.* 命名空间。展示不受影响(标题仍读原始 ctx.data,关系字段照常显示名称)。

谓词现在整体传入 evalRowPredicate(不再拆出 .source),所以 { dialect: 'cel' } 信封不会被拍平成方言猜测 —— 与行 kebab 直接传 def.visible 的规则逐字一致。

三面 parity 证据

packages/plugin-grid/src/__tests__/predicate-surface-parity.test.tsx(plugin-grid 是唯一能同时看见三面的包:它拥有行 kebab 的 isCustomRowActionVisible 与批量栏的 partitionBulkRows,并依赖 @object-ui/components 拿到 page:header)。16 个 case(16 passed),每个 case 断言三面结论逐字相等:

✓ 'method call `.size()` — true' / '— false'
✓ 'method call `.contains()` — true'
✓ '`in` operator — true' / '— false'
✓ 'stdlib `today()` — true' / '— false'
✓ 'CEL envelope'
✓ 'relation bound as its foreign key (expanded payload)' / '(unexpanded payload)' / 'on someone else's record'
✓ 'bare field shorthand' / '`data.*` binding'
✓ 'legacy `${…}` template — true' / 'legacy `===` — false'
✓ 'faulting predicate fails closed'

反向验证(方向预先声明为「改动前红」):只回退 containers.tsx、保留两个新测试文件后重跑,20 个断言转红,红的正是页头列:

× 'method call `.size()` — true'   AssertionError: expected { rowKebab: true, …(2) } to deeply equal { rowKebab: true, …(2) }
× '`in` operator — true'           (同上,recordHeader: false)
× 'stdlib `today()` — true'
× 'CEL envelope'
× 'relation bound as its foreign key (expanded / unexpanded payload)'
× 'legacy `${…}` template — true'
× applies CEL to `hidden`, the mirror predicate      expected true to be false
× applies CEL to `disabled` …                        (按钮未 disabled)

两处如实记录,免得后人误读:

  1. CEL-false 的 case 在改动前也是绿的 —— 因为「抛错 → fail-closed 隐藏」和「求值为 false → 隐藏」看起来一样。所以每类构造都钉了双向,红的那一半才是证据。
  2. ${…} 模板在改动前也是红的:老页头调的是 evaluateExpression,它拿不到 ${…} 模板,按钮恒隐藏。也就是说遗留方言这条路不但没回归,${…} 反而从「求不出」变成「经回退正确求值」。

遗留方言不回归

packages/components/src/__tests__/page-header-predicate-dialect.test.tsx 里单列一组:${…} 模板、裸 ===、JS-only 方法 .includes()、遗留 disabled,全部仍走遗留引擎并保留既有 deprecation 警告(isLegacyDialectSource 兜底,未动 spec)。

fail-closed 与 warn-once 也逐条钉住:故障 visible 仍隐藏、故障 disabled 仍保持可用、故障 hidden 仍渲染(都是历史方向,由 fallback: false 保住)。

诊断措辞的一处变化(有意)

故障报告改由 evalRowPredicate 自己的 warn-once 机制写出,label 带 page:header action "…" visible,与行 kebab / 批量栏逐字同一措辞 —— 页头原来那条 [page:header] … its predicate threw 的本地实现随之删除。#2358 要的事实一件没少(隐藏的按钮报自己的名字、引用自己的谓词、只报一次),但引擎给的 reason 文本不再回显。页头本地只保留「谓词引用了 payload 里不存在的字段」这一条 —— 那是引擎看不到的成因(服务端会把 hidden: true 字段从详情 payload 里剥掉)。

因此 page-header-actions.test.tsx 里两条断言按新事实更新:措辞从 predicate threw 改为 failed to evaluate(仍断言含 page:header、动作名、谓词原文、去重一次);trap 3 从「一共一条警告」改为「成因一条 + 判决一条,各一次」—— 遗留引擎对缺席键返回 undefined 不抛错,所以以前只有第一条,CEL 对缺席键是 fault,判决那条是新增的信号。

验证

pnpm vitest run packages/components packages/plugin-detail packages/react   → 182 files, 1583 passed
pnpm vitest run packages/plugin-grid packages/core/src/evaluator            →  65 files,  784 passed
pnpm vitest run packages/app-shell/src/views                               → 184 files, 1691 passed, 1 skipped
pnpm --filter @object-ui/components type-check                             → tsc --noEmit + typetests 通过
pnpm --filter @object-ui/plugin-grid type-check                            → 通过
eslint(4 个改动文件)                                                      → 0 errors(仅既有 no-explicit-any warning)
node scripts/check-control-bytes.mjs                                       → OK(3721 tracked files)
新增两个文件单跑                                                            → parity 16 passed / dialect 26 passed

消费半径已按调用方清扫:page:header 的渲染方(components 自身测试、app-shell 的 RecordDetailView.*、plugin-detail 的 header 合成)全跑过;仓内没有别的 record_header 动作谓词夹具使用遗留语法(已 grep 确认)。

两个新测试文件按约定登记进 vitest.config.mtsheavyDomTests(都经 ComponentRegistry 渲染)。

⛔ 未扩进 objectstack#5970(spec 侧 ActionSchema.visible 统一):isLegacyDialectSource 回退在不动 spec 的前提下完整保住了,本单不需要 spec 变更。


Generated by Claude Code

`page:header` 的 `visible` / `hidden` / `disabled` 一直交给 objectui 的遗留 JS
求值器(`new ExpressionEvaluator(evalCtx)` + `evaluateExpression(src)` 两处),
而行 kebab、批量栏、条件格式自 #1584 / ADR-0058 起统一走 `evalRowPredicate`
(裸字符串即 CEL —— 这正是 `@objectstack/spec` 给 `ActionSchema.visible` 的类型,
也是服务端执行的方言)。于是每一个 CEL 独有构造在列表里对、在详情页头抛错,
而抛错是 fail-closed:按钮直接消失,页面上没有任何痕迹。

- 方法调用:`record.f_tags.size() > 0`、`record.f_textarea.contains("x")`
- `in` 运算符:`'"red" in record.f_multiselect'`(解析失败)
- stdlib 函数:`record.f_date < today()`(`today is not a function`)

两处求值改走 `evalRowPredicate`:同一入口、同一套绑定(`record.*` + 裸字段 +
`data.*` + 宿主 scope,关系字段按 #3501 折回外键)、同一套 fail-closed +
warn-once。谓词整体传入(不再拆出 `.source`),所以 `{ dialect: 'cel' }` 信封
保持权威 CEL,与行 kebab 传 `def.visible` 的规则一致。

遗留方言不回归:`${…}`、`===`/`!==`、`?.`、`??`、`.includes()` 等仍由
`isLegacyDialectSource` 兜到遗留引擎(保留既有 deprecation 警告)。`${…}` 模板
此前在页头根本求不出值(`evaluateExpression` 拿不到模板)、按钮恒隐藏,现在经
回退正确求值。

诊断:故障报告改由 `evalRowPredicate` 的 warn-once 机制写出,label 带上
`page:header action "…" visible`,与行 kebab/批量栏逐字同一措辞;页头本地只保留
「谓词引用了 payload 里不存在的字段」这一条(引擎看不到的成因)。

测试:
- `page-header-predicate-dialect.test.tsx` —— 三类 CEL 构造双向求值、信封、
  `hidden`/`disabled`、裸字段/`data.*`/`os.user.*` 绑定、#3501 关系绑定、
  遗留方言回退、fail-closed + warn-once。
- `plugin-grid/__tests__/predicate-surface-parity.test.tsx` —— 同一谓词 + 同一
  记录,在行 kebab(`isCustomRowActionVisible`)、批量栏(`partitionBulkRows`)、
  记录页头三处给出同一结论(17 例)。
@vercel

vercel Bot commented Aug 8, 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 8, 2026 4:19pm

Request Review

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-BqI7mC-6.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) 481.72KB 106.06KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 9.85KB 3.18KB
data-objectstack (index.js) 139.51KB 35.97KB
fields (index.js) 230.90KB 56.84KB
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.09KB 42.72KB
plugin-dashboard (index.js) 117.06KB 30.24KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 232.81KB 57.42KB
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.71KB 49.68KB
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

勘误(不值得为它 force-push,commit message 留原样):提交正文末尾写的 parity 用例数「17 例」是笔误,实际是 16 例(pnpm vitest run packages/plugin-grid/src/__tests__/predicate-surface-parity.test.tsxTests 16 passed (16))。PR 正文里的数字是对的。

另外两条在实施本单时发现、与本 PR 改动无关的观察已单独归档(finding,未排期、未认领,不在本 PR 修):


Generated by Claude Code


Generated by Claude Code

issue 表格里点名的第三个方法调用。它也是离遗留标记最近的一个:`.match(` 是
legacy marker,`.matches(` 故意不是(marker 要求 `match` 后紧跟 `(`),所以必须
落到 CEL 引擎 —— 正反两向各钉一条,免得「不再隐藏」被当成「求值正确」。

夹具补 `f_email`(showcase field-zoo 里同名字段的形状)。

pnpm vitest run packages/components/src/__tests__/page-header-predicate-dialect.test.tsx
  → Tests 28 passed (28)
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-BqI7mC-6.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) 481.72KB 106.06KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 9.85KB 3.18KB
data-objectstack (index.js) 139.51KB 35.97KB
fields (index.js) 230.90KB 56.84KB
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.09KB 42.72KB
plugin-dashboard (index.js) 117.06KB 30.24KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 232.81KB 57.42KB
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.71KB 49.68KB
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

✅ 验收通过(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt)—— undraft + auto-merge。

git 实物核验(d4630195f):净 diff 6 文件;页头 visible/hidden/disabled 两处求值合并为 evalHeaderPredicate = evalRowPredicate(…, {fallback:false, warnOnError:true}),与行 kebab/批量栏逐字同一入口同一选项 —— 正是分诊席验收锚点要的收敛修复而非补函数;new ExpressionEvaluator 在页头路径移除。#3501 的绑定半完整保留(其钉子测试未改一行仍绿);isLegacyDialectSource 回退未动 spec,停手条件未触发。

验证质量:28 条方言单测(CEL 构造双向 + 信封 + 绑定)+ 16 条三面 parity(行菜单/批量栏/页头逐字相等),反向验证预判「改动前红」实测 20 条红且全在页头列;两处诚实记录尤佳 —— CEL-假用例改动前也绿(抛错隐藏与求值假隐藏外观同形,故每类钉双向)、${…} 模板在页头旧路径本就求不出值(遗留侧是改善而非回归)。visible:false 行/页头分歧刻意排除并注明归 #3758,边界正确。

风险裁定(四条均采纳 dev 处置):诊断措辞变化已归档 #3792;非布尔真值收紧与非 cel 信封按 CEL 求值均为向行面既有规则收敛,仓内无受影响夹具;宿主 scope record 优先级边缘已归档 #3796。commit 正文「17 例」笔误经 PR 评论勘误(禁 force-push,处置合规)。18 项 CI 全部完成 0 失败;changeset patch 三门绿。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 8, 2026 16:35
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 8, 2026
Merged via the queue into main with commit e24d767 Aug 8, 2026
18 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3521-header-predicate-cel branch August 8, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

记录页头的动作谓词走遗留 JS 求值器而非 CEL:.size() / .contains() / in / today() 在详情页一律抛错并静默隐藏按钮

2 participants