Repository navigation
RBAC + 组织架构权限:真实 UI 测试结论 — 后端判定全部正确,但「记录范围(readScope/writeScope)」在 Setup/Studio 无法配置与查看,另有 7 项管理体验缺口(待 PM 确认排期) #26059
Copy link
Copy link
Open
Labels
area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:uipriority:p1High: required for production / M2High: required for production / M2tracking
Description
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/cloudapps/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」一节只描述代码声明方式。
- ADR-0084(Accepted 2026-07-01)规定 Access 支柱的编辑器为「a permission matrix (roles × objects × CRUD + record scope)」——
- 影响:后端、企业版解析器、explain 都已完整支持;只有界面缺失。应用包开发者在代码里声明不受影响;客户管理员自建/调整权限集时无法设置组织架构范围,也看不到现状——这是本功能面向客户的核心入口。
- 建议:矩阵每个对象行增加「读范围」「写范围」两个下拉(
own/own_and_reports/unit/unit_and_below/org)。需遵守现有约束:勾「查看全部」时不可设读范围、勾「修改全部」时读写范围都不可设(服务端会拒存,见 A permission set accepts a hierarchyreadScopebesideviewAllRecords: 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 是否按 ADR-0084 补齐,定为本期 P1?
- 问题 5(终端用户手动共享入口)是否纳入本期范围?
- 其余 P2/P3 是否合并为一张体验修复卡,还是分别建卡?
- 问题 9 归属框架(explain / 元数据保存诊断)还是 cloud(objectos-ee 社区模式提示)?
复现材料
测试脚本与 140+ 张截图保存在执行会话的临时目录,未提交仓库;需要时可整理为 cloud docs/qa/platform-checklist/ 的正式测试项(coverage.json 中 hierarchySecurity 当前为 WAIVED,无测试项覆盖)。
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:uipriority:p1High: required for production / M2High: required for production / M2tracking
Activity
objectstack-fleet commentedon Oct 26, 2026
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 preservesreadScope/writeScopeand 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:
domain:ui. Two scope selects per object row, with the existing all-records exclusions (A permission set accepts a hierarchyreadScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870) and an unlicensed-hierarchySecurityhint. WirePermissionPreviewread-only into the permission-set and position pages. About 2–3 dev-days.domain:uiplus docs, p2.domain:ui, p2.domain:ui, p2.domain:ui, p3.domain:ui, p3.domain:server, p3.domain:server, p2, two cards. 9a: the runtime save of an unlicensed hierarchy scope is silent wheredefineStackrefuses it. 9b: explain names the wrong cause when the resolver is absent.hierarchySecurityis WAIVED incoverage.jsonwith no item. Adomain:devxp2 card turns this run into checklist items.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
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/maincdd4eea9; the test conclusions and the triage leanings 6414323834 are not restated):PermissionMatrixEditor.tsx:961–975only carriesreadScope/writeScopethrough a save (the type-alias-convention.pin.test.ts 的分节注释写着 "717 isomorphic aliases",实际 822 —— 唯一没被计数断言盖住的数字 #6605 repair) and offers no control. ADR-0090's ObjectUI landing list (:642) names "permission matrix gains OWD context per object row" and no scope column — the requirement sits in ADR-0084 and was never carried into 0090's list, as the card says. So item 1 is a declared-but-unbuilt editor, not a new capability: no ADR change, the contract already exists.ObjectAccessScopeSchema=own | own_and_reports | unit | unit_and_below | org(permission.zod.ts:18); the contradictory-pair refusal (A permission set accepts a hierarchyreadScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870: View All / Modify All beside a depth) is on the BASE schema (:632), so the server already refuses what the UI must grey out;previews/PermissionPreview.tsx:78–137already renders both scopes (SCOPE_ORDER,own+reports,unit+below) and is not wired into the permission-set or position pages.platform-capabilities.ts:224: "ADR-0057 hierarchy scopes ship in the enterprise edition" — the source for the unlicensed hint.git grep -iE 'hierarchy.{0,40}(licen|entitl|enterprise)'onpackages/{spec,core,plugins/plugin-security,objectql}/srcfinds notes only, no compile-time refusal of an unlicensed hierarchy scope indefineStack; where that refusal lives (lint, or the cloudsecurity-enterprisepackage) is for the dispatched dev to name before 9a is built as "make the runtime match the compile path".docs/qa/platform-checklist/coverage.json:42—hierarchySecurityWAIVED 2026-10-24, no item; this run is the material for the checklist card (domain:devx, in cloud,cloud:checklist-author).needs-user-decision+pm:awaiting-maintainertogether; the second leaves in the ruling's stroke.os-decision-facets
readScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 置灰互斥项、未授权时提示,错配置在保存前被拒;9a/9b 让「声明了但不生效」在保存与 explain 上响亮。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.objectstack-fleet commentedon Oct 26, 2026
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/maincdd4eea9; 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.tsxmodels C/R/U/D/Tr/VA/MA (:273–283) and carriesreadScope/writeScopethrough 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, enumObjectAccessScopeSchemapermission.zod.ts:18), the permission-set and position pages (wirepreviews/PermissionPreview.tsx:78–137, which already renders both scopes, read-only), and the hint sourceplatform-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. UnlicensedhierarchySecurity: 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:AdminScopeSchemascopes WHAT may be assigned, not the doors the assigner needs: Setup entry is the capabilitysetup.access(capabilities.ts:49), and choosing a person or unit needssys_user/sys_business_unit/sys_business_unit_memberread plussys_user_positionCRUD — the docs example lists the object grants (delegated-administration.mdx:26–36) but notsetup.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 gainssetup.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:serverfirst. Cause, deliberate on the UI side:isPermissionError(ui/packages/react/src/utils/error-message.ts:202) treats every 403 /PERMISSION_DENIEDas one class and the form renders it as the fixed lineform.noPermissionToSave(flowResponse.ts:336–369, ruling objectstack#24914 B′) — a rule that exists so a denial never leaks why. The delegated-admin gate throwsPermissionDeniedErrorfor 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 whilePERMISSION_DENIEDkeeps 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 writesmode: 'draft'only when the item sits under apackageId(PermissionMatrixEditor.tsx:1104, ADR-0086 P0/P2: authoring inside an installed package is a draft until publish), andEmbeddedItemEditor.tsx:167does 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 whichpackageIdthe 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, familyrecord-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 byresolveAffordance('listNew')→crud.createfrom the object's effective API operations ANDperms.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 iscan()before/me/permissionsis loaded (resolveFieldAffordancereturns true when!perms.isLoaded,:321; whethercandoes 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–47documents "Enter picks the highlighted type" and the list is acmdkCommand; 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_titleisrecord.user_id + ' in ' + record.business_unit_idandtitleFormat: '{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 informula/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–4845validateHierarchyScopeCapability— a stack usingunit/unit_and_below/own_and_reportswithoutrequires: ['hierarchy-security']errors withSTACK_HIERARCHY_SCOPE_CAPABILITY_REQUIRED("the open runtime would silently fail closed to owner-only; the metadata would lie, ADR-0049"). Its only reader isstack.zod.ts; the metadataPUTdoor runs no such check, so the same declaration is accepted at runtime with_diagnostics.valid: trueand falls closed to owner-only — exactly the inert metadata ADR-0078 forbids. Fix: the runtime save door asks the kernel whetherhierarchy-securityis 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–2075builds the depth record fromsharingFilter/depthAdmitsRow/sharingMatches; when no hierarchy resolver is mounted the open sharing service answers owner-only (sharing-service.ts:377–378), sosharingMatches === falseand the branch at:2063prints "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:42holdshierarchySecurityWAIVED 2026-10-24 with no item; this run's 33 scenarios are the material. Onedomain:devxp2 card in cloud (cloud:checklist-author), after the maintainer's letters.维护者速读(逐项)
PermissionPreview只读接入两页setup.access与sys_user/sys_business_unit/成员读、sys_user_positionCRUD 是配套授权,文档缺setup.access,UI 块无提示setup.accessPERMISSION_DENIED不变/data/:object/:id/shares门已有,记录页 Share 只是复制链接/me/permissions判定 New,但审计员仍看到:疑为权限未加载时can()回 true 或 View All 级联;需复现user_id + ' in ' + business_unit_idvalidateHierarchyScopeCapability拒绝,运行时PUT门不跑它,静默回落 owner-only(ADR-0078 禁止的惰性元数据)hierarchy-security是否挂载,未挂载给诊断并指明企业版(发布拒绝 / 草稿告警由您定)hierarchySecurity在 cloud 测试清单 WAIVED、无测试项回批格式:
26059 Q1 是/否 Q2 p3/本期 Q3 分卡/合并 Q4 框架/cloud,如对任一项的等级或改法有改动,逐项点名即可。objectstack-fleet commentedon Oct 26, 2026
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.tsxwired 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:uiseat, with the triage seat first-touching each), every one carryingRuling-ref:naming this comment andPart of #26059: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–2075gains a resolver-absent branch ahead of the depth verdict ("hierarchy resolver absent — requires enterprise; depth evaluated as 'own'"). Framework code only: cloud mountsSecurityEnterprisePluginby licence (apps/objectos-ee/objectstack.config.ts:1333,:1597) and changes nothing.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 fromPERMISSION_DENIED.Blocked-by:card 3: the toast renders that code's server message;PERMISSION_DENIEDkeepsform.noPermissionToSave(ruling 24914 B′ unchanged).setup.access,sys_user/sys_business_unit/sys_business_unit_memberread,sys_user_positionCRUD) with a one-click "grant these" into the same set;delegated-administration.mdxgainssetup.access.packageIdthe Setup "New" carried (PermissionMatrixEditor.tsx:1104, ADR-0086 P0/P2) — a draft written with none is itself the defect.resolveAffordance('listNew')verdict (affordanceGrants.ts:293–310; suspectedcan()before/me/permissionsloads).FieldTypePickerhighlights the first match when a query is present, so Enter picks it.sys_business_unit_member's title over the two lookups' display names instead ofrecord.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:devxp2 card in objectstack-ai/cloud (cloud:checklist-author): turn this run's 33 scenarios intodocs/qa/platform-checklist/items, retire thehierarchySecurityWAIVED 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 carriesBlocked-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:queuein this act (domain:ui, p1,area:access);pm:awaiting-maintainerremoved 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 cardtrackingwith 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.objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop · parent filing · 2026-10-26T14:54Z
Session:
session_01SMps7wq7rBTk53e8KTSEupAccount:
os-steveDomain:
domain:uiSeat:
domain:ui#2Role: 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:
Ruling-ref:(this ruling comment) andPart of #26059, and the triage seat first-touches it.domain:ui.domain:server.domain:devxverification card (objectstack-ai/cloud,cloud:checklist-author) through the coordinator. This seat has no access to the cloud repository.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.
objectstack-fleet commentedon Oct 26, 2026
Tracking: #26059's children, per the ruling's Q3 (分拆)
Ruling-ref: 6416233738 (#26059 (comment))
Each child carries
Part of #26059and thatRuling-ref:. The triage seat first-touches each one. Item 5 (an end-user "share this record with…" dialog) is not filed, per Q2 (不做).readScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 pairs, with an unlicensed hint;PermissionPreviewread-only on the permission-set and position pages (item 1, Q1)PERMISSION_DENIED(item 3, server half)PERMISSION_DENIEDkeepsform.noPermissionToSave(item 3, ui half; Blocked-by delegated-admin gate: scope violations answer PERMISSION_DENIED, so a client cannot tell them from an opaque denial (own registered code) #26189)delegated-administration.mdxgainssetup.access(item 2)packageIdpremise is settled (item 4)listNewverdict (item 6)FieldTypePickerhighlights the first match on a query, so Enter picks it (item 7)sys_business_unit_membertitle is built over the two lookups' display names; mechanism named first (item 8)domain:devx,cloud:checklist-author). It turns this run's 33 scenarios intodocs/qa/platform-checklist/items, retires thehierarchySecurityWAIVED row (coverage.json:42), and re-runs them in the licensed and community postures as children 1–6 land. Blocked-by: none.This card stays open as
trackinguntil the last child above closes.objectstack-fleet commentedon Oct 26, 2026
2026-10-26T20:37Z ·
domain:serverseat 2 · child 9 (Q3, #26195) landed: PR #26300 merged as1f40201977(landing record on #26195). This tracking card stays open for its other children.Generated by Claude Code