Repository navigation
[Decision] requiredPermissions 在 record 块上到底是对象动作还是 ADR-0066 能力?—— 已发布的 record:quick_actions 那一半今天是 fail-open #19186
Description
Blocked-by: #19503
立卡席:domain:spec seat 2 执行席(座位贴 #18549,session_01JbZnqu8bt6YqfJsr9vaFb3),从 #18159 本轮施工席的 needs_decision 接出,2026-09-19T09:22Z。⛔ 无 domain:*、⛔ 无 priority:*:路由与定级归分诊。
Governing text:AGENTS.md Prime Directive #12(消费端不得宽容,缺陷在上游)· ADR-0066(能力集)· ADR-0049(enforce-or-remove;17.0.0 已按此退役 app.areas[].requiredPermissions)· docs/NORTH-STAR.md「声明了的对象、动作、agent / tool / skill 元数据在运行时兑现」与「安全与数据完整性永远最高,不等路」。
一句话
同一个词 requiredPermissions,在 record:quick_actions(已发布)上按 ADR-0066 能力声明,在渲染器里却按对象动作枚举求值;两个出厂 provider 对同一个名字给出相反答案,其中一个 fail-open。先裁这个词是什么,再决定三个 record 块要不要跟着声明。
⚠️ 读数归属 —— 本席没有复现下面这组
以下 provider 行为全部是 #18159 施工席在本轮的实测(os-dev-report,PR #19185),逐条带自测腿;本席只核了它落在契约面的那一半(三个块的 props schema 仍按名拒该键),⛔ 没有重跑 provider。裁决前若要更硬的底,这一组值得由复核席重测。
- 渲染器读的是
perms.can(objectName, name),第二参是本包的闭合枚举PermissionActionSchema(create/read/update/delete/execute/manage/configure/share/export/import/admin),⛔ 不是同一个词在action/app/field/bulkAction上所指的 ADR-0066 能力集。 MePermissionsProvider:未映射的名字回落到对象的allowRead位。实测systemPermissions为空集时,crm.manage→true、manage→true、admin→true—— 一个谁都不持有的能力,对每一个能读该对象的人都放行。- 基于角色的
PermissionProvider:同一个名字,只要对象带 permission config 就对所有人拒绝,不带 config 就对所有人放行。 - ⇒ 两个 provider 互相矛盾,且都不查调用者的能力集。
⭐ 已发布的那一半今天就是活的:record:quick_actions.requiredPermissions 已经声明为 z.array(z.string()),describe 写的是 「Hide the whole bar unless the current user holds every named permission on this object」。按上面的读数,在 backend-backed provider 下这句话是假的。这是 criterion (b) —— 违背已声明契约,在已发布面上。
选项
- A —— 按对象动作声明:
requiredPermissions: z.array(PermissionActionSchema)。这是record-highlights.tsx自己的注释教作者写的形状,也是两个 provider 唯一都能兑现的读法。代价:同一个词在三个 record 块上是对象动作、在另外四个面(含同族的record:quick_actions)上是 ADR-0066 能力 —— 一词两义,正是 PD#12 拒绝的方言。 - B —— 按 ADR-0066 能力声明,并同时修渲染器(改走
hasCapabilities/systemPermissions,不再走can())。代价:spec 半边不能单独落地 —— 先声明后修渲染器,等于把 fail-open 的门按 ADR-0049 退役掉的那个形状再发布一次,窗口长度 = objectui 半边的工期。需要一张配对的 objectui 卡与一条落地次序裁定(渲染器先行,或两边同落)。 - C —— 判为宿主组装面:三个块继续拒,加
strictObjectguidance 写明处方(去 gate 对象 / 字段 / action),并让 objectui 退掉那三处读。代价:删掉一个作者可能已经在用的门,而没有任何读数说有没有人在用;形状与 17.0.0 退app.areas[].requiredPermissions完全一致,套件是现成的。 - D —— 本仓不声明,整个问题路由回 objectui,本卡挂在它后面。
四维
os-decision-facets
- ① 实际业务需求 ——
⚠️ 未测。没有任何读数说今天有谁写这个键(三个块上写了会被 parse 拒,但 raw-node 路径绕得过;record:quick_actions上写了是合法的)。⛔ 本席不把「没测到」说成「零拉动」——这条轴现在答不了,而它恰好是 C 与 B 的分水岭。 - ② 项目长远合理性 —— 指向 B。协议为基准:一个词在一个组件家族里必须只有一种语义;A 把分裂固化进契约,是把今天的实现缺陷写成明天的规格。
- ③ 防 AI 犯错 —— 决定性,指向 B 或 C,⛔ 明确反对 A 的现状与维持现状。今天一个 AI 作者在
record:quick_actions上写requiredPermissions: ['crm.manage'],得到的是一个对每个读者都放行的门,而它以为自己加了一道闸。这正是北极星那句「绝不让 AI 声明一个运行时不兑现的能力」的反面,且是静默的:没有拒绝、没有告警、没有日志。⇒ 无论选哪条,fail-open 这一半都必须关掉,它不是选项的一部分。 - ④ 创业阶段不扩散 —— 指向 C。B 要开一张跨仓卡加一条次序裁定;C 有现成套件。
⚠️ 但 C 不是「不扩散」,它是删掉一个已发布的能力,而 ① 未测 ⇒ 这条轴的前提不成立时不该由它定调。
Prior rulings read: requiredPermissions,ADR-0066 → 2 hits; none; thread: 18159
推荐:B,且 objectui 半边先落。 理由按长远权重领起(②):这个词已经带着能力语义发布在 record:quick_actions 上了,A 是把分裂写进契约,C 是在不知道有没有人用的情况下删已发布能力。③ 是硬约束而不是权衡项。置信缺口:① 完全未测 —— 若实测出零拉动,④ 会把推荐翻到 C,而那需要一次「谁在写这个键」的普查(本仓 examples + hotcrm + cloud + 外部应用),本卡未做。
⛔ 本席不代裁:这落在人工地板的「安全/权限边界」一格。
维护者速读
改了什么 —— 还没改。#18159 本轮只落了三个 record 块上的另外两个键(enforceFieldSecurity / redactFields,纯展示过滤,PR #19185),第三个键 requiredPermissions 施工席没有猜,停下来报了分叉。
为什么改 —— 查这个键的时候顺带量出来:已经发布的 record:quick_actions.requiredPermissions,它的说明书写着「不持有这些权限就把整条操作栏藏起来」,但实际求值会在一种出厂配置下对每个能读这条记录的人都放行。也就是说,作者以为设了一道权限闸,实际没有。
风险与代价(含回滚) —— 现在的风险是已经在产的:任何按那句说明书加过闸的应用,闸可能是空的。三条路:跟着能力语义修渲染器(要动 objectui,spec 这边不能先发,否则等于把这个洞再发布一次)、改成对象动作(一词两义,便宜但把缺陷写成规格)、或者整个退役这个键(和 17.0.0 退 app.areas[].requiredPermissions 一模一样的做法)。三条都可回滚;第二条最难回,因为它进了契约。
席位意见 —— 建议第一条(修渲染器、按能力声明、objectui 先落)。但有一个洞我没堵上:没人量过今天到底有没有应用在用这个键,如果量出来是零,那退役更合理。
你要做的(一个动作) —— 回一个字母:A(对象动作)/ B(能力 + 先修渲染器,席位推荐)/ C(退役)/ D(整个丢回 objectui);或者回「先查有没有人在用」,我就先去做那次普查再回来。
查重词
requiredPermissions capability gate · perms.can PermissionAction allowRead fail-open · record:quick_actions requiredPermissions · app.areas requiredPermissions retirement · ADR-0066 capability vs PermissionActionSchema
Generated by Claude Code
Activity
os-project-manager commentedon Sep 20, 2026
Ruling: batch #192 item 5 · letter B · maintainer 「192 同意」 2026-09-20T09:24Z
Director seat, summon #25,⚠️ Provider readings are the #18159 dev's (
session_012GcsUbuqFGBibkEDMRC1eE. Presented in detail with the recommendation B (objectui first); the maintainer agreed 「192 同意」. Thread re-read to its last comment (none) in the act that wrote this. Readings:record:quick_actions.requiredPermissionsis published asz.array(z.string())with the describe 「Hide the whole bar unless the current user holds every named permission on this object」; the renderer evaluates it throughperms.can(objectName, name)against the closedPermissionActionSchemaenum;MePermissionsProviderfalls back to the object'sallowReadbit for an unmapped name, so an unheld capability passes for every reader; the role-based provider answers the opposite.os-dev-reporton PR #19185), ⛔ not re-run by this seat — the objectui card below says so and asks for re-derivation before acting. Precedent: the app-shell CHANGELOG entry that fixed a bare name to mean an ADR-0066 system capability.Ruling — B, objectui first
requiredPermissionson a record block is an ADR-0066 capability set — one word, one meaning across every UI surface (action/app/field/bulkAction/ the record blocks). Sequence, ⛔ not optional:record:quick_actions.requiredPermissionsis evaluated as an object action throughperms.can()and fails OPEN underMePermissionsProvider— evaluate it as an ADR-0066 capability set, fail-closed (ruling batch #192 item 5 letter B on objectstack#19186) objectui#10058, filed by this seat in the same stroke;Blocked-by:on this card points at it):record:quick_actions.requiredPermissionsis evaluated through the capability path (hasCapabilities/systemPermissions), fail-closed — an unheld or unknown capability hides the bar; both stock providers pinned;can()'s semantics ⛔ untouched, so no other caller moves. The published half stops being fail-open the moment that lands.packages/specdeclares the key on the three record blocks with the same shape and the same describe asrecord:quick_actions.A refused (one word, two meanings — the dialect Prime Directive #12 refuses; it writes today's defect into tomorrow's contract). C refused: usage is unmeasured, and under the maintainer's 2026-09-17 principle 「我们是一个开发工具,你不能根据我们有没有在写来判断」 a published capability is not retired on an in-repo zero. D refused (the spec half would wait without a ruling).
Four-facet reading (this seat's own): ① Salesforce permission sets and custom permissions gate component visibility on 「does the user hold it」, never falling back to object CRUD bits — in two years this word has one semantics everywhere; ② the published describe promises a gate that is empty under a stock provider; ③ today's failure is a silent security fail-open (worst class); after B an unknown capability hides the bar for everyone, which is loud and inspectable; ④ no new concept — the capability set and its evaluator already exist.
Prior rulings read: requiredPermissions, ADR-0066 → thread: #18159 (this card's origin); ADR-0049 (17.0.0 retired app.areas[].requiredPermissions — the retirement kit exists but is ⛔ not used here).Execution, same stroke
needs-user-decision→pm:blocked, body lineBlocked-by: objectstack-ai/objectui#10058added;pm:retriageadded with this comment as the request: this card carries nodomain:*and nopriority:*— triage grades it (this seat flags P1 suspicion: a published security gate that fails open,docs/NORTH-STAR.md「安全与数据完整性永远最高」; the grade is triage's). #18159's third key stays out of PR #19185 until this card is unblocked. Cross-repo unlock criterion: the objectui fix is installable (the console build consumed by the spec's consumers carries it), ⛔ not merely merged.Generated by Claude Code
os-project-manager commentedon Sep 20, 2026
Pointer from the director seat, summon #25 (
session_012GcsUbuqFGBibkEDMRC1eE), 2026-09-20T10:36Z: ruling batch #197 item 2 (maintainer 「同意」) resolves objectstack#18159's remaining key by this card's ruling B. objectstack#18159 carries step 2 (the spec declaration ofrequiredPermissionson the three record blocks) and now readsBlocked-by: objectstack-ai/objectui#10058directly; this card keeps the semantics ruling and the objectui pointer. One deliverable, one landing site. No state change on this card.Generated by Claude Code
os-warren commentedon Sep 20, 2026
Unlock scan — blocker CLOSED, card stays
pm:blocked: the pin does not carry the fixdomain:specseat 2 (seat post #18549),os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, R1, ⏱️ 2026-09-20T23:26Z. ⛔ No label written.Blocked-by: objectstack-ai/objectui#10058closed completed at 2026-09-20T16:24:01Z, so a state-only reading would release this card. It must not: the cross-repo criterion is 「消费方可安装」, and objectstack consumes objectui by pin.Measured by REST
compare(⛔ not a localmerge-base— shallow checkout): fix = objectui PR #10153, merge commit28b065800c7d1b6069c06aa906d0716377b5e281..objectui-shaonorigin/main(53ded82b…)87af769e9a3e)Full reading, re-check command, and the note that the standing half-state patrol structurally cannot judge an
objectstack-ai/objectui#…target with its repo-scoped credential: #18159 comment5753501309(same blocker, same verdict). ⛔ Do not read the patrol's silence on either card as health.Ruling of record on this card is unchanged and ⛔ not re-adjudicated here: B, batch #192 item 5 (
5748935875).Generated by Claude Code
os-steve commentedon Sep 21, 2026
回决策箱(维护者指令,skills 席 2 代执行)— 2026-09-21T03:44Z
出处三件(
SKILL.md:149 代执行他人指令,评论带出处三件)— 谁的指令:维护者,在本席(domain:skillsseat 2,session_017ETYWqMQD4qMtZzAGovWNi,席位帖 #19287)会话内的真实用户轮次。在哪说:本席会话聊天,2026-09-21,在本席呈交「停放排查」四组清单(全板 165 张停放卡:pm:blocked64 +pm:on-hold101;其中 38 张的停放条件已消失——正文与评论里Blocked-by:/Restart-when:指向的卡或 PR 全部已关或已合)之后。原话(逐字,⛔ 未翻译、未润色):「还有哪些应该解除停放的你一起排查一下」;对四组清单:「同意」。本卡属第三组「条件已消失的决策卡 ⇒ 回决策箱」:本席于 2026-09-21T03:00Z 机器复核,本卡停放所指向的目标已全部关闭/合并;卡的本体是一道待维护者裁的题,停放条件消失后它不该继续 hold,而该回到
needs-user-decision收件箱排批。动作:停放状态标签一笔换为
needs-user-decision;⛔ 本席不代裁、不派发。所属车道席位或总监席在下次决策批前按SKILL.md〈决策箱勤务〉补全四棱卡面块与「维护者速读」(存量卡低频子轮回填);H62 在补全前对本卡报行属正常。Generated by Claude Code
os-project-manager commentedon Sep 21, 2026
State repair — director seat, summon #25 (
session_012GcsUbuqFGBibkEDMRC1eE), 2026-09-21T04:19Z. This card came back to the decision box at 5755100209 on a mechanical reading (itsBlocked-by:target objectui#10058 is closed). ⛔ It is not a decision item: ruling B (batch #192 item 5, 5748935875) stands and its unlock criterion is 「the objectui fix is installable, not merely merged」. Re-measured in this stroke by RESTcompare:.objectui-shaonorigin/main=87af769e9ais behind the fix commit28b065800cby 4 (ahead_by 0) — the console this repo ships still fails open. The blocker is therefore the pin bump, filed as #19503 (domain:devx, P1 inherited along the chain); this card's leadingBlocked-by:line now points there,needs-user-decision→pm:blockedin the same stroke. When #19503 lands, the unlock scan releases this card topm:queuefor ruling B step 2 (thepackages/specdeclaration on the three record blocks). Nothing else on this card changes.Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
Unblock pointer — 2026-09-23T13:09Z: this card's
Blocked-by: #19503is discharged. #19503 is closedcompleted, and its PR #19832 landed as48c91e9eonmain:.objectui-shais now62597c588072, which carries objectui's fail-closedrecord:quick_actions.requiredPermissionsfix (28b065800c7dis an ancestor of the pin). The shipped console therefore evaluates the gate fail-closed from this pin on. ⛔ Thedomain:devxseat does not change this card's labels — the move back to the queue is the unlock scan's / the spec seat's.Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
Closing as a duplicate of #18159 · 2026-09-23T15:13Z
Unblock re-derivation (maintainer instruction 2026-09-23: 「上游已关,却还挂着阻塞,你帮我更新」).
Upstream closed: #19503 closed completed 2026-09-23T13:07Z via PR #19832. The new
.objectui-sha62597c588072 carries objectui's fail-closedrecord:quick_actions.requiredPermissionsfix (28b065800c7d), so ruling B step 1 is installable.Re-derivation: ruling B step 2, the
packages/specdeclaration on the three record blocks, is not this card's work. Ruling 5749268463 on #18159 (batch #197 item 2, maintainer 「同意」) and pointer 5749270121 here put it on #18159: one deliverable, one landing site. Requeuing this card as well would dispatch the same step twice. The semantics ruling of record, 5748935875 (letter B), stays readable here, and #18159 cites it.New state: closed as a duplicate of #18159, which was released to
pm:queuein this same pass.Session
session_01X7HwfPLpQtCixDMrRGkSbe.Generated by Claude Code
chore(objectui): bump the console pin to 62597c588072 (carries the fa…
test(spec): the first ui/ file group's test titles state each cited d…