Skip to content

fix(lint): ADR-0105 D6 ② 的收件人词表扩至 ADR 原文范围 —— unit_and_subordinates 也判红 (#4991) - #5004

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4991-d6-unit-and-subordinates
Aug 3, 2026
Merged

fix(lint): ADR-0105 D6 ② 的收件人词表扩至 ADR 原文范围 —— unit_and_subordinates 也判红 (#4991)#5004
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4991-d6-unit-and-subordinates

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4991

问题

org-axis-cross-org-bu-grant(ADR-0105 D6 ②)此前只对一种收件人判红:

if (recipientType !== 'business_unit') return;

ShareRecipientType 里有两个业务单元收件人,漏掉的那个授权面更大:

sharedWith.type 展开 改前 改后
business_unit 正好一个 BU 的成员 判红 ✅ 判红 ✅
unit_and_subordinates 该 BU 加上其全部后代单元(ADR-0057 D5) 放行 判红 ✅

两者的缺陷完全相同 —— 平台级对象(tenancy.enabled: false / systemFields.tenant: false)没有 organization 列可供 Layer 0 收口,BU 子树没有任何 organization 可供解析,授权因而跨到库里每一个 organization。而漏掉的恰恰是 ADR-0105 D6 ② 自己点名的那一个:

Every BU mechanism — unit_and_subordinates sharing, adminScope delegation, depth scopes — operates within one organization. There is no cross-org tree.

词表核对(本单的核心要求)

以 spec 的 ShareRecipientType(packages/spec/src/security/sharing.zod.ts,sharedWith.type 的唯一可写词表,.strict() 逐名拒绝其余)为准逐词交叉核对。五个成员,拦二放三,差集在规则注释里以表格逐条写明:

ShareRecipientType 理由
business_unit BusinessUnitGraphService,org-predicated
unit_and_subordinates 同一解析器,严格更宽的授权
user 字面 user id,完全不展开,没有树可解析
team sys_team 是扁平协作分组(ADR-0090 D3),走 TeamGraphService
position 扁平 holder 展开(ADR-0090 D3 已终结职位层级),走 PositionGraphService;D6 ② 同时点名的 BU depth scope 是 scope 机制,不是 sharing-rule 收件人

差集是故意的,不是遗漏:放行的三个正是共享平台级目录的既定方式(ADR-0066)。② 禁止的不是"共享全局对象",而是"在没有 organization 可供解析的地方解析 BU 子树"。逐一按 plugin-sharingexpandRecipient 复核过运行时展开路径 —— 恰好只有两个走 BusinessUnitGraphService

运行时契约 SharingRuleRecipientType 另有 queue,刻意不可授权(没有 sys_queue),expandRecipient 对其返回 [],注释里一并记账。

结构性防复发

新增断言:拦截半 ∪ 放行半 == ShareRecipientType.options#4991 正是这条断言缺席的产物 —— ② 照着一个收件人写完就没回头看枚举。将来枚举加第六个成员会在词表处失败,而不是无声地落进没人选过的那一桶。

双向证明

全部 fixture 走 #4992 引入的 sharingRule() meta-guard(先经真 SharingRuleSchema 解析,spec 合法才算数):

  • 判红且点名:unit_and_subordinates + tenancy.enabled: false / systemFields.tenant: false 两种拼法均判红,message 含 `unit_and_subordinates` 与 "AND every descendant unit"
  • 不回归:business_unit 既有拦截保持,message 不含 "descendant"
  • 判绿:user / team / position 在平台级对象上放行;unit_and_subordinates 在 org-scoped 对象上放行(showcase 的真实形状)

真实元数据复核(未沿用 #4984 的预核,独立重跑)

把改后的规则真跑在仓库的真实元数据上,而非只 grep:

=== app-showcase ===
objects scanned: 21
tenancy-disabled objects: (NONE)
sharing rules: 8
  - share_new_inquiries_with_field_ops: sharedWith.type=unit_and_subordinates object=showcase_inquiry
  - (其余 7 条均为 position)
org-axis findings: 0

=== app-crm ===
objects scanned: 6
tenancy-disabled objects: (NONE)
sharing rules: 3   (均为 position)
org-axis findings: 0

TOTAL org-axis reds on real metadata (widened rule): 0

并复核了 examples 之外:全仓 sharedWith 的非测试出现点只有这两个 examples;default-permission-sets.ts / app-default-permission-set.ts 不含任何 sharing rule;tenancy.enabled: false 的非测试声明为零。扩张后零新红,结论与 #4984 的预核一致但为独立重跑所得。

验证

  • pnpm --filter @objectstack/lint testTest Files 55 passed (55) / Tests 1017 passed | 4 skipped (1021)
  • 目标文件 25 passed(改前 20)
  • pnpm --filter @objectstack/lint typecheck → 干净
  • npx eslint 两个改动文件 → 干净
  • changeset:@objectstack/lint patch

改动限于 packages/lint(2 个文件)+ 1 个 changeset。未触碰 content/docs/releases/


Generated by Claude Code

… 也判红 (#4991)

`org-axis-cross-org-bu-grant` 此前只拦 `sharedWith.type === 'business_unit'`,
放行了授权面更大的另一个业务单元收件人 `unit_and_subordinates`(一个 BU 加上其
全部后代单元,ADR-0057 D5 子树扩张)—— 恰恰是 ADR-0105 D6 ② 原文点名的那一个。

两者缺陷完全相同:平台级对象没有 organization 列,BU 子树无从解析,授权跨到库里
每一个 organization。判定改为收件人类型 ∈ { business_unit, unit_and_subordinates },
诊断点名实际写下的类型与其触及范围。

词表与 spec 枚举 `ShareRecipientType` 的差集不再隐式:注释逐条写明放行
`user` / `team` / `position` 的理由(运行时展开均不经 `BusinessUnitGraphService`),
并加断言两半恰好划分该枚举 —— 将来加枚举成员会在词表处失败。

复核真实元数据:全仓 11 条已声明 sharing rule、零个对象关掉 tenancy,
扩张后 org-axis 红线数为 0。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 8:03pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx (via @objectstack/lint)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/releases/v17.mdx (via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0105 D6 ② 只拦 business_unit 收件人,漏了 unit_and_subordinates —— 同一个跨 org BU 授权,更宽的那个反而放行

2 participants