Skip to content

fix(plugin-grid): ObjectGrid 的 editable 键补 can(object,'update') 门 (#5143) - #5147

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-5143-objectgrid-editable-gate
Aug 18, 2026
Merged

fix(plugin-grid): ObjectGrid 的 editable 键补 can(object,'update') 门 (#5143)#5147
yinlianghui merged 2 commits into
mainfrom
claude/issue-5143-objectgrid-editable-gate

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #5143

问题

ObjectGrid 早已算出当前 principal 的写判定 —— permissionUpdate = perms.can(objectName, 'update') —— 却只把它喂给行 kebab(resolveRowCrudAffordances)。三处行内编辑 props 读的是 schema 原值:

  • editable: schema.editable ?? false
  • renderCellEditor: schema.editable ? … : undefined
  • rowActions: !!(schema.editable && hasActions)

于是同一个组件对「该用户可否写这些记录」给出两个相反答案:无 update 授权的 principal 打开一张 grid 块声明 editable: true 的 SDUI 页面,点单元格即进入编辑器,只能靠服务端 403 拦住;而同一批行的 kebab 正确隐藏了 Edit。没有数据落库(服务端门是牢的),代价是 UI 明知会失败还把用户走完一遍往返。

卡面前提三条已逐条实测复核,全部成立(复核记录见 #5143 评论)。

#4647 / PR #5145 的关系

#4647ListView 层关上了同一个洞(PR #5145 已落 main,本 PR 基线 dfc697554):开关、紧凑工具栏入口、以及 ListView 写进 grid schema 的 editable 值,都 AND 了 isObjectInlineEditable(objectDef, effectiveApiOps) && can(objectName, 'update')

声明式编写的 object-grid schema 是不经过 ListView 的第二扇门。本 PR 用同构的合取式把它关上,两扇门因此无从漂移。

改动

新增一个已解析的判定,三处 props 共读:

const objectInlineEditable =
  isObjectInlineEditable(objectSchema, effectiveApiOps) &&
  (objectName ? perms.can(objectName, 'update') : true);
const inlineEditable = (schema.editable ?? false) && objectInlineEditable;

作者声明的键留在合取式左半,所以本门只收紧、不放宽:任何判定都无法为「没要求可编辑」的 grid 打开编辑。

rowActions 随同一判定(卡面待核点的定夺)

单独核实后确认应当随同,理由不是顺手扩面:该列唯一会被填充的状态,就是行有 pending change 时的保存/取消按钮对。ObjectGrid 从不把 onRowEdit / onRowDelete / rowActionDefs 传给 DataTable 的内建菜单(它们走 columnsWithActions 另一列),所以 DataTableRowActionsMenu 在这里每行都渲染 null。只关编辑而留下该列,无授权用户会拿到一条永远空的尾列加表头 —— 一种今天并不存在的 grid 形态(不带 editable 的 schema 今天就不生成该列)。跟随同一判定,才使被收紧后的 grid 与「本就不可编辑的 grid」逐列一致。测试 (e) 正是钉这一条:editableNoGrant === neverEditable,并以 editableWithGrant === neverEditable + 1 作对照证明探针确实能分辨两种形态。

fail-open 语义(与同文件兄弟门一致)

  • PermissionProvidercan()true —— standalone embed、Studio 设计器画布行为不变;
  • 无 objectName 的纯内联 data grid ⇒ isObjectInlineEditable(null) 解析到默认可写桶,且 can() 根本不会被调用。

收紧只在「确实存在一个对象可供判定」时才生效。

反向验证(预判先写,commit 后变异还原)

(a) 摘门 —— 三处 props 还原为读 schema 原值。预判 7 红 / 4 绿(逐条点名),实测完全一致:红 = a / d / e / engine-owned / userActions.edit: false / effectiveOps 缺 update / opt-in 敌不过缺授权;绿 = b(有授权)/ c(无 Provider)/ narrows-only / 纯内联 grid。

(b) 塞假 verdict —— objectInlineEditable 强制为 false。预判 6 红 / 5 绿,实测完全一致:红 = b / c / d / e(对照半)/ effectiveOps 含 update 的后半 / 纯内联 grid;绿 = a / engine-owned / userActions.edit: false / opt-in / narrows-only。两个方向都可观测,证明钉子不是「因为什么都没产出」而绿。

(c) 自选方向:两条通道在门后的合成语义。 ListView 把 editable: inlineEdit && inlineEditOffered 写进 grid schema,所以 ObjectGrid 的 schema.editable 已被同一合取式预先 AND 过一次,本门再 AND 一次。

  • 预判 c1「幂等」:A ∧ B 再 ∧ B = A ∧ B,ListView 路径行为不变 —— 实测符合(ListView 说 false 仍 false,说 true 且有授权仍 true;plugin-list 全量 + ListView.permissions.test.tsx 全绿)。

  • 预判 c2(非显然的一半)两门并非字面同一谓词:ObjectGrid 解析 objectName = dataConfig.object ?? schema.objectName,ListView 只读 schema.objectName。故当对象身份data: { provider: 'object', object: X } 传入、无顶层 objectName 时,ListView 的 schema.objectName ? … : true 分支会落开,而 ObjectGrid 这门有对象可判 —— 预判严格更强。实测证实:该形态下无授权 ⇒ 不进编辑态(can(X,'update') 确被调用),有授权 ⇒ 照常。也就是说本门在 dataConfig 寻址的 grid 上是唯一的门,不是 ListView 那道的冗余副本。

    这条非显然事实已留成第二个 commit 的钉子(而非只写进正文):变异验证把 objectName 收窄回 schema.objectName,预判「仅此一例红」,实测恰好 1 红 11 绿 —— 钉子既咬得住又不误伤。同一 commit 另订正了首个 commit 里一句过强的注释(原写「两门是同一谓词」,与此处实测相抵触)。

测试

新增 packages/plugin-grid/src/__tests__/inlineEditPermissionGate.test.tsx(12 例),断言用户可见结果(点单元格是否出现编辑器 / 表头列数 / kebab 条目),而非产生它的 prop。kebab 两例与编辑两例刻意同文件:两个 affordance 的分歧本身就是本卡的缺陷,任何一半被重新打开都必须在这里红。

  • pnpm exec vitest run packages/plugin-grid ⇒ 76 files / 680 tests 全绿
  • plugin-list + plugin-detail ⇒ 128 files / 1451 tests 全绿
  • plugin-view + react(spec-bridge / element-data-source)+ plugin-tree ⇒ 21 files / 252 tests 全绿
  • pnpm type-check ⇒ 81/81
  • eslint 变更文件 ⇒ 0 errors
  • pnpm check:control-bytes ⇒ OK;另按 [\x00-\x08\x0b\x0c\x0e-\x1f] 自扫变更文件 ⇒ 无命中
  • CI:20 项检查全部终态,无失败(4 个 test shard / Type Check / Lint / Build & E2E / Control Byte Scan / changeset 三关等)

半径外发现

#5148 —— showAddRow 骑在作者声明的 operations.create 上、无 can(object,'create') 门(同族的 create 面)。已按 observation-class 挂 finding、不挂 pm:queue、不指派,可达性在卡面写明,留给 triage 定级。本 PR 不动它。

同族

)

#4647 在 **ListView** 层关上了行内编辑的权限缺口(PR #5145):开关、紧凑
工具栏入口、以及 ListView 下发的 `editable` 值,三者都 AND 了对象的已解析
可编辑性与 `can(obj, 'update')`。

`ObjectGrid` 另有一扇**不经过 ListView** 的门:声明式编写的 `object-grid`
schema 直接带 `editable: true`。组件其实早已算出该 principal 的判定
—— `permissionUpdate = can(objectName, 'update')`,就在上方几行 —— 却只
喂给行 kebab;三处行内编辑 props 读的是 schema 原值。于是同一个组件对
「该用户可否写这些记录」给出两个相反答案:kebab 正确隐藏 Edit,而点一下
单元格却把人放进编辑器,只能靠服务端 403 拦住。没有数据落库(服务端门是
牢的),代价是 UI 明知会失败还把用户走完一遍往返。

现在 `editable` / `renderCellEditor` / 承载保存取消列的 `rowActions` 读同
一个判定:作者声明的键 ∧ 对象已解析的可编辑性(ADR-0103 桶、
`userActions.edit`、服务端 effective API operations)∧ principal 自己的
授权。与 #4647 用的合取式逐字同构,两扇门因此无从漂移。

`rowActions` 随同一判定,是本 PR 单独核实后的定夺,不是顺手扩面:该列
**唯一**会被填充的状态就是行有 pending change 时的保存/取消对,而
ObjectGrid 从不把 `onRowEdit`/`onRowDelete`/`rowActionDefs` 传给 DataTable
的内建菜单(它们走 `columnsWithActions`),所以 `DataTableRowActionsMenu`
在这里每行都渲染 `null`。只关编辑而留下该列,无授权用户会拿到一条永远空
的尾列加表头 —— 一种今天并不存在的 grid 形态(不带 `editable` 的 schema
今天就不生成该列)。跟随同一判定,才使被门收紧后的 grid 与「本就不可编辑
的 grid」逐列一致。

fail-open 与本文件每个兄弟门一致:无 `PermissionProvider` 时 `can()` 答
`true`,无 objectName 的纯内联 data grid 解析到默认可写桶 —— standalone
embed、Studio 设计器画布、无对象语义的内联表格行为不变。收紧只在「确实
存在一个对象可供判定」时才生效。

行为变化(如实记录):无 `update` 授权的 principal 在此类 grid 上不再进入
行内编辑,也不再看到为其服务的保存/取消列。有授权者不受影响。

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
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) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.02KB 0.88KB
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) 502.20KB 112.21KB
core (index.js) 4.11KB 1.62KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 159.03KB 44.08KB
fields (index.js) 234.25KB 58.48KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
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.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 241.46KB 60.56KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 123.33KB 29.93KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 198.05KB 53.21KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.39KB 27.03KB
plugin-map (index.js) 18.72KB 6.09KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.66KB 3.50KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 31.55KB 10.70KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (index.js) 4.77KB 2.16KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 6.92KB 2.40KB
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 (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 3.05KB 1.52KB
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

反向验证 (c) 查出一条非显然事实,值得留成钉子而不是只写进 PR 正文:
本门与 #4647(PR #5145)ListView 那道**并非字面同一谓词**。ObjectGrid 解析
`objectName = dataConfig.object ?? schema.objectName`,ListView 的
`inlineEditOffered` 只读 `schema.objectName`。因此当对象身份**仅**经
`data: { provider: 'object', object: … }` 传入、无顶层 `objectName` 时,
ListView 的 `schema.objectName ? … : true` 分支会落开,该形态**只由本门**
判定 —— 本门不是 ListView 那道的冗余副本。

新增钉子覆盖该形态(无授权 ⇒ 不进编辑态;有授权 ⇒ 照常)。变异验证:把
`objectName` 收窄回 `schema.objectName`,预判「仅此一例红」,实测恰好 1 红
11 绿 —— 钉子既咬得住又不误伤。

同时订正上一次提交里一句过强的注释:原文写「两门是同一谓词」,与刚验证出
的差异相抵触。改为如实说明幂等只成立于 ListView 路径,以及差异朝安全方向
跑。「上游已经设过门了」正是当初留下这扇门的推理,不该由注释再复述一遍。

纯注释 + 测试,产物行为不变。

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
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) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.02KB 0.88KB
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) 502.20KB 112.21KB
core (index.js) 4.11KB 1.62KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 159.03KB 44.08KB
fields (index.js) 234.25KB 58.48KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
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.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 241.46KB 60.56KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 123.33KB 29.93KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 198.05KB 53.21KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.39KB 27.03KB
plugin-map (index.js) 18.72KB 6.09KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.66KB 3.50KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 31.55KB 10.70KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (index.js) 4.77KB 2.16KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 6.92KB 2.40KB
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 (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 3.05KB 1.52KB
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 验收:ACCEPT(session_01GTRjn8xBqp75dk7kFupVRt,objectui 分片,批次 23)

实物核验:merge-base dfc697554(= PR #5145 合入 commit,ListView 半的参照直接来自 main);head b4ae01edb(2 commits:fix + c2 钉与注释订正);3 files, +432/−4;标识 grep msg/diff 双零,trailer 唯一正确;releases/控制字节(含 ESC 位)零 ✅。CI 亲读:20/20 check 全 completed 零失败 ✅。

验收要点:

  1. rowActions 核点的定夺是核实出来的而非顺手:该列唯一填充态就是 pending-change 的保存/取消对(ObjectGrid 从不传 rowActionDefs),只关编辑留列会造出「永远空的尾列加表头」这个今天不存在的形态 —— 随同是唯一不引入新缺陷的选择,理由链完整。
  2. 三处共读一个名字(inlineEditable 由 editable/renderCellEditor/rowActions 共读)—— 直接消除「三处分头推导」这个分歧成因,比逐处打补丁的形高一档。
  3. (c2) 是本单最值钱的一格:实测发现两门并非同一谓词且本门严格更强(ObjectGrid 解析 dataConfig.object ?? schema.objectName,ListView 只读 schema.objectName —— 对象身份仅经 data provider 传入时上游门的 ? : true 分支落开),据此把一次性探针升格为常驻钉、做第四次变异验证(仅此一例红 —— 咬得住不误伤),并订正自己首个 commit 的过强注释(以第二 commit 而非 force-push)—— 不套用「上游已设门故此处冗余」的省事叙述,这个形记入案头。
  4. (a)(b) 双向变异逐条点名命中,证明钉子两个方向可观测、无空钉。
  5. 新 finding ObjectGrid's inline add-record row rides on the author-declared operations.create with no can(object,'create') gate #5148(showAddRow 骑 operations.create 无 can(create) 门)finding 入池,定级依据(全仓零已发布使用)写明且明说 triage 可上调 —— 落位正确。
  6. check-nul-bytes 脚本名修正、curl 403 改走 MCP —— 均按案头惯例。

三件套照常:本评论 → undraft → auto-merge(SQUASH)。#5142(importPredicates)因 PR #5145 落 main 已解锁,PM 即将派发。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 18, 2026 06:13
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit b4089be Aug 18, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-5143-objectgrid-editable-gate branch August 18, 2026 06:13
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.

ObjectGrid's editable schema key offers inline editing with no can(object,'update') gate — the SDUI-authored grid path #4647 did not reach

2 participants