Skip to content

RBAC + 组织架构权限:真实 UI 测试结论 — 后端判定全部正确,但「记录范围(readScope/writeScope)」在 Setup/Studio 无法配置与查看,另有 7 项管理体验缺口(待 PM 确认排期) #26059

Open

Description

@objectstack-fleet

Ruled: 6416233738 · letter Q1 是 / Q2 不做 / Q3 分拆 / Q4 框架 · 2026-10-26T12:40Z

一句话结论

基于 RBAC(权限集 + 岗位)+ 组织架构(业务单元树 + 汇报链)控制记录权限的后端判定全部正确,未发现越权或误判;但最核心的「按组织架构控制范围」(own / own_and_reports / unit / unit_and_below / org)在控制台 UI 中既不能设置也看不到,客户管理员无法自助配置。另有 7 项管理体验缺口与 2 项未授权(社区版)模式下的诊断问题。

请 PM 确认范围与优先级后拆卡排期。

测试环境与方法

  • 框架:objectstack fc2aaaec(main);企业版:objectstack-ai/cloud apps/objectos-ee + @objectstack/security-enterprise,启动日志 ObjectOS Enterprise — LICENSED,features 含 hierarchySecurity(Trial 许可证,离线验签)。
  • 租户库:本地 SQLite(file:),单租户姿态。
  • 方法:管理员在控制台 UI 完成全部配置(Studio 建对象/应用并发布;Setup 建业务单元树、13 个用户、部门成员、上级关系、8 个权限集、6 个岗位与持有人、2 条共享规则、分级管理范围),再以各角色真实登录浏览器验证(Playwright,每个角色独立浏览器上下文,服务端 get-session 校验身份)。
  • 唯一的非 UI 步骤:权限集上的 readScope / writeScope 只能通过 PUT /api/v1/meta/permission/:name 写入(见问题 1)。
  • 此前还跑过:security-enterprise 包测试 409/409 通过;接口层测试 60+ 项(授权企业版 + 未授权对照)。

已验证通过(UI,33/33)

场景 结果
10 个角色的列表可见性:本人 / 本单元及下级 / 本人及下属(含间接下属)/ 查看全部 / 无权限用户显示无权访问页 10/10
字段级权限:销售的表单与列表均不出现敏感字段,经理可见 通过
增删改:经理可改/删本单元记录;平级单元记录按 URL 打开显示「记录不存在」;VP(读下属、写自己)在下属记录上无编辑按钮;销售无删除项;审计员(查看全部)无编辑按钮;无新建权限点 New 被服务端 403 拒绝并提示 9/9
UI 中调整组织后立即生效:调部门、换上级;直接授权的权限集与岗位的权限集取并集 4/4
UI 中创建共享规则:发给某单元不含下级、发给「单元及下级」含下级;UI 中停用规则后立即失效 3/3
分级管理:子树内分配成功且被分配人可用;子树外、白名单外岗位、给自己提权均被拒 5/5
授权有效期:已过期无权限;UI 中延长后恢复 2/2

问题清单

