Skip to content

[Decision] objectui#4421 batch #13 item 3: change usePermissions' no-provider default as ruled, or record it satisfied by the engine binding (measured off its path; changing it drops Edit/Delete in embeds) #11213

Description

@objectstack-fleet

Ruled: 5912469799 · letter A — objectui#4421 batch #13 item 3 is satisfied by the engine binding; usePermissions' no-provider default stays; no code · 2026-09-30T13:41Z

Filing-gate category: ② a decision only the maintainer can make: a measured fact contradicts the premise of a recorded ruling's item, and the seat cannot re-rule it. Raised by the domain:ui seat 1 (session_0122Knsowci76D2rBWReCzzZ) from objectui#4421's dispatch (PR objectui#11208). Reader who acts: the maintainer (answer), then the domain:ui seat that holds objectui#4421 (executes).
Dedup: objectui#4421's thread is the only record of item 3; no other card carries it.

Governing text:objectui#4421 裁决批 #13(5474563898)第 3 项原文:「Rider 1 为两半共同交付要件:usePermissions 无 provider 默认 can: () => true 是 fail-OPEN,必须改为 isLoaded 前答 false——独立于选型,先行修复不等 os 半」;packages/plugin-grid/src/rowCrudAffordances.ts 文档块记载该默认是刻意的(没有 PermissionProvider 的独立嵌入没有权限来源,不能因此丢掉 Edit/Delete);ADR-0124 D1(服务端执法、前端只是礼貌)。是否改协议:否,只关 objectui 的一个默认值。

前提(带复核命令):

  • 已落地的绑定(PR objectui#11208)从不调用 usePermissions().can,它读的是 MePermissionsProvider 发布的 effectiveObjects;无 provider 时 effectiveObjects 不存在,current_user.can(...) 被引擎响亮拒绝,于是在 fail-closed 的面上隐藏。复核:git -C objectui show 分支 claude/issue-4421-current-user-can-wiring:packages/app-shell/src/providers/ExpressionProvider.tsx | grep -n effectiveObjects(阳性对照:同文件 isLoaded)。
  • 同一棵树里 usePermissions().can 在无 provider 时仍答 true(PR 的渲染测试钉住了这一点)。
  • rowCrudAffordances.ts 的内置 Edit/Delete 依赖这个默认值。

一句话问题:裁决当时以为「让权限谓词在加载前隐藏」必须改掉 usePermissions 的全局默认值;实测新绑定根本不走这个默认值,它已经做到加载前隐藏。现在改这个默认值不会让任何权限谓词更安全,只会让没有权限服务的嵌入页丢掉编辑和删除按钮。

选项 × 真实代价

做什么 客户可感知的后果
A 记为已满足 不改默认值;在 objectui#4421 上记录「第 3 项的目标(加载前不泄露)已由引擎绑定达成」 嵌入页照旧有编辑/删除;current_user.can 在加载前照样隐藏(实测)
B 照字面改 无 provider 时 can 在加载前答 false 所有没有挂权限服务的独立嵌入页,表格行上的编辑/删除按钮消失;current_user.can 的行为不变(它不读这个默认值)

业务含义直译:A = 门禁已经换成新锁,旧锁不用动;B = 新锁已经装好,还要把旧锁也焊死,结果是没有钥匙柜的分店连正常的门都开不了。

四轴(业务立场)

  • 项目长远合理性:一个关注点一个失败方向。current_user.can 的「加载前隐藏」由引擎契约保证(无权限数据即响亮拒绝);usePermissions 的无 provider 默认值管的是另一件事:没有权限服务的嵌入页该不该有内置按钮。把两者绑在一起,是让一个默认值同时回答两个问题。服务端仍然执法(ADR-0124 D1),两种选择都不改变授权边界。
  • 实际业务需求:今天真正使用无 provider 默认值的是独立嵌入(rowCrudAffordances.ts 文档块写明);改掉它会让这些嵌入页丢按钮。没有任何实测场景需要 B 才能让权限谓词安全。
  • 防 AI 犯错:A 下,AI 写 current_user.can(...),加载前是响亮拒绝加隐藏,出错看得见;B 对这一点没有任何增益,却让嵌入页里「按钮消失」变成一种无诊断的现象,AI 会去错误地排查权限配置。
  • 创业阶段不扩散:A 零改动;B 要改一个共享默认值,并给所有嵌入场景补回按钮的来源。

os-decision-facets

  • ① 项目长远合理性:A 一个关注点一个失败方向;B 让一个默认值同时回答两个问题。
  • ② 实际业务拉动:独立嵌入今天依赖该默认值;B 无实测收益。
  • ③ 防 AI 犯错:A 保持响亮拒绝;B 让嵌入页按钮无诊断消失。
  • ④ 创业阶段不扩散:A 零改动;B 改共享默认值并需补偿嵌入场景。
    Prior rulings read: 4421, usePermissions, rowCrudAffordances, batch #13 → objectui#4421 thread 11 comments read in full; ADR-0124 D1; thread: ruling 5321072032, ruling 5474563898 item 3

推荐:A。只看①选 A;②③④ 是否翻转:否。回退:B(若维护者认为独立嵌入也必须在无权限来源时默认不显示内置按钮,那是一个单独的产品决定,应连同嵌入场景的补偿一起做)。
置信缺口:独立嵌入的真实用户数本席未测;dev 的测量在 jsdom 渲染测试中完成,未在运行中的嵌入页复测。

裁后执行:裁 A ⇒ 本席在 objectui#4421 记录第 3 项已满足并关本卡,不改代码。裁 B ⇒ 本席派发一张带嵌入场景补偿的实现卡(usePermissions 默认值 + rowCrudAffordances.ts 的按钮来源),objectui#4421 的其余部分不受影响。

Activity

  1. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #258 item 1 · letter A · maintainer 「11213 A」 2026-09-30T13:39Z

    Director seat (objectstack#12708, summon #30 续 2, session_01AsCNgFBs8HCjwhyHQsFbx3). Provenance: maintainer, live PM chat with the director seat, 2026-09-30, replying 「2465 D 11213 A」 to batches #257 / #258 as presented. Presented on this card: the domain:ui seat 1's decision request (the body, from objectui#4421's dispatch, PR objectui#11208) with its recommendation A and the director's reading concurring.

    Ruled: A — objectui#4421 batch #13 item 3 is recorded as satisfied by the engine binding; usePermissions' no-provider default is not changed. The item's purpose — a current_user.can(...) predicate must not leak before permissions load — is met by the landed binding, which reads MePermissionsProvider's effectiveObjects and never the hook's default: with no provider the engine refuses loudly and the fail-closed face hides. The permissive default keeps doing the one thing it is documented for in rowCrudAffordances.ts — a standalone embed with no permission source keeps its built-in Edit / Delete. The server still enforces (ADR-0124 D1); neither letter moves the authorisation boundary.

    Readings that decided it: ① one concern, one failure direction — B would make one default answer two questions; ② standalone embeds are the only measured users of the default, and B has no measured gain; ③ under A a broken predicate is refused loudly and visibly, under B "the buttons vanished" becomes an undiagnosed symptom; ④ A is zero change. Confidence gaps recorded: the count of standalone embeds is unmeasured; the dev's measurement ran in jsdom, not in a running embed. Fallback recorded: B is a separate product decision (embeds without a permission source hide built-in buttons) and would come with its own compensation card.

    State: no code. needs-user-decision removed, this card closed completed with this record; the Ruled line goes on the body; a note goes on objectui#4421 that item 3 is satisfied, for the domain:ui seat that holds it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions