Skip to content

fix(objectql): wantOwner 翻为正面清单 + 注入 owning_business_unit_id (ADR-0117 D1) (#5677) - #5802

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5677-want-owner-positive-list
Aug 6, 2026
Merged

fix(objectql): wantOwner 翻为正面清单 + 注入 owning_business_unit_id (ADR-0117 D1) (#5677)#5802
os-zhuang merged 2 commits into
mainfrom
claude/issue-5677-want-owner-positive-list

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5677

ADR-0117 D1 的执行面。范围严格是 issue 工作面第 1-3 项,全部落在 packages/objectql

为什么先动引擎

applySystemFields 判定是否注入 owner_idwantOwner排除式的:只排除
org / none,于是任何第四个 ownership 值都会掉进默认分支并被盖上 owner_id
—— 与 D1 表格恰好相反(business_unit 档的全部含义就是「归属于组织单元而非个人」)。
#4611 的一次性探针已实测确认过这个反向结果。所以顺序是:先翻面,枚举扩展才安全。

改了什么

1. wantOwner 翻为正面清单 —— ownership === undefined || ownership === 'user'
(managedBy 平台表与 sys_ 命名空间的跳过规则原样保留,并抽成两个锚点共用的
ownershipEligible,避免二者日后漂移)。对今天存在的三个值行为完全不变;唯一
变化是新档位不再靠「落进默认分支」继承 owner 语义。

2. 注入 owning_business_unit_id —— lookup 到 sys_business_unit,按 D1 表格:

ownership owner_id owning_business_unit_id
undefined / user
business_unit
org / none

3. conformance 归类迁移 —— system-managed-fields-conformance.test.ts 中该名从
documented-reserved 组移入 Group A(由实时注入代码推导)。#5675 在测试注释里埋的
就是这一步;不迁会以 stray entry 失败。迁移后额外把该名写进 Group A 的
「非空洞」守卫列表,把这次搬家钉住:否则将来若注入回退,Group A 会悄悄缩回去、
测试转而要求把它重新列回 reserved —— 一个描述着与已接受决定相反事实的绿色套件。

列的形态,以及它为什么依赖未裁定的 D2

比照 organization_id(服务端盖章的作用域锚点)而非 owner_id(用户可指派的业务
字段):readonly: true + hidden: true + required: false,不建索引

  • readonly —— D3 要求该值服务端推导并校验,客户端传入的值一律被覆盖;改动它是
    D4 的 transfer 类操作,而 D4 尚未落地,因此现在不得存在任何客户端写入路径。
  • hidden —— 盖章中间件未落地(D2 未裁、D8 未写),列是已提供但惰性的。在每个
    业务对象的布局上摆一个恒为 NULL 的 lookup,等于展示运行时并不提供的能力
    (Prime Directive chore: version packages #10)。另外 FIELD_GROUP_SYSTEM_FIELDSpackages/spec,本单
    不碰,hidden 让渲染面无需 spec 改动即可正确。
  • required: false —— 可空。D2 的 owningBusinessUnit.required插入时拒绝
    规则,不是列的可空性,读不出来也不该从这里读。
  • 无索引 —— 会用到索引的层级谓词(D6)随企业版作用域解析器发布。

⚠️ 这三项都不预设 D2 的 pinned / follow_owner / transferable:它们不授予
任何能力,因此 D2 的每一种结论都仍然可达。未触发 needs_decision。

与相邻单的时序

验证

  • 反向验证(方向先判后跑):预判为「恢复排除式判定 → business_unit 档误得
    owner_id,恰好一条断言转红,其余(owning_business_unit_idorg/none
    user)保持绿」。实测完全一致,且复现了 ADR-0105 D13's "scoping field" has no metadata home — the 0105↔0117 linkage gap #4611 探针的原样输出:
    AssertionError: expected { type: 'lookup', …(6) } to be undefined / + Received: { "reference": "sys_user", "label": "Owner", … },Tests 1 failed | 93 passed
  • 既有 registry.test.tsorg/none opt-out 用例未改一字且保持绿 —— 这是
    「翻面没有改变既有语义」的证据。
  • pnpm --filter @objectstack/objectql test123 files / 2043 tests 全绿;
    typecheck 干净。
  • 因为本改动给每个已注册对象加了一列,消费半径是全仓,所以跑了全量:
    turbo run test135/135 tasks 成功(含 dogfood 85 files / 513 tests、
    showcase、crm);turbo run typecheck125/125 成功
  • 门禁:check:query-options-erasure(84 unswept / test surface 267 —— 恰在天花板,
    未引入新豁免)、check:engine-double-contractcheck:adr-anchors
    check:nul-bytes 全过;改动文件 eslint 干净,并做了控制字符自检。

Generated by Claude Code

…s_unit_id (ADR-0117 D1) (#5677)

`applySystemFields` decided owner injection with a DENY-list — only 'org' and
'none' opted out — so ANY fourth `ownership` value fell through to the default
branch and was stamped with `owner_id`. ADR-0117 D1's new `business_unit` tier
means the opposite ("owned by a UNIT, not a person"): owner_id no,
owning_business_unit_id yes. #4611's one-shot probe measured the inversion.

The judgement is now a positive list (`undefined | 'user'`), which is
behaviour-identical for the three values that exist today and stops a new tier
from inheriting the owner branch by accident. A second anchor covers the union
of the owner tiers with `business_unit` and injects `owning_business_unit_id`
(lookup to sys_business_unit).

The column is shaped after `organization_id` (server-stamped scope anchor), not
after `owner_id` (user-assignable field): readonly, hidden, nullable, unindexed.
None of those presumes ADR-0117 D2's undecided stamping policy — they grant no
capability, so every D2 outcome stays reachable; the column is provisioned but
inert until the stamping middleware (D2/D4) and backfill (D8) land.

The objectql conformance test migrates the name from the documented-reserved
group to Group A, where it is derived from the live injection code — the
migration #4611 left a note for.

Scope is the issue's work items 1-3. The spec-side JSDoc/description sync is
#5767 and the `ownership` enum member is #5678, both strictly after this PR.

Fixes #5677

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

vercel Bot commented Aug 6, 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 6, 2026 6:08am

Request Review

@github-actions github-actions Bot added the size/m label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/plugins/index.mdx (via @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx (via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql)

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 6, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 6, 2026 07:52
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 0d24078 Aug 6, 2026
24 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5677-want-owner-positive-list branch August 6, 2026 08:04
baozhoutao pushed a commit that referenced this pull request Aug 6, 2026
队列把本 PR 踢出:@objectstack/objectql 记 333,队列基实测 334。333 是在 07:41
的 main 上冻结的,而 #5802(registry.test.ts +116 行)与 #5850 在 07:52 之后才
落地。合并当前 main 后实测 335,两条增量都能逐一归因:

- +1 TS2339 在 src/registry.test.ts —— #5802 新增的 registry 测试;
- +1 TS2554 在 src/engine-update-prior-read-scope.test.ts —— #5850(#5284)
  新建的文件。

tests 125 -> 126。其余 33 条纹丝不动(总计 2018 raw errors,无一超出记录值)。

先证伪了另一种解释:同一棵树连跑两次 --re-measure,输出逐字节相同,所以不是 tsc
计数不确定,校准就是正确处置(不需要谈容差)。

顺带把队列这一面写进 MEASURED 的文档块:队列是按「合并到队首」构建的,队首会随
前面的条目落地而移动,所以重跑失败的 job 无法自愈(重跑复用同一个 merge ref,
量的还是那个旧基),唯一修法是推新提交;以及排在后面的 PR 会被连坐,红了要先撤出
队列再修。双跑证伪法也一并写下,免得下一个人重新推导。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW
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-0117 D1 执行面:applySystemFields 的 wantOwner 由排除式翻为正面清单 + 注入 owning_business_unit_id

2 participants