feat(spec,lint): unique 范围显式词汇 'organization' + D5a/D5b lint 规则(ADR-0120 17.x 波,#4986) - #5208
Merged
Merged
Conversation
… ADR-0120 17.x 波) - UniqueScopeSchema → boolean | 'global' | 'organization'(D1,17.x 纯增量); 'tenant'/'org' 拒绝且报错指名 'organization'(§Terminology,strictTenancyError 先例) - IndexSchema.unique 注释与 describe() 按 D1/D6 重写:verbatim 收窄到 'global'; 'organization' = 注册期并入 NULL-safe 组织键部(COALESCE 物化随 #5030); 裸 true 弃用(17.x 警告 → 协议 18 拒绝,#5082) - UniqueScopeSchema 文档块单租户假豁免替换为 D3 真相(NULL 桶 + COALESCE); Resolved #2 非规范性引导入注释(示例/脚手架输出 'organization',裸 true 永久合法) - 新导出 isOrganizationUnique;isUniqueDeclared 计入 'organization'(ADR-0078) - lint 新规则 unique/unscoped-declared-index(D5a,警告,只判拼写不推断租户性); R10 unique/double-declaration 四象限重写(D5b),fix 改说 'organization' 词汇 - 测试:spec unique-scope 契约测试扩充;cli data-model-rules 测试按四象限重写 - 再生成:references/data/{field,object}.mdx、api-surface.json;adr-anchors 三条; changeset 两包(spec minor / lint minor) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
…deferred artifact) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
导出面即承诺(#4409 wiring 测试):导出的规则必须入注册表或入账。搭车在 lintUniqueDeclarations 里跑对注册表不可见,且 validate/build 会经两条路径 各报一次。改为:R11 自己的 AUTHORING_RULES 条目(validate/build,advisory), lintDataModel 直接调用它服务 os lint —— 三命令全覆盖,每条 finding 恰好一报。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 4, 2026
os-zhuang
marked this pull request as ready for review
August 4, 2026 12:02
os-zhuang
enabled auto-merge
August 4, 2026 12:02
Resolves the scripts/adr-anchors.json conflict by keeping all five ADR-0120 anchor entries (spec/lint side + driver side from #5212), and clears the os-regen deferred merge of packages/spec/api-surface.json via spec build + gen:api-surface (isOrganizationUnique retained). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4986
本 PR 是什么
ADR-0120(Status: Accepted,2026-08-04)17.x 波的第二块:spec 词汇 + lint 规则,纯增量、零语义变化。前一开发会话在环境回收时中断,本会话同分支接手,对既有改动逐条对照 ADR 审计后补完(审计结论与接手说明见 issue 评论区)。
packages/spec(D1 / D3 / D6 / §Terminology / Resolved #2)
UniqueScopeSchema从boolean | 'global'扩为boolean | 'global' | 'organization',字段级与IndexSchema.unique共用(D1)。既有拼写零语义变化。strictTenancyError先例):'tenant'/'org'不接受、不做别名,union 级 error map 在报错里指名'organization',并对任何非法拼写完整列出词汇表。IndexSchema.unique注释块与describe()按新契约重写(D1/D6):verbatim 契约收窄为'global'的语义;'organization'= 注册期由 driver 前置 NULL-safe 组织键部(COALESCE 物化随 driver-sql: 单租户栈上 organization_id 恒为 NULL,#3696 的 (tenant, col) 复合 UNIQUE 因 NULL-distinct 而完全不生效 —— 字段级 unique: true 静默零约束 #5030 落地);裸true='global'的弃用位置式拼写(17.x lint 警告 → 协议 18 拒绝,ADR-0120 协议 18 项:D2 conversion(声明索引 unique: true → 'global')+ 裸 true 硬拒 + synonym pin 退役 —— 挂 18 列车,现在勿动工 #5082)。describe()进生成 JSON Schema,是 AI 作者读的那份契约,措辞照 ADR。UniqueScopeSchema文档块的单租户假豁免(「租户列是常量,复合索引退化为单列」—— 已被 driver-sql: 单租户栈上 organization_id 恒为 NULL,#3696 的 (tenant, col) 复合 UNIQUE 因 NULL-distinct 而完全不生效 —— 字段级 unique: true 静默零约束 #5030 证伪:常量是 NULL,SQL UNIQUE 是 NULL-distinct)替换为 D3 真相(NULL 桶 + COALESCE);字段级describe()同步。Resolved ✨ Set up Copilot instructions #2 非规范性引导入注释:新代码示例/脚手架输出'organization',裸true字段级永久合法。isOrganizationUnique(只认显式词,声明索引侧 SQL/Mongo 同步的单一事实源);isUniqueDeclared计入'organization'(词汇表接受而 driver 不读 = declarable-but-inert,ADR-0078 反例,测试钉死)。packages/lint(D5a / D5b)
unique/unscoped-declared-index(D5a,warning,advisory):声明索引裸unique: true—— 范围未表态的那一种拼写(driver-sql: 表级{ fields, unique: true }在 tenant-scoped 对象上落成平台级 UNIQUE —— normalizeDeclaredIndex 不补租户列(#4698 实例 2 移交) #4986 陷阱)。只判拼写,不推断租户性/posture(authoring 期不可知,validate/lint have no check for "declared but never read" metadata — three instances found in one app in a day #4698 已论证死路);fix 文案照 D5a 双词处方('global'= 今天的行为;'organization'= 每组织一持有者)。独立注册进AUTHORING_RULES(validate/build),lintDataModel直接调用服务os lint—— 三命令全覆盖、每条 finding 恰好一报([cli/lint] 作者时规则的命令覆盖漂移是系统性的:23/26 条手工接线规则只跑部分命令,9 条 error 级有门禁盲区(os build 会发布 os lint 拒绝的流) #4409 wiring 测试通过)。unique/double-declaration四象限重写(D5b):同列双声明按两侧拼写判象限 —— 跨范围 = 矛盾(装置级索引物理上更严、胜出,每组织意图静默死亡),同范围 = 冗余(同一索引声明两次)。旧的字段级'global'豁免取消(改报冗余);fix 文案中手写['organization_id', X]的建议退役,改说'organization'拼写(手写复合不是 NULL-safe,driver-sql: 单租户栈上 organization_id 恒为 NULL,#3696 的 (tenant, col) 复合 UNIQUE 因 NULL-distinct 而完全不生效 —— 字段级 unique: true 静默零约束 #5030)。再生成 + 门禁 + 锚点
content/docs/references/data/{field,object}.mdx经gen:schema && gen:docs再生成;api-surface.json经gen:api-surface(+isOrganizationUnique;合并 main 后的 merge-driver 递延件已按check:generated --fix收敛)。pnpm --filter @objectstack/spec check:generated:8/8 全绿(含check:docs/check:api-surface/check:authorable-surface)。scripts/adr-anchors.json新增三条(field.zod.ts / object.zod.ts / data-model-rules.ts),pnpm check:adr-anchors绿(23 文件)。@objectstack/specminor、@objectstack/lintminor。测试
pnpm --filter @objectstack/spec test(--maxWorkers=2)pnpm --filter @objectstack/lint testpnpm --filter @objectstack/cli test(--maxWorkers=2)pnpm typecheck(spec, lint, cli)新增/重写用例:spec
unique-scope.test.ts(词汇接受、'tenant'/'org'处方式拒绝、predicate 三元组、'organization'与true经 driver predicate 全等);clidata-model-rules.test.ts(D5a 规则齐套 + R10 四象限逐象限 fixture + 一报不重复)。范围外(照派发令,零触碰)
'organization'的 COALESCE 物化、drift 两侧、重复预检 —— 全部由 driver-sql: 单租户栈上 organization_id 恒为 NULL,#3696 的 (tenant, col) 复合 UNIQUE 因 NULL-distinct 而完全不生效 —— 字段级 unique: true 静默零约束 #5030 的 PR 承载(本 PR 晚于它合并)。true硬拒)—— ADR-0120 协议 18 项:D2 conversion(声明索引 unique: true → 'global')+ 裸 true 硬拒 + synonym pin 退役 —— 挂 18 列车,现在勿动工 #5082,挂起。content/docs/releases/零触碰。🤖 Generated with Claude Code
https://claude.ai/code/session_01Akrzh2mHi2siSNVPPtfTw7
Generated by Claude Code