Skip to content

docs(adr): ADR-0120 draft — explicit unique-scope vocabulary (organization/global) and NULL-safe per-organization uniqueness (#4986, #5030) - #5054

Draft
os-zhuang wants to merge 6 commits into
mainfrom
claude/issue-4986-analysis-24ol6q
Draft

docs(adr): ADR-0120 draft — explicit unique-scope vocabulary (organization/global) and NULL-safe per-organization uniqueness (#4986, #5030)#5054
os-zhuang wants to merge 6 commits into
mainfrom
claude/issue-4986-analysis-24ol6q

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

这是什么

#4986 维护者裁定后的 ADR-0120 完整草案(Status: Proposed),一张 ADR 同时覆盖 #4986(表级声明唯一索引的组织语义岔路)与 #5030(NULL 组织行上复合唯一约束蒸发)。本 PR 仅新增 ADR 文档,零代码语义改动 —— D8 明确:ADR 被接受前,driver-sql / spec / lint 均不动。

已含五轮维护者评审:①「app 元数据姿态可移植」为总纲(single | group | isolated,ADR-0105;每客户单库 = 环境级选择);② 词汇弃用 'tenant';③ 姿态切换罕见、不自动化,执行交 AI 写升级脚本;④ 第三词 'company' 被否(记入 Alternatives #6),S14 改部署接缝处理;⑤ 四个遗留评审问题全部裁决(见下),Open questions 节已转为 Resolved 决策记录。

决定摘要(D1–D8)

  • D1 词汇显式化:unique: 'global' | 'organization' 两种写法通用,两词均不依赖姿态;声明索引裸 true 弃用(17.x 警告 → 协议 18 拒绝);字段级 true 永久合法,官方示例/脚手架一律输出 'organization'(非规范性引导)。
  • D2 存量无感转换:ADR-0087 D2 条目 true → 'global',存量库零漂移;九个 engine-owned 幂等键钉死不变。
  • D3 NULL 安全组织键:物化为 COALESCE(organization_id, '__global__') —— 报错自述平台桶、与 autonumber 的 GLOBAL_TENANT 同词;组织创建保留该 token,存储仍为 NULL;17.x 落地
  • D4 迁移仪式:纯收紧 recreate_index + 重复数据预检,预检不净永不自动应用。
  • D5 门禁:未表态 scope 的 lint/publish 拒绝 + R10 重写 + D5e 安装期姿态硬门('global' 业务唯一装进 isolated 环境逐索引确认/改写,留痕不复问;doctor/plan 兜底存量与姿态变更)。
  • D6 五处成文契约同波次改
  • D7 分期:17.x 增量(含 D3、D5e),协议 18 拒裸 true
  • D8 冻结令:接受前零单侧语义改动。

四个评审问题的裁决(2026-08-04,详见 ADR §Resolved review questions)

  1. COALESCE 字面量 = '__global__'(从 '' 翻转):事故现场报错自述性 + 子系统词汇统一(多租户缺陷:unique 物化为全局唯一索引、无视 tenancy,与按租户分裂的 autonumber 序列自相矛盾(跨租户必然撞号 + 存在性探测泄露) #3696 教训)。
  2. 字段级裸 true 永久合法:无歧义无陷阱,弃用只买 churn 不买安全;示例引导显式写法。
  3. D3 落 17.x:等待成本复利(重复数据越积越多),18 波本来就零物理漂移。
  4. isolated 决策点 = 安装期硬门 + 留痕:危险方向(跨客户唯一墙 + 存在性泄露)永不静默落地;advisory 只兜历史存量。

ADR 已无未决问题,可评审定稿(Proposed → Accepted)后拆实现波次。

Refs #4986, #5030 · Amends #3696 · Relates #4698, #4884, ADR-0105

🤖 Generated with Claude Code

https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7

…-safe tenant uniqueness (#4986, #5030)

Proposed-status draft for maintainer review. Records the amendment of the
#3696 verbatim-declared-index decision into an explicit 'global' | 'tenant'
scope vocabulary (D1), a lossless ADR-0087 D2 conversion for stored
metadata (D2), NULL-safe COALESCE tenant key parts fixing #5030 (D3), the
duplicate-pre-flight migration ceremony (D4), authoring gates (D5), the
five-surface truth sweep (D6), 17.x -> 18 staging (D7), and the freeze on
unilateral driver-sql changes until acceptance (D8). Docs-only: no code
semantics change in this PR.

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

vercel Bot commented Aug 4, 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 4, 2026 2:09am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation size/m labels Aug 4, 2026
@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 4, 2026 — with Claude
claude added 2 commits August 4, 2026 01:17
…nstraint

Maintainer requirement from #4986 review: one metadata app must run
unmodified under single-org and multi-org deployment; the author states
the business boundary of a unique constraint, never the topology. Adds
the governing principle, a dedicated Mode portability section with the
declaration x mode matrix and both mode-transition stories (single->multi
relaxes with zero migration; multi->single tightens through the D4
ceremony), matrix row S13, a D1 anchor line, and the dual-mode
conformance requirement in Acceptance tests.

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

Maintainer review round 2 on #4986. Two corrections from review:

1. The authorable word 'tenant' was ambiguous — the platform has a second
   tenancy sense (database-per-customer environments) besides row-level
   organization scoping. The scope tokens are now 'organization' and
   'company'; 'tenant' is not accepted even as an alias, and a Terminology
   section records why.

2. Multi-org splits into group (one corporate family) and isolated
   (organizations are separate customers) postures per ADR-0105 D1, which
   makes 'company-wide unique' posture-VARIANT: installation-wide under
   group, per-organization under isolated. A third scope word 'company' is
   added, resolved to one of the two existing physical shapes at
   registration where the posture is known; posture transitions re-resolve
   it through the D4 ceremony. New matrix row S14, reworked Posture
   portability section with the declaration x posture table and the
   acquisition/consolidation transition stories, acceptance coverage for
   all three postures, and two new open questions (token name; where the
   isolated-posture 'global' advisory lives).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
@os-zhuang os-zhuang changed the title docs(adr): ADR-0120 draft — explicit unique-scope vocabulary and NULL-safe tenant uniqueness (#4986, #5030) docs(adr): ADR-0120 draft — explicit unique-scope vocabulary (organization/company/global) and NULL-safe per-organization uniqueness (#4986, #5030) Aug 4, 2026
Maintainer ruling on #4986 review round 3: a posture change is a planned
re-platforming event, expected to be rare; the platform's obligation is a
well-defined target state and a trustworthy migrate plan, not built-in
transition machinery. Per ADR-0087's operating assumption the migrator is
an AI agent that writes the upgrade script from the D4 plan (including the
duplicate probe report). Transition acceptance coverage slimmed to one
smoke assertion; D1 and Consequences reworded to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
@github-actions github-actions Bot added size/l and removed size/m labels Aug 4, 2026
…deployment-seam handling for S14

Maintainer ruling round 4: a posture-resolved third word is the only token
in the vocabulary that cannot be used without first understanding the
posture spectrum — too easy for AI authors to confuse — and the scenario
it served is rare. Vocabulary stays 'global' | 'organization'. The S14
residual (company-wide keys in a posture-portable app) moves to the
deployment seam: author 'global', and under isolated posture os doctor /
migrate plan surface each 'global' business unique as an explicit decision
point (confirm or rewrite to 'organization', AI-authored install
adjustment). Posture flips now have zero automatic schema consequences by
construction. The rejected design is recorded in Alternatives #6 per
PD #13; open questions renumbered (now four).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
@os-zhuang os-zhuang changed the title docs(adr): ADR-0120 draft — explicit unique-scope vocabulary (organization/company/global) and NULL-safe per-organization uniqueness (#4986, #5030) docs(adr): ADR-0120 draft — explicit unique-scope vocabulary (organization/global) and NULL-safe per-organization uniqueness (#4986, #5030) Aug 4, 2026
…ecisions)

Q1: COALESCE literal is '__global__' (flipped from '') — self-describing
constraint-violation errors for AI ops triage, and one vocabulary with the
autonumber GLOBAL_TENANT; org creation reserves the token, storage stays
NULL. Q2: field-level bare true stays valid indefinitely; examples and
scaffolding emit 'organization' as non-normative guidance. Q3: D3 ships in
17.x — waiting compounds the duplicate sets the D4 probe must report, and
the protocol-18 wave is physically empty either way. Q4: the isolated-
posture decision point is a hard install-time gate with recorded
confirmations (new D5e); doctor/plan keep the advisory form for pre-gate
installs and posture changes. Open-questions section converted to a
resolved-decisions record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
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/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants