Repository navigation
docs(adr): ADR-0090 D12 names the organization administrator's assignment boundary; ADR-0131 D3 notes it - #24163
Conversation
…ment boundary; ADR-0131 D3 notes it Ruling of record 6228728551 (letter B): an organization administrator creates and removes sys_user_position / sys_user_permission_set rows for its own members when every distributed set has no wildcard superuser bit, no adminScope and only scope 'org' capabilities. D12 now names three kinds of administrator (tenant-level, organization-level, delegated); strict containment and the everyone / guest anchors are unchanged. The eight shipped sets are classified in the note; none is left unsettled, so the ruling's fallback denylist names no set. Claude-Session: https://claude.ai/code/session_01HSDGREvqxQUt7K7pjHXMyC Co-authored-by: Claude <noreply@anthropic.com>
The shipped organization_admin still keeps the assignment tables read-only until the boundary is enforced; the ADR-0131 note names whose admins it means. Claude-Session: https://claude.ai/code/session_01HSDGREvqxQUt7K7pjHXMyC Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs read: card #24135 body and both comments (claim ① Derived judgmentsThe ruled boundary (items 1, 3, 4) against the amended D12 text.
The six "precisions" of the PR body.
The shipped-set table against the code (
Other factual sentences of the new text, checked: ② Semver levelADR prose only; two files under ③ Boundary flags
Prescription for the next head: in the D12 note's measured paragraph, name the three D10 ceilings in the ruling's item-4 denylist with the reason (an agent ceiling's bound is the delegator's grants, not the wall; held by a person it carries no row scope on the Layer-0-inert projection tables), drop "names no set at this date", and move the three from "Inside" to "Outside (denylist)". Keep everything else. The seat files the general-class question for the maintainer in the same act. Implemented-by: VERDICT: FAIL Generated by Claude Code |
…eilings The contract review on the previous head found the three conditions mis-settle the shipped agent ceilings: their bound is the delegating principal's grants (D10 rule 1), not the organization wall, so held by a person in a walled deployment with no platform baseline the reading ceilings reach every organization's rows on the Layer-0-inert SCIM projection tables. The ruling's fallback denylist now names mcp_agent_data_read, mcp_agent_data_write and, by kind, mcp_agent_restricted. The general case (an authored wildcard read with no row scope on a Layer-0-inert object, no baseline) is held for a maintainer decision and not settled here. The reserved identity names are refused from every writer. Claude-Session: https://claude.ai/code/session_01HSDGREvqxQUt7K7pjHXMyC Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSDGREvqxQUt7K7pjHXMyC Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs read: card #24135 body and all three comments (claim ① Derived judgmentsThe ruled boundary (items 1, 3, 4) against the amended D12 text at this head.
The previous record's prescription, carried out exactly. This round's delta ( The new reason sentence, against the code.
The general-case sentence, against #24164. The note says: "The general case — an authored set with a The The six precisions of the PR body, re-checked.
The classification table against the code (
Shipped positions: the four identity names refused for every writer (above); the two anchors refused unconditionally on Other factual sentences of the new text, checked. "The shipped ADR-0131 D3's note. Unchanged this round. It points at D12's 2026-10-17 amendment as the rule D3's sentence runs under and restates the three conditions as what an organization's admins assign. The restatement is a necessary condition — true of every set the boundary admits — and the denylist reaches D3 through "runs under that amendment's boundary", which is the note whole. Consistent; no second edit owed. The dev's deviation 3 — is the pointer in the rule paragraph needed? No. The denylist is binding on #24136 as the text stands, for three reasons read on the head: (i) ruling item 4 itself makes the denylist part of the boundary ("the ADR card names an explicit denylist for that set instead of widening the rule"), and the note names the denylist's three members and its growth rule in the same dated amendment under D12 — a dated note is the ADR's rules text, and the "Measured on" heading frames the evidence, not the standing of the sentence "therefore names those three sets"; (ii) the rule paragraph's "Outside the boundary, by the conditions or by name:" list is not phrased as exhaustive and nothing in it admits a set the measured paragraph excludes, so the two paragraphs do not contradict; (iii) ADR-0131's note runs D3 under "that amendment's boundary", the whole note. The boundary is therefore stated exactly across the note: three conditions, by-name exclusions, item-4 denylist — the ruled boundary and nothing wider. The pointer would be a readability aid for #24136's implementer, not a correction; this record judges the head as it stands and does not require it. The seat can give #24136's implementer the same aid at no cost to this head by naming the denylist (and the position-name fold) in that card's dispatch text. ② Semver levelADR prose only: two files under ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读改了什么在权限模型 ADR-0090 的 D12(管理权限)里加了一条带日期的修订,并在 ADR-0131 D3 下加了一行注记。纯文字:不改代码,不加 spec 键。 修订把管理者分成三类:
组织级管理员可以给本组织成员增加或移除岗位与权限集,但只限「被组织墙约束住」的集,即同时满足三条:
另外,三个给 AI 代理用的 MCP 上限权限集列入您裁决第 4 条预留的黑名单,组织管理员不能把它们分给人。 为什么改这是您 10 月 17 日对 #23960 裁决 B(「同意」)的第一步。ADR-0131 D3 承诺「组织的管理员给本组织成员分配」,但代码为了防止组织管理员给自己绑 实测出货的 8 个权限集,三条件能判定其中 5 个(2 个在内,3 个在外)。剩下 3 个 MCP 代理上限集,按三条件看似在内,但它们本是配给 AI 代理的:约束来自委托人自己的权限,不是组织墙。直接分给一个人时,在不配平台基线的部署里,它能读到其他组织的 SCIM 身份数据,而分配它的组织管理员自己都读不到。所以按裁决第 4 条把它们列入黑名单。 风险与代价(含回滚)只改两份 ADR 的文字,今天的产品行为不变:两张分配表仍然只读,直到 #24136 落地。 措辞上的几处精确化全部是收紧,没有放宽:
还有一个更一般的口子,本 ADR 没有定:任何自建的「通配只读、没有行级范围」的权限集,在不配平台基线的部署里,同样能碰到那几张没有组织列的 SCIM 表。是加第四个条件,还是把保证写成「以平台基线为前提」,已单独立成待您决策的 #24164,本 PR 不替您决定。 回滚:直接 revert 本 PR,不涉及数据和发布。 席位意见建议批准。
你要做的(一个动作)在 PR #24163 上点 Approve( 席位核对ACCEPT · The seat's own reading, on top of the
Generated by Claude Code |
Fixes #24135
Clause-②: no
Summary
Executes the maintainer's ruling of record
6228728551on #23960 (letter B, 「同意」): the boundary for what an organization administrator may assign is declared in the ADRs first, so theplugin-securitycard (#24136,Blocked-by:this one; it remains open) can enforce it.docs/adr/0090-permission-model-v2-concept-convergence.md): one status-line entry (the ADR's own convention for amendments, beside the 2026-10-15 entry for ruling6181988435) and one dated note under D12,Amended 2026-10-17— the form D11's 2026-10-14 note uses. D12 now names three kinds of administrator (tenant-level, organization-level, delegated); the organization-level kind creates and removessys_user_position/sys_user_permission_setrows for its own organization's members when every distributed set has no wildcard superuser bit, noadminScopeand onlyscope: 'org'capabilities;admin_full_access, platform sets,organization_adminitself and every delegated set stay outside; organization-administrator status is conferred by membership grade only. Strict containment and theeveryone/guestanchors are unchanged. Ruling item 4's denylist names the three D10 agent ceilings, which the three conditions mis-settle; the general case behind them is held for the maintainer's decision on [Decision] The organization administrator's assignment boundary: an assignable set with a wildcard read and no row scope reaches other organizations' SCIM rows in a deployment with no platform baseline #24164. The D12 text above the note is not rewritten.docs/adr/0131-total-organization-ownership-no-null-organization-id.md): one dated note under D3's assignment paragraph, pointing at the D12 boundary as the rule that sentence runs under. It inherits the denylist by reference.packages/speckey, no test, no migration text. The v18 entry'sreplacementtext (18.walled-catalog-tenant-writes-retired) stays as published.Patch round 1 (contract review
6232429140on headb0173e29c, FAIL)The review found precision 5 of the previous head wrong for this boundary: the three D10 agent ceilings pass the three conditions mechanically but are not wall-bounded on their own. Re-measured here before editing, on
origin/maineda6f14b7b:mcp_agent_data_readisobjects: { '*': { allowRead: true } }with norowLevelSecurity(default-permission-sets.ts:1427–:1435); the seven SCIM projection objects aretenantField: null,reach: outinscripts/platform-object-tenancy-census.json;fallbackPermissionSet: nullcomposes the human baseline to[](app-default-permission-set.ts:40–:57). The head61c8a5d34therefore:mcp_agent_data_read,mcp_agent_data_writeandmcp_agent_restricted(the last by kind: an agent ceiling is not a person's set) from "Inside" to "Outside (denylist)" in the D12 note's measured paragraph, with the reason — an agent ceiling's bound is the delegating principal's own grants (D10 rule 1), not the organization wall; held by a person as a direct grant, the two reading ceilings carry no row scope on the Layer-0-inert SCIM projection tables, and in a walled deployment with no platform baseline they read every organization's rows there, a read the granting organization administrator does not hold;'*'read and no row scope on a Layer-0-inert readable object, in a deployment with no platform baseline — is not settled by the note and is held for the maintainer's decision on [Decision] The organization administrator's assignment boundary: an assignable set with a wildcard read and no row scope reaches other organizations' SCIM rows in a deployment with no platform baseline #24164. No fourth condition is written;reserved_identity_positiontakes no provenance exemption (sys-user-position.object.ts:225–:265).The rule paragraph of the note (the three conditions and the by-name exclusions) is unchanged; the denylist lives in the measured paragraph, as the review prescribed.
Mechanism assumptions, measured on
origin/maineda6f14b7b· **Amended** (…)status-line entries (:4–:8), and a superseding change also gets a dated blockquote in the decision itself (D11's> **Amended 2026-10-14 …**; D3 / D5 / D8 carry> **Note (2026-10-14) …**). The 2026-10-15 D12 amendment is status-line only. This PR uses both: the status line, and anAmended 2026-10-17blockquote at the end of D12 (D12 now at:515).:322–:328. ADR-0131 itself already uses> **Note (2026-10-14) — …**blockquotes (:66); the note follows that form. No status-line entry was added to ADR-0131: the note changes no D3 text (its inline 2026-10-10 withdrawal at:315set the precedent of a note without a status entry).packages/spec/src/security/capabilities.ts:33declaresPlatformCapability.scope: 'platform' | 'org', and every one of the 10PLATFORM_CAPABILITIESrows sets it (platform:manage_users,manage_metadata,manage_platform_settings,setup.access,studio.access;org:manage_org_users,setup.write,manage_sharing,view_all_audit_log,manage_mailboxes). A package capability declares it throughCapabilityDeclarationSchema.scope(:189),z.enum(['platform', 'org']).default('platform'). The superuser bits areObjectPermissionSchema.viewAllRecords/modifyAllRecordson theobjects['*']entry; the delegation key isPermissionSetSchema.adminScope(permission.zod.ts:1022). All three conditions are readable from declarations alone.packages/plugins/plugin-security/src/objects/default-permission-sets.ts, held equal toSHIPPED_PERMISSION_SET_NAMES(packages/cli/src/utils/organization-ownership-inventory.ts:215) by that file's test; a repo-wide search forPermissionSetSchema.parse(/definePermissionSet(outside tests finds no other shipped set (fixtures inpackages/verifyandpackages/qaonly).pnpm check:adr-anchorsexits 0 on the final head.Shipped permission sets against the three conditions
*superuser bitadminScopeorgcapabilities onlyadmin_full_accessviewAllRecords+modifyAllRecordsmanage_users,manage_metadata,manage_platform_settings,setup.access,studio.accessorganization_adminviewAllRecords+modifyAllRecordssetup.accessorganization_admin_no_bypasssetup.accessmember_default*entry at allviewer_readonlyallowReadonlymcp_agent_data_readallowReadonly, no row scopemcp_agent_data_writemcp_agent_restrictedobjects: {}Shipped positions: the four ADR-0068 D2 identity names (
platform_admin,org_owner,org_admin,org_member) are refused onsys_user_position.positionfor every writer by the object validationreserved_identity_position(sys-user-position.object.ts:256, no provenance exemption), and the two anchors by the gate's unconditional anchor refusal (delegated-admin-gate.ts:298–:312). Neither is a question for this boundary; the note records them as unchanged.Precisions the contract review should read
Each narrows; none widens the ruled rule.
TENANT_ADMIN; the note says in so many words that D12's "tenant-level" is the first kind only, so the two vocabularies cannot be read into each other.scopedefaults to'platform', and asystemPermissionsname no declaration carries is not anorgcapability.[...positions, ...explicitPermissionSets](security-plugin.ts:6542), so a held position also grants the permission set of its own name; the note counts that set while the fold stands. Without this a position could carry an outside set past the boundary.organization_admin_no_bypassis outside by (c) today only becausesetup.accessisplatform; the note also excludes it by name, which is what "conferred by membership grade only" requires.fallbackPermissionSet: null) they read every organization's rows there, a read the granting organization administrator does not hold (its explicit per-object entries keep the_self/_nonescope).mcp_agent_restrictedis named by kind. The general case — any authored set with a'*'read and no row scope on a Layer-0-inert readable object, in a no-baseline deployment — is not settled by the conditions or by a shipped-set denylist; the note holds it for the maintainer's decision on [Decision] The organization administrator's assignment boundary: an assignable set with a wildcard read and no row scope reaches other organizations' SCIM rows in a deployment with no platform baseline #24164 and writes no fourth condition.allowCreate/allowDeleteon,allowEditoff.Gates (final head
61c8a5d34)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 19 commands from the tree at61c8a5d34(change set: the two ADR files, +76 / -0 against the base; the same 19 on every head of this PR). Each ran on61c8a5d34with the exit code captured before any pipe:node scripts/check-adr-links.mjs:: exit 0 — "769 relative link destination(s) under docs/adr/ resolve";--self-test:: exit 0node scripts/check-adr-symbol-anchors.mjs:: exit 0 — "2353 anchors across 142 records resolve";--self-test:: exit 0node scripts/check-ci-filter-parity.mjs:: exit 0node scripts/check-closing-keyword-parity.mjs:: exit 0;--self-test:: exit 0node scripts/check-comment-mask-corpus.mjs:: exit 0 — "9404 files, 0 disagree"pnpm --filter @objectstack/lint run check:doc-formula-expressions:: exit 0 (its prerequisites@objectstack/formulaand@objectstack/lintbuilt first under the verify lock,pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2, VERDICT command-exit 0; on the first head the gate had answered exit 3, PREREQUISITE NOT MET, before that build)pnpm check:adr-anchors:: exit 0 — "check-adr-anchors: OK (70 anchored file(s), every governing ADR still referenced; 136 decision number(s) …; 47869 citation(s) across 6347 file(s) resolve …)"pnpm check:cross-package-test-inputs,check:doc-authoring,check:driver-memory-census,check:gitlink-declared,check:nul-bytes,check:pm-governed-merges,check:pm-prior-rulings,check:refd-timer-probe,check:watch-hint-literal:: exit 0 each--ranreconciliation: "19 derived famil(ies) accounted for — 19 run, 0 NOT-MEASURED (a DERIVED zero — all 19 recorded an exit code and none of them is 3)".node scripts/pm/check-governed-merges.mjs --branch claude/issue-24135-adr-0090-d12-org-admin-boundary(exit 3, the governed code): "GOVERNED — a human merge is the review record for this PR", "landing tier: H(人合)",docs/adr/**×2, under the 3000-line threshold.Everything else CI runs on this PR is CI's; nothing was re-run locally. No package builds or tests are owed: the diff touches no package.
Acceptance notes
Read-only inferences from the measurement above, not reproduced and not filed:
setsBoundToPosition,delegated-admin-gate.ts:1086) reads only the definition'spermissionSets, while the resolver also grants the set of the position's own name (precision 3). A delegate's allowlist check therefore does not see a same-named set. Carrier: plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136, whose third branch reuses that walk.isTenantAdmin(delegated-admin-gate.ts:256, a wildcardmodifyAllRecordsholder) reads the walledorganization_adminas tenant-level and the wall-less variant as no administrator at all; under the amended D12 both are organization-level. Carrier: plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136.docs/design/permission-model.md§10 describes delegated administration only; the organization-level kind belongs there once plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136 enforces it (docs follow the implementation). Carrier: plugin-security: organisation admins assign positions and permission sets to their own members inside the D12 boundary — gate branch, overrides, explain and my-delegable-scope, three-posture acceptance (ruling B on #23960) #24136.维护者速读(草稿)
改了什么
在权限模型 ADR-0090 的 D12(管理权限)里加了一条带日期的修订,并在 ADR-0131 D3 下加了一行注记,纯文字,不改代码、不加 spec 键。修订把管理者分成三类:租户级(平台管理员)、组织级(组织的 owner / admin,自动获得组织管理员权限集)、委托级(带
adminScope的人)。组织级管理员可以给本组织成员增加或移除岗位与权限集,但只限"被组织墙约束住"的集:不带通配超级用户位、不带adminScope、所含能力全部是组织级。admin_full_access、平台级集、组织管理员集本身、任何委托集都不能分;组织管理员身份只能由成员资格(owner / admin)获得,不能靠分配复制。本轮按席位复核意见补了一条:三个给 AI 代理用的 MCP 上限权限集列入裁决预留的黑名单,组织管理员不能把它们分给人。为什么改
这是您 10 月 17 日对 #23960 的裁决 B(「同意」)的第一步。ADR-0131 D3 承诺"组织的管理员给本组织成员分配",但代码为了防止组织管理员给自己绑
admin_full_access越过组织墙,把两张分配表设成了只读;缺的正是"组织管理员能分配哪些"这条边界。边界先写进 ADR,代码卡 #24136(被本卡阻塞)才能开工。实测出货的 8 个权限集:三条件能判定 5 个(2 个在内、3 个在外);剩下 3 个 MCP 代理上限集按三条件看似在内,但它们本来只配给 AI 代理、靠委托人自己的权限收窄,直接分给人时在"不配平台基线"的部署里能读到其他组织的 SCIM 身份数据,而分配它的组织管理员自己都读不到——所以按裁决第 4 条列入黑名单。风险与代价(含回滚)
只改两份 ADR 的文字,今天的产品行为不变:两张分配表仍然只读,直到 #24136 落地。规则措辞上的几处精确化全部是收紧、没有放宽:未声明范围的能力按平台级算;岗位按名字带出的同名权限集也算"岗位携带的集";无墙部署用的
organization_admin_no_bypass也按名字排除;三个 MCP 上限集列入黑名单。还有一个更一般的口子没有在本 ADR 里定:任何自建的"通配只读、没有行级范围"的权限集,在不配平台基线的部署里同样能碰到那几张没有组织列的 SCIM 表——是否加第四个条件,还是把保证写成"以平台基线为前提",已单独立成待您决策的 #24164,本 PR 不替您决定。回滚:直接 revert 本 PR,不涉及数据和发布。席位意见
你要做的(一个动作)
席位复核通过后,在本 PR 上点 Approve。
Generated by Claude Code