P1 — 1. 记录范围(readScope / writeScope)在 UI 中不能配置,也不显示【漏做,非设计如此】

  • 现象:Setup → 权限集 与 Studio → Access 的权限矩阵只有 C/R/U/D/Tr/VA/MA 与字段读写;没有「只看自己 / 本人及下属 / 本单元 / 本单元及下级 / 全组织」选项。权限集页、岗位页都不显示当前范围。
  • 依据(为什么判定为漏做):
    • ADR-0084(Accepted 2026-07-01)规定 Access 支柱的编辑器为「a permission matrix (roles × objects × CRUD + record scope)」——docs/adr/0084-application-builder-information-architecture.md:47。
    • 现有编辑器 ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx(约 961–985 行注释)只保证保存时不误删这两个键(type-alias-convention.pin.test.ts 的分节注释写着 "717 isomorphic aliases",实际 822 —— 唯一没被计数断言盖住的数字 #6605 的修复),从未提供编辑控件。
    • ADR-0090 落地清单的 ObjectUI 部分(docs/adr/0090-permission-model-v2-concept-convergence.md:642)未包含「矩阵加范围列」,需求在两个 ADR 之间漏掉;未找到跟踪 issue。
    • 用户文档 content/docs/permissions/permission-sets.mdx「Access depth」一节只描述代码声明方式。
  • 影响:后端、企业版解析器、explain 都已完整支持;只有界面缺失。应用包开发者在代码里声明不受影响;客户管理员自建/调整权限集时无法设置组织架构范围,也看不到现状——这是本功能面向客户的核心入口。
  • 建议:矩阵每个对象行增加「读范围」「写范围」两个下拉(own / own_and_reports / unit / unit_and_below / org)。需遵守现有约束:勾「查看全部」时不可设读范围、勾「修改全部」时读写范围都不可设(服务端会拒存,见 A permission set accepts a hierarchy readScope beside viewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870);未授权 hierarchySecurity 时层级三档应提示「需要企业版,否则只按本人生效」。权限集/岗位详情页应只读展示当前范围(previews/PermissionPreview.tsx 已有展示组件,未接入这两个页面)。

P2 — 2. 分级管理员默认进不了 Setup,配置界面无提示

  • 现象:给权限集配置「Delegated Admin Scope」并授予用户后,该用户打开 Setup 显示「You don't have access to this app」。还需额外授予系统能力 Setup Access(setup.access),以及 sys_business_unit / sys_user 读权限(lookup 才能选人选部门),才能在 UI 中执行分级分配。
  • 建议:在 Delegated Admin Scope 区块提示所需的配套授权,或保存时给出诊断/一键补全;文档 delegated-administration 一并说明。

P2 — 3. 被拒绝时 UI 只显示笼统提示,丢失服务端原因

  • 现象:分级管理员越界时服务端返回具体原因(如「business unit … is outside the delegated subtree」「position 'pos_mgr' distributes permission set 'ps_mgr', which is not in the scope's allowlist」),UI toast 只显示「You don't have permission to save this record.」。
  • 建议:toast 展示服务端 error 文案(或其本地化版本)。

P2 — 4. 岗位保存后进入草稿,但岗位列表不显示草稿

  • 现象:Setup → Positions → New → Save 发起 PUT /api/v1/meta/position/:name?mode=draft,返回 200,但岗位列表不出现该岗位,GET 返回 not found;需进入详情页再点 Publish。管理员容易误以为没保存成功(权限集保存则直接生效,两者行为不一致)。
  • 建议:列表显示草稿并标注「未发布」,或保存后引导发布。

P3 — 5. 普通用户没有「把记录共享给指定人」的界面入口

  • 现象:记录详情 More → Share 只是复制链接;sys_record_share 的手动共享只能通过 POST /api/v1/data/:object/:id/shares。按规则批量共享可在 Setup 配置(已验证可用)。
  • 建议:PM 确认手动共享是否属于本期范围;若是,记录页增加「共享给…」对话框(用户/团队/岗位/业务单元 + 只读/可编辑)。

P3 — 6. 无新建权限的用户也显示「New」按钮

  • 现象:审计员、财务只读用户(无 Create)在列表上仍有 New 按钮,提交后服务端 403。
  • 建议:按 /me/permissions 的有效权限隐藏或置灰。

P3 — 7. Studio 字段类型搜索按回车选中的是当前类型

  • 现象:Add field / 字段属性中,类型搜索框输入「Number」后回车,选中的是当前已选的「Phone」(当前项置顶),需用鼠标点选。

P3 — 8. 业务单元成员列表「Title」列显示原始 ID

  • 现象:Setup → Business Unit Members 列表 Title 列为「 in 」,看不到人名和部门名。

P3 — 9. 未授权(社区版)模式下的两个诊断问题(不导致越权,均为 fail-closed)

在同一 objectos-ee 不带许可证启动(ObjectOS Community mode)时:

  • 9a 保存层级范围无任何提示:PUT 一个 readScope: 'unit_and_below' 的权限集返回 200、_diagnostics.valid: true,日志无告警,但该范围实际不生效。代码中 defineStack 声明同样内容会在编译期报错,运行时元数据接口却放行,两条路径不一致(ADR-0049 / ADR-0078 精神)。
  • 9b explain 给出错误原因:经理查看同单元下属记录不可见时,explain 的 depth 层写「owner … is outside the caller's 'unit_and_below' read depth」,而下属就在其单元内;真实原因是层级解析器未安装(未授权)。建议 explain 明确报告「hierarchy resolver absent / requires enterprise」。

设计确认(非缺陷,记录备查)

  • 用户自己查询看不到的记录时,explain 只返回不可见、不给原因、不透露记录存在;管理员代查可见完整原因——防止存在性泄露,符合预期。
  • 共享规则在 objectos-ee 运行时可通过 Setup(sys_sharing_rule)创建;元数据类型 sharing_rule 仍为代码声明(运行时 PUT 返回 code-only),两者并存。

请 PM 确认

  1. 问题 1 是否按 ADR-0084 补齐,定为本期 P1?
  2. 问题 5(终端用户手动共享入口)是否纳入本期范围?
  3. 其余 P2/P3 是否合并为一张体验修复卡,还是分别建卡?
  4. 问题 9 归属框架(explain / 元数据保存诊断)还是 cloud(objectos-ee 社区模式提示)?

复现材料

测试脚本与 140+ 张截图保存在执行会话的临时目录,未提交仓库;需要时可整理为 cloud docs/qa/platform-checklist/ 的正式测试项(coverage.json 中 hierarchySecurity 当前为 WAIVED,无测试项覆盖)。


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: graded as a decision card — priority:p1 · domain:ui · area:access · pm:awaiting-maintainer · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T10:56Z

    ⛔ Not a claim, ⛔ not a dispatch. This card is the parent. Its items are split into child cards once the maintainer answers.

    Verified on origin/main: ADR-0084:47 requires "a permission matrix (roles × objects × CRUD + record scope)". PermissionMatrixEditor.tsx (around :961–985) only preserves readScope / writeScope and offers no control. No open or recent card tracks the gap (600 newest scanned; #26040 and #26056 are adjacent, not the same).

    Triage leaning on the four questions:

    1. Item 1 → p1, domain:ui. Two scope selects per object row, with the existing all-records exclusions (A permission set accepts a hierarchy readScope beside viewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870) and an unlicensed-hierarchySecurity hint. Wire PermissionPreview read-only into the permission-set and position pages. About 2–3 dev-days.
    2. Item 5 (manual share UI): not this period. Rule-based sharing works, and the server door exists. File it as a p3 card for later.
    3. Split the rest by landing site, not one card:
    4. Item 9 → framework, domain:server, p2, two cards. 9a: the runtime save of an unlicensed hierarchy scope is silent where defineStack refuses it. 9b: explain names the wrong cause when the resolver is absent.
    • Also: hierarchySecurity is WAIVED in coverage.json with no item. A domain:devx p2 card turns this run into checklist items.

    Generated by Claude Code

  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #396 · 2026-10-26T12:17Z

    Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #396 as its one item, at the maintainer's word in the summon chat (「再和我讨论 #26059 清单确认」). Seat increment (re-verified on origin/main cdd4eea9; the test conclusions and the triage leanings 6414323834 are not restated):

    os-decision-facets

    • ① 项目长远合理性:问题 1 是 ADR-0084 已声明、未建的编辑器,补上即「声明 = 执行」;问题 9 两张卡让运行时元数据保存与 explain 不再静默(ADR-0078 / ADR-0049)。
    • ② 实际业务拉动:客户管理员自建权限集时设不了组织范围、也看不到现状(实测 33/33 后端通过,唯独界面缺口);问题 5 的手动共享服务端门已有、仅缺入口,零客户要求。
    • ③ 防 AI 犯错:矩阵里的范围下拉按 A permission set accepts a hierarchy readScope beside viewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 置灰互斥项、未授权时提示,错配置在保存前被拒;9a/9b 让「声明了但不生效」在保存与 explain 上响亮。
    • ④ 创业阶段不扩散:问题 1 零新键(枚举与校验已在 spec);问题 5 本期不做;其余按落点拆卡而不合成一张「体验卡」,避免一张永不关闭的清单卡。
      Prior rulings read: ADR-0084 record scope · PermissionMatrixEditor readScope · #16870 · #6605 · hierarchySecurity → ADR-0084:47, ADR-0090:642, permission.zod.ts:632, platform-capabilities.ts:224, cloud coverage.json:42; thread: 6414323834.
    • Facets read: same direction as triage on Q1, Q3, Q4; on Q2 the seat recommends file a p3 queue card, not this period (an existing server door with no UI entry is a gap card, not a new capability). Workload (card column + triage): Q1 ui M (2–3 dev-days); Q3 six cards S each (five ui, one server) + docs; Q4 two server cards S–M; checklist card devx S–M in cloud.
  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Item-by-item reading for the maintainer · director seat · 2026-10-26T12:26Z

    Asked in the summon chat (「26059 要每个问题给我具体分析」). Every claim below was read on objectstack origin/main cdd4eea9; where the seat did not read a path line by line it says so. Each item: cause → where it lands → fix shape → size → grade the seat recommends.

    1. Record scope (readScope / writeScope) cannot be set or seen in the UI — p1, domain:ui, one card. Cause: ADR-0084:47 declares the Access editor as "a permission matrix (roles × objects × CRUD + record scope)"; PermissionMatrixEditor.tsx models C/R/U/D/Tr/VA/MA (:273–283) and carries readScope/writeScope through a save untouched (:961–975, the #6605 repair) — the column was never built, and ADR-0090's ObjectUI list (:642) names OWD context, not scope. Lands in: PermissionMatrixEditor.tsx (two selects per object row, enum ObjectAccessScopeSchema permission.zod.ts:18), the permission-set and position pages (wire previews/PermissionPreview.tsx:78–137, which already renders both scopes, read-only), and the hint source platform-capabilities.ts:224 ("hierarchy scopes ship in the enterprise edition"). Constraints already enforced server-side and only mirrored in the UI: View All beside a read depth and Modify All beside either depth are refused on the BASE schema (permission.zod.ts:632, #16870) — the selects grey out, they do not re-implement. Unlicensed hierarchySecurity: the three hierarchy values stay selectable with the hint "requires enterprise; otherwise owner-only" (the open runtime fails closed, sharing-service.ts:377). Size: ui M, 2–3 dev-days, no spec change, no ADR change.

    2. A delegated admin cannot enter Setup; the scope block gives no hint — p2, domain:ui + docs, one card. Cause: AdminScopeSchema scopes WHAT may be assigned, not the doors the assigner needs: Setup entry is the capability setup.access (capabilities.ts:49), and choosing a person or unit needs sys_user / sys_business_unit / sys_business_unit_member read plus sys_user_position CRUD — the docs example lists the object grants (delegated-administration.mdx:26–36) but not setup.access. The UI block (PermissionAdvancedFacets.tsx:405–420, :677) gates only the business-unit boundary (objectui#9464). Fix: in the block, a one-line checklist of the companion grants with a "grant these" action that adds them to the same permission set (one save, no new key), and the docs page gains setup.access. Not a server change: the server is right to refuse. Size: ui S + docs S.

    3. The refusal toast drops the server's reason — p2, two halves, domain:server first. Cause, deliberate on the UI side: isPermissionError (ui/packages/react/src/utils/error-message.ts:202) treats every 403 / PERMISSION_DENIED as one class and the form renders it as the fixed line form.noPermissionToSave (flowResponse.ts:336–369, ruling objectstack#24914 B′) — a rule that exists so a denial never leaks why. The delegated-admin gate throws PermissionDeniedError for scope violations (delegated-admin-gate.ts:403–503) with reasons that describe the CALLER's own scope (:1136–1179, "outside the delegated subtree", "not in the scope's allowlist") — safe to show, but indistinguishable on the wire from a denial that must stay opaque. Fix shape: the gate answers its own code (e.g. DELEGATION_SCOPE_VIOLATION, registered in the ADR-0112 ledger, status 403) and the UI renders that code's message while PERMISSION_DENIED keeps the fixed line. Not "show every 403 message": that reverses 24914 B′. Size: server S (code + ledger row + pins) → ui S.

    4. A new position saves as a draft the list does not show — p2, domain:ui, one card (confirm the premise first). Reading: the editor writes mode: 'draft' only when the item sits under a packageId (PermissionMatrixEditor.tsx:1104, ADR-0086 P0/P2: authoring inside an installed package is a draft until publish), and EmbeddedItemEditor.tsx:167 does the same; the Setup list reads published items. So the measured path is probably a position created inside a package's scope, and the gap is the list, not the save. Fix: the list shows the caller's drafts with an "unpublished" badge and a Publish action (the drafts ledger is the read #26056 is fixing for members; the position row rides that). Premise for the dev: reproduce which packageId the Setup "New" carried; if none, the draft write itself is the defect. Size: ui S.

    5. No end-user "share this record with…" entry — p3, domain:ui, filed for later, not this period. Reading: the server door exists (GET/POST /api/v1/data/:object/:id/shares, rest-route-ledger.ts:403–404, family record-shares); the record page's Share item copies a link (RecordDetailView.tsx:3436–3464). Rule-based sharing through Setup works (card: 3/3). A dialog (user / team / position / business unit × read / edit) is a permanent surface with zero measured customer pull; the door being there makes this a gap card, not a capability card. Size when picked up: ui M.

    6. New shows for a user without Create — p3, domain:ui, one card (defect). Reading: the list already gates New by resolveAffordance('listNew') → crud.create from the object's effective API operations AND perms.can(object, 'create') (ObjectDataPage.tsx:503, affordanceGrants.ts:293–310, objectstack#3391). The card still saw New for the auditor, so one of the two inputs answered true: the most likely is can() before /me/permissions is loaded (resolveFieldAffordance returns true when !perms.isLoaded, :321; whether can does the same was not read line by line) or View All cascading into the create verdict. The dev reproduces with the auditor set and pins the verdict. Size: ui S.

    7. Field-type search: Enter picks the current type — p3, domain:ui, one card. Reading: inspectors/FieldTypePicker.tsx:40–47 documents "Enter picks the highlighted type" and the list is a cmdk Command; the current type is rendered as the checked item, and with the current value kept at the top of the filtered list the highlight stays on it after typing. Fix: on a non-empty query, highlight the first MATCH (or drop the current type to its group) so Enter selects what the user typed. Size: ui S, one test.

    8. Business-unit member title shows raw ids — p3, domain:server (platform-objects), one card. Cause is a formula, not the renderer: sys-business-unit-member.object.ts:52–55 — display_title is record.user_id + ' in ' + record.business_unit_id and titleFormat: '{user_id} in {business_unit_id}' (:38) — so the title IS the ids by declaration. Fix: a title over the two lookups' display names; whether the formula language can read a lookup's name was not found in formula/src/stdlib.ts (no lookup-display helper located), so the dev names the mechanism (formula over related display names, or a stamped title kept by hook on both ids) before writing. Size: server S.

    9a. Runtime save of an unlicensed hierarchy scope is silent — p2, domain:server, one card (premise verified). The compile path does refuse: stack.zod.ts:4818–4845 validateHierarchyScopeCapability — a stack using unit / unit_and_below / own_and_reports without requires: ['hierarchy-security'] errors with STACK_HIERARCHY_SCOPE_CAPABILITY_REQUIRED ("the open runtime would silently fail closed to owner-only; the metadata would lie, ADR-0049"). Its only reader is stack.zod.ts; the metadata PUT door runs no such check, so the same declaration is accepted at runtime with _diagnostics.valid: true and falls closed to owner-only — exactly the inert metadata ADR-0078 forbids. Fix: the runtime save door asks the kernel whether hierarchy-security is mounted and, when it is not, answers a diagnostic that names the enterprise requirement (refuse vs warn is the one choice for the maintainer; the seat reads ADR-0078 as refuse-on-publish, warn-on-draft). Size: server S–M. Lives in the framework: a self-hosted community runtime has the same silence; cloud's community mode is one host of it.

    9b. explain names the wrong cause when the resolver is absent — p2, domain:server, one card. Reading: explain-engine.ts:2040–2075 builds the depth record from sharingFilter / depthAdmitsRow / sharingMatches; when no hierarchy resolver is mounted the open sharing service answers owner-only (sharing-service.ts:377–378), so sharingMatches === false and the branch at :2063 prints "outside the caller's 'unit_and_below' read depth" — true of the computation, false about the configuration. Fix: a resolver-absent branch ahead of it ("hierarchy resolver absent — requires enterprise; depth evaluated as 'own'"), one pinned explain cell. Size: server S. Pairs naturally with 9a in one dispatch.

    Checklist: cloud docs/qa/platform-checklist/coverage.json:42 holds hierarchySecurity WAIVED 2026-10-24 with no item; this run's 33 scenarios are the material. One domain:devx p2 card in cloud (cloud:checklist-author), after the maintainer's letters.

    维护者速读(逐项)

    # 一句话成因 落点 改法 大小 / 等级
    1 ADR-0084 声明了「矩阵 + 记录范围」,矩阵编辑器只模型了 CRUD/VA/MA,范围列从未建(#6605 只保住不丢) ui 矩阵编辑器 + 权限集/岗位页 每行两个范围下拉;按 #16870 置灰互斥项;未授权提示「需企业版,否则按本人」;PermissionPreview 只读接入两页 ui M · p1
    2 分级范围只管「能分什么」,不管「进门要什么」:setup.access 与 sys_user/sys_business_unit/成员读、sys_user_position CRUD 是配套授权,文档缺 setup.access,UI 块无提示 ui 高级面板 + 文档 块内列出配套授权并提供「一键补全」到同一权限集;文档补 setup.access ui S + docs S · p2
    3 UI 故意把一切 403 显示成固定一句(裁决 #24914 B′,防泄露原因);分级管理员的越界原因是关于操作者自己的,安全可显示,但线上与普通拒绝无法区分 先 server 后 ui 分级门禁自己的错误码(ADR-0112 台账登记)+ UI 对该码显示服务端文案;普通 PERMISSION_DENIED 不变 server S → ui S · p2
    4 在包范围内新建的元数据按 ADR-0086 存为草稿直到发布;列表只读已发布 ui 列表 列表显示本人草稿 + 「未发布」标记 + 发布入口(搭 #26056 的草稿台账读);dev 先复现 Setup「New」带的 packageId ui S · p2
    5 服务端 /data/:object/:id/shares 门已有,记录页 Share 只是复制链接 ui 记录页 「共享给…」对话框;本期不做,立 p3 排队 ui M · p3(后排)
    6 列表已按 /me/permissions 判定 New,但审计员仍看到:疑为权限未加载时 can() 回 true 或 View All 级联;需复现 ui 列表 复现审计员权限集,钉判定 ui S · p3
    7 类型搜索是 cmdk 列表,当前类型置顶且保持高亮,回车选高亮 ui FieldTypePicker 有搜索词时高亮第一个匹配项 ui S · p3
    8 标题就是公式 user_id + ' in ' + business_unit_id platform-objects 标题改为两个 lookup 的显示名(公式能否读关联显示名未定位到,dev 先定机制) server S · p3
    9a 编译期有 validateHierarchyScopeCapability 拒绝,运行时 PUT 门不跑它,静默回落 owner-only(ADR-0078 禁止的惰性元数据) 框架 metadata 保存门 保存时问内核 hierarchy-security 是否挂载,未挂载给诊断并指明企业版(发布拒绝 / 草稿告警由您定) server S–M · p2
    9b explain 的 depth 分支只看共享服务的 owner-match 结果,解析器缺席时得出「在范围之外」的错误原因 框架 explain 解析器缺席分支先行:「hierarchy resolver absent — requires enterprise」 server S · p2(与 9a 同派)
    附 hierarchySecurity 在 cloud 测试清单 WAIVED、无测试项 cloud devx 把本轮 33 个场景整理成正式测试项 devx S–M · p2

    回批格式:26059 Q1 是/否 Q2 p3/本期 Q3 分卡/合并 Q4 框架/cloud,如对任一项的等级或改法有改动,逐项点名即可。

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #396 item 1 · letter Q1 是 / Q2 不做 / Q3 分拆 / Q4 框架 · maintainer 「共享给指定人 不考虑,其他是否建议分拆issue」「哪些需要cloud访问权限的可以交给cloud 车道」 2026-10-26T12:40Z

    Recorded by the director seat (summon #51 continuation, session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708; brief 6415795834, item-by-item reading 6415971852). The maintainer's two sentences, taken together with the seat's split proposal they answer: item 5 is out; the rest is split by landing site; whatever needs cloud access goes to the cloud lane.

    Q1 → 是. Item 1 is the p1 child: the permission matrix gains a read-scope and a write-scope select per object row (ObjectAccessScopeSchema, permission.zod.ts:18), greyed by the #16870 pairs the BASE schema already refuses (:632), the three hierarchy values carrying the hint "requires enterprise; otherwise owner-only" (platform-capabilities.ts:224); previews/PermissionPreview.tsx wired read-only into the permission-set and position pages. No spec key, no ADR change (ADR-0084:47 already declares it).

    Q2 → 不做. Item 5 (an end-user "share this record with…" dialog) is not filed and not built; rule-based sharing and the server door stay as they are. A future card needs a customer asking.

    Q3 → 分拆, by landing site — nine child cards, filed by the claiming seat of this parent (the domain:ui seat, with the triage seat first-touching each), every one carrying Ruling-ref: naming this comment and Part of #26059:

    1. ui p1 — item 1 as above.
    2. server p2 — items 9a + 9b in one dispatch: the runtime metadata save door asks the kernel whether the hierarchy resolver is mounted (the open sharing service fails closed without it, sharing-service.ts:377) and answers a diagnostic naming the enterprise requirement when it is not — the compile path already refuses (stack.zod.ts:4818–4845, STACK_HIERARCHY_SCOPE_CAPABILITY_REQUIRED), so this makes the runtime path match it (ADR-0078 / ADR-0049); shape unless the maintainer says otherwise: refuse on publish, warn on a draft save. 9b: explain-engine.ts:2040–2075 gains a resolver-absent branch ahead of the depth verdict ("hierarchy resolver absent — requires enterprise; depth evaluated as 'own'"). Framework code only: cloud mounts SecurityEnterprisePlugin by licence (apps/objectos-ee/objectstack.config.ts:1333, :1597) and changes nothing.
    3. server p2 — item 3, server half: the delegated-admin gate (delegated-admin-gate.ts:403–503, reasons :1136–1179) answers its own code (e.g. DELEGATION_SCOPE_VIOLATION, status 403, ADR-0112 ledger row), distinct from PERMISSION_DENIED.
    4. ui p2 — item 3, ui half, Blocked-by: card 3: the toast renders that code's server message; PERMISSION_DENIED keeps form.noPermissionToSave (ruling 24914 B′ unchanged).
    5. ui + docs p2 — item 2: the Delegated Admin Scope block lists the companion grants (setup.access, sys_user / sys_business_unit / sys_business_unit_member read, sys_user_position CRUD) with a one-click "grant these" into the same set; delegated-administration.mdx gains setup.access.
    6. ui p2 — item 4: the metadata list shows the caller's drafts with an "unpublished" badge and a Publish action (beside fix(app-shell): no drafts-ledger read for a member, no published read for a new draft #26056's drafts-ledger read); premise first: which packageId the Setup "New" carried (PermissionMatrixEditor.tsx:1104, ADR-0086 P0/P2) — a draft written with none is itself the defect.
    7. ui p3 — item 6: New hidden or greyed without Create; reproduce with the auditor set and pin the resolveAffordance('listNew') verdict (affordanceGrants.ts:293–310; suspected can() before /me/permissions loads).
    8. ui p3 — item 7: FieldTypePicker highlights the first match when a query is present, so Enter picks it.
    9. server p3 — item 8: sys_business_unit_member's title over the two lookups' display names instead of record.user_id + ' in ' + record.business_unit_id (sys-business-unit-member.object.ts:38, :52–55); the dev names the mechanism first.

    Q4 → 框架, as card 2 above. Cloud lane (the maintainer's second sentence): what needs cloud access is the verification, not the code — one domain:devx p2 card in objectstack-ai/cloud (cloud:checklist-author): turn this run's 33 scenarios into docs/qa/platform-checklist/ items, retire the hierarchySecurity WAIVED row (coverage.json:42), and re-run them on objectos-ee in both the licensed and the community posture as each child lands in a pin cloud carries. That card is the end-to-end re-verification of children 1–6; it carries Blocked-by: none (authoring the items starts now) and its run record waits on the pins.

    Workload of the chosen letters (reading 6415971852): ui M + ui S ×5 + docs S; server S–M + S + S; cloud devx S–M. Item 5: 0.

    Execution: needs-user-decision → pm:queue in this act (domain:ui, p1, area:access); pm:awaiting-maintainer removed in the same stroke (half-state beside the decision label). This card is the parent: the first claimant files the nine children and the cloud card request, relabels this card tracking with a checklist comment, and keeps it open until the last child closes. Decisions are anchored here; a question born in a child goes on that child linking here.

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop · parent filing · 2026-10-26T14:54Z
    Session: session_01SMps7wq7rBTk53e8KTSEup
    Account: os-steve
    Domain: domain:ui
    Seat: domain:ui#2
    Role: first claimant of this parent, per the 2026-10-26T12:40Z ruling (Q3 → 分拆). No branch and no code on this card.
    Scope of this claim:

    1. File the nine child cards named in the ruling. Each one carries Ruling-ref: (this ruling comment) and Part of #26059, and the triage seat first-touches it.
      • The ui children (1, 4, 5, 6, 7, 8) are filed domain:ui.
      • The server children (2, 3, 9) are filed domain:server.
    2. Request the cloud-lane domain:devx verification card (objectstack-ai/cloud, cloud:checklist-author) through the coordinator. This seat has no access to the cloud repository.
    3. Relabel this card tracking, post a checklist comment, and keep it open until the last child closes.
      Writes: only through the relay tools (issue-create.mjs, post-stamped.mjs, label-write.mjs). ⛔ No GitHub MCP writes.
      Serial constraints cleared: none; this is filing only.
  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Tracking: #26059's children, per the ruling's Q3 (分拆)

    Ruling-ref: 6416233738 (#26059 (comment))

    Each child carries Part of #26059 and that Ruling-ref:. The triage seat first-touches each one. Item 5 (an end-user "share this record with…" dialog) is not filed, per Q2 (不做).

    This card stays open as tracking until the last child above closes.

  7. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    2026-10-26T20:37Z · domain:server seat 2 · child 9 (Q3, #26195) landed: PR #26300 merged as 1f40201977 (landing record on #26195). This tracking card stays open for its other children.


    Generated by Claude Code

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

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:uipriority:p1High: required for production / M2tracking

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions