Skip to content

fix(approvals): resolve department approvers against env-wide business units (#3807) - #3811

Merged
os-zhuang merged 1 commit into
mainfrom
claude/approver-value-lookup-fix-t9hr0r
Jul 28, 2026
Merged

fix(approvals): resolve department approvers against env-wide business units (#3807)#3811
os-zhuang merged 1 commit into
mainfrom
claude/approver-value-lookup-fix-t9hr0r

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #3807.

问题

expandBusinessUnitUsers(approval-service.ts)读 sys_business_unit 时用的是严格等值 organization_id = <请求的 org>,于是 organization_id = null 的单元完全不可见:种子检查查不到行 → 整个展开返回 [] → 审批人落到死值 department:<id>,路由到没有人。

这不是边角情况,而是常态。应用的组织架构一般是 seed 出来的,而 seed 阶段拿不到运行时才创建的组织 id,所以每一行都是 organization_id = null;审批请求却总是带着 org。结果:设计器 Department 选择器里能选到的每一个单元,都解析到没有人,而且是静默的 —— 请求照常打开、名单为空,配上 lockRecord 记录就此锁死没人能动(#3424 就是同一条死路的下游形态)。

配合 #3508 刚落地的记录 lookup,这条路更容易被走到:以前要手敲 BU id,现在从选择器里点一下就得到一个死槽位。

改动

种子检查与子树下钻都改为「本组织 ∪ 无组织」:

$or: [{ organization_id: <org> }, { organization_id: null }]

sys_metadata 待发布草稿列表为同一个原因(严格等值静默丢掉 env-wide 行)所采用的谓词一致。

两个组织之间的墙没有变:别的组织的单元仍然匹配不上;null org 的父单元也不会把别的组织的子单元拖进子树。只有「不属于任何组织」的行变得可见 —— 而这类行只可能由部署方(seed / 文件层 / bootstrap)写入,租户管理员写不出来(写路径会盖 org 戳)。

验证

真机,同一个库、同一条 flow、只换构建:

记录 BU 的 org 构建 department 槽位
pm8MCA… null(seed 原样) 修前 department:bu_hq_finance ❌ 死值
0h-cVa… 手工补上 org 戳 修前 usr_showcase_phone_demo ✅(印证因果)
p-G0OQ… 改回 null 本 PR usr_showcase_phone_demo

单测:新增 4 例 —— env-wide 单元能解析;别的组织的单元仍然不可见;null org 的父单元不会拉进别组织的子单元;停用的 env-wide 单元仍然不出人。其中两个正向用例在不改源码时确实失败(已回退验证)。

plugin-approvals 全量 252/252 通过;仓库 pnpm lint 干净。

未一并改动的地方

plugin-sharingBusinessUnitGraphService.orgScope同形的严格等值。目前不可达 —— 实测所有物化出来的 sys_sharing_ruleorganization_id 都是 null,过滤器整个被跳过,BU 子树共享规则照常生效。它是一条授权路径(决定谁能看到记录),在没有可复现失败的前提下不宜跟着放宽,故本 PR 不动它,只在此备案。

🤖 Generated with Claude Code

https://claude.ai/code/session_01BVRVgSvmyCwmDfTmKmCZoH


Generated by Claude Code

…ess units (#3807)

`expandBusinessUnitUsers` scoped its `sys_business_unit` reads with a strict
`organization_id = <request org>` equality, so a unit whose `organization_id`
is `null` was invisible: the seed check found no row, the expansion returned
`[]`, and the approver fell back to the dead `department:<id>` literal that
routes to nobody.

That is the normal case, not an edge case. An app's org tree is seeded, and a
seed cannot know the organization id the runtime mints at boot, so every seeded
unit carries `organization_id = null` — while an approval request always
carries one. Every business unit a flow author could pick therefore resolved to
nobody, silently, and with `lockRecord` the record stays locked with no one able
to act (#3424 is the downstream shape of the same dead end).

Both the seed check and the subtree descent now scope to this org ∪ env-wide —
`$or: [{ organization_id: <org> }, { organization_id: null }]` — the same
predicate `sys_metadata`'s pending-draft listing settled on for the identical
reason. The wall between two organizations is unchanged: another org's unit
still fails the match, and a null-org parent does not drag another org's child
unit into the subtree.

Verified on a live showcase stack, same DB and same flow across builds:
seeded (null-org) unit + old build → `pending_approvers: department:bu_hq_finance`;
same unit + this build → the unit's member.

Tests: 4 new cases (env-wide unit resolves; another org's unit stays invisible;
a null-org parent does not pull in another org's child; an inactive env-wide
unit still contributes nobody). The two positive cases fail without the source
change. plugin-approvals 252/252 pass; repo lint clean.

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

vercel Bot commented Jul 28, 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 Jul 28, 2026 6:54am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-approvals.

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

  • content/docs/automation/approvals.mdx (via @objectstack/plugin-approvals)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-approvals)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-approvals)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-approvals)

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.

@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 06:57
@os-zhuang
os-zhuang merged commit 3ea7271 into main Jul 28, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/approver-value-lookup-fix-t9hr0r branch July 28, 2026 07:11
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.

approvals: a department approver never resolves when the business unit has organization_id = null (every seeded BU)

2 participants