Skip to content

Commit 5d1382a

Browse files
docs(adr): ADR-0150 addresses behavioural metadata by (package, name); objects keep the literal prefix (#31586)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XBsDxUffG35RvTHueTQ8ZGa)_ Refs #31527 Refs #30806 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:新增 ADR-0150(Proposed)。流程、动作、页面等行为元数据按(软件包, 名称)寻址,对象继续用字面前缀。你对 Q1、Q2、Q3 和 D2 的裁决已全部写入,ADR 里不再有待决问题:AI 侧名字用 NS-NAME,绑定对象的动作用 OBJECT-ACTION;职位同名保持拒绝;命名空间永远不能等于对象名(各入口双向拒绝);同一命名空间内行为元数据同名唯一。七轮合约审查的意见也已并入。另有三条拒绝不在你的裁决里,由本 ADR 新增,合并即视为你接受:一是命名空间不能拼成路由里的保留词(如 trigger、hooks、actions、global);二是这几类元数据的名字不能以下划线开头,因为下划线开头的路径段归路由所有;三是软件包里的元数据如果按名字引用一个只有环境(客户自建)才有的条目,今天能读到,以后会被拒绝(D3 (a))。前两条在第一方代码里都是 0 处命中;第三条在软件包代码里无法测量,升级报告会逐条列出。同时在 ADR-0028 和 ADR-0048 的状态行各加一行指向,并重新生成 ADR 索引。只改文档,不改代码。 - 为什么改:你在 13:12 裁定转向包寻址、撤回改名(#30806 S1/S5),15:36 裁定按类型分档,16:30 裁定 Q1/Q2,17:13 裁定 Q3,17:17 同意 D2。本 ADR 把这些裁决落成一份设计,并附一份可逐个发版的子卡执行计划(23 个构建子卡加 13 个按类型收窄的子卡)。 - 风险与回滚:不改代码,没有运行时风险。不合并即回滚。子卡已按史诗裁决立卡,但这份记录被接受之前,任何子卡都不落地。 - 先看:TL;DR 第 7 条(新增的五条拒绝:两条是你裁的,三条是本 ADR 加的),然后看 D3 解析规则、D5 地址语法和「Execution plan」一节。 - 两分钟验证:运行 pnpm check:adr-index 和 node scripts/check-adr-links.mjs,两条都应退出 0。 - 需要你做的:审阅 ADR-0150,同意的话把这个 PR 转为 ready 并合并。 ## What changes - `docs/adr/0150-behavioural-metadata-addressed-by-package-objects-keep-the-literal-prefix.md` (new, Proposed). It covers: - D1, the ruled four buckets, with why objects differ; - D2, identity is the package id and the address is the namespace (accepted 17:17Z). Namespace co-owners keep names unique (child N). Measured on HotCRM 6961ab1: 0 of 297 keys are shared; - D3, the ruled resolution rule and its exceptions; - D4, the runtime keys, and a package-less row over a package-held name, which is that holder's customization (ADR-0146 D11 migrates it) and never a second item; - D5, the address grammar: - the grammar's literal segments are read first. The namespace-position set is a `spec/kernel` constant that `route-ledger.ts`'s test pins. The install gate refuses a namespace that spells one, and a name of a bucket 2/3 type that starts with `_` is refused (child P). Both refusals are added by this record beyond the rulings, for acceptance by merge; - then an optional namespace segment, then the bare grammar; - a reserved `_env` segment for environment items; - a 409 with the candidates; - D6, the AI face as ruled: `NS-NAME`, and `OBJECT-ACTION` for an object-bound action. A collision check of the two forms led to Q3, ruled A: a namespace never equals an object name, refused in both directions at every object door (child K); - D7, positions keep the one-holder refusal, as ruled; - D8, the stored references: a package column beside the name, backfilled from the phase-1 holder, with the ambiguous set reported and never guessed; - D9, what retires, and D10, the upgrade; - the execution plan: - 23 build children (N and K included) plus 13 per-type backstop narrowings; - each with who builds it, its file surface, its Clause-② and narrowing arm, and red line 3 releasability; - of the 12 types phase 1 does not cover, hook has no first-match read and job keeps `jobKeyFor`'s reading; the narrowing for the other 10 is declared (E4, E8, G1). `rag_pipeline`, which the ruled table names, was retired on main by #31456 before the inventory was measured, so no child touches it; - a fleet-pace critical path of about 4 to 5 working days. - `docs/adr/0028-metadata-naming-and-namespace-isolation.md` and `docs/adr/0048-cross-package-metadata-collision.md`: one status-header pointer line each, effective on acceptance. The ADR-0048 line cites the Q2 ruling. - `docs/adr/README.md`: regenerated (`pnpm adr:index`). ## Tests None. This is a docs-only design record (`skip-changeset`). ## Acceptance notes - ADR-0149 is on `main` (#31535), so this record is 0150. - The stage-1 inventory this rests on is `/mnt/project-files/pm-orders/server/31527-inventory.md`, measured read-only at 86e3766. - D9 also records, as a hand-off outside this epic, the scenario scorer's `bare-name` Build check and its build prompt narrowed to bucket 1 types, raised by the cloud#3600 epic seat (6726694021 on #31527). It lands after P and adds no child. - Every question is recorded as ruled: Q1 and Q2 (record 6724050556), Q3 (6724530715) and D2 (6724570466), all on #31527. - The contract review of aeafb42 (record 6724089022) and the delta reviews of 565c63c (6724370084), 082272a (6724845936), 5c69b2a (6725073557), 86412ee (6725359156), 7238c28 (6725571288) and cdf9c9e (6725879758) are folded in: S1–S18 and N1–N25. `?package=X` on a `/meta` read is the exact qualifier the D2 ruling names: X's item or a refusal naming the holders, never X's closure. Studio's held package id reaches the server only as `?package=`, so it is the exact qualifier too, never a caller. A reference written in an environment-authored overlay resolves as a caller with no package, so the ruled Q2 binding works. D3 also states the two readings child R (#31637) implements: a package caller never resolves to an environment-authored item by bare name, and an overridable platform baseline ranks below a package holder. ## Verification At head b54cdad: - every family from `dispatch-gates --commands` was run, with exit codes captured before any pipe; - all 21 derived commands exited 0; - `dispatch-gates --ran` answered: 21 run, 0 NOT-MEASURED. No package is touched, so there is no scoped `pnpm lint` family to run. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FEkQ5psDxi37aZAzXB36TT <!-- signed: epic:#31527#1 · os-project-manager · session_01FEkQ5mLd7pR2PHjsPwiGkz --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8c9260f commit 5d1382a

4 files changed

Lines changed: 373 additions & 6 deletions

‎docs/adr/0028-metadata-naming-and-namespace-isolation.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0028: Metadata Naming & Namespace Isolation — Derived Physical Names, Namespace-Scoped Identity, and a Single Kernel Contract
22

3-
**Status**: Deferred (2026-07-16; proposed 2026-06-01) — the target model (namespace as identity dimension, short authored names, derived physical names, `namingMode` dual-read, `sys` reservation, namespaced transport segments) is entirely unbuilt, and adopting it now would be a platform-wide migration (physical table derivation, transport routes, dual-read compatibility) with no present pain to justify it: the literal-prefix current-state contract (`spec/kernel/namespace-prefix.ts`) is enforced, collision-proof (ADR-0028/0048), and working. **Re-open trigger**: real multi-package ecosystem naming pain — e.g. a marketplace package whose natural short names are unusable under literal prefixing, or cross-environment portability requiring namespace remapping. Until then this stays a recorded design, not planned work. · **Amended** (2026-10-25, [ADR-0141](./0141-inter-app-dependency-declared-checked-before-install.md) D6, rulings [`6369704387`](https://github.com/objectstack-ai/objectstack/issues/25403#issuecomment-6369704387) and [`6377678758`](https://github.com/objectstack-ai/objectstack/issues/25403#issuecomment-6377678758) on #25403): D4's sentence is rewritten from "Apps are sandboxed: no cross-app references" to "No cross-app reference without a declared dependency", and the three sentences that restate it (TL;DR item 4, D6's author-time bullet, leak-point 3) follow; nothing else moves and the record stays Deferred.
3+
**Status**: Deferred (2026-07-16; proposed 2026-06-01) — the target model (namespace as identity dimension, short authored names, derived physical names, `namingMode` dual-read, `sys` reservation, namespaced transport segments) is entirely unbuilt, and adopting it now would be a platform-wide migration (physical table derivation, transport routes, dual-read compatibility) with no present pain to justify it: the literal-prefix current-state contract (`spec/kernel/namespace-prefix.ts`) is enforced, collision-proof (ADR-0028/0048), and working. **Re-open trigger**: real multi-package ecosystem naming pain — e.g. a marketplace package whose natural short names are unusable under literal prefixing, or cross-environment portability requiring namespace remapping. Until then this stays a recorded design, not planned work. · **Amended** (2026-10-25, [ADR-0141](./0141-inter-app-dependency-declared-checked-before-install.md) D6, rulings [`6369704387`](https://github.com/objectstack-ai/objectstack/issues/25403#issuecomment-6369704387) and [`6377678758`](https://github.com/objectstack-ai/objectstack/issues/25403#issuecomment-6377678758) on #25403): D4's sentence is rewritten from "Apps are sandboxed: no cross-app references" to "No cross-app reference without a declared dependency", and the three sentences that restate it (TL;DR item 4, D6's author-time bullet, leak-point 3) follow; nothing else moves and the record stays Deferred. · **Superseded in part by [ADR-0150](./0150-behavioural-metadata-addressed-by-package-objects-keep-the-literal-prefix.md)** (proposed 2026-11-08, maintainer rulings of 2026-11-08 on #30806 and #31527): D1's `(namespace, type, name)` identity and D3's addressing segment are adopted for behavioural and presentation metadata, keyed `(package, name)`; objects keep the literal prefix (ADR-0129), so D2 stays Deferred. This line takes effect when ADR-0150 is accepted.
44
**Deciders**: ObjectStack Protocol Architects
55
**Supersedes**: the hand-written object-namespace-prefix authoring rule documented in `packages/spec/src/kernel/manifest.zod.ts` (the `namespace` field) and enforced by `validateNamespacePrefix()` in `packages/spec/src/stack.zod.ts` — that rule's standalone record is [ADR-0129](./0129-object-name-is-the-canonical-id.md) (2026-08-29; it governs until this ADR's re-open trigger fires).
66
**Builds on**: [ADR-0005](./0005-metadata-customization-overlay.md) (one Zod source per type, org overlay), [ADR-0008](./0008-metadata-repository-and-change-log.md) (Repository · ChangeLog · Cache · Registry; `MetaRef = org/type/name`), [ADR-0010](./0010-metadata-protection-model.md) (protection model), [ADR-0019](./0019-app-as-consumer-unit.md) (app as the consumer-installable unit), [ADR-0025](./0025-plugin-package-distribution.md) (package distribution), [ADR-0029](./0029-kernel-object-ownership-and-platform-objects-decomposition.md) (**prerequisite** — kernel object ownership; D5/D6 below assume the kernel is properly owned per ADR-0029)

‎docs/adr/0048-cross-package-metadata-collision.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0048: Cross-package metadata collision — package-id identity, namespace install gate, package-scoped resolution
22

3-
**Status**: Revised (2026-06-13) — supersedes the original *per-item collision detection* framing. The runtime guard shipped under the original proposal is **retained as a same-package authoring backstop**; the strategic direction for the app-marketplace era is revised below. · **Superseded in part by [ADR-0146](./0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md)** (2026-10-31) — §3.3's sentence that a context-free read "is best-effort: it returns the first match": a read of a name a code package ships has an owner — that package, or the declared override its paired `<base>.custom` publishes — and a same-named item of a package outside that closure is served only under its own address (ADR-0146 D8); and §3.4's coexistence "by construction", for a base's dependency closure only: the paired `<base>.custom`'s item bearing a base item's name is a declared override or is refused at publish — declare or rename — and any other dependent package's item bearing a base item's name is refused at publish — rename; only the pair declares (ADR-0146 D2, ruled 2026-10-31 on #28364 and #28390). Packages with no dependency relation keep §3.3 and §3.4 as written; qualify-to-cross, the same-package overwrite and the 2026-10-08 addendum stand. · **Amended** (2026-11-07, [#30794](https://github.com/objectstack-ai/objectstack/issues/30794)): §3.5 — rename-on-install ships for **unmanaged** copies, through the one rename engine `duplicatePackage` applies; a managed install is never renamed and §3.2 stands for it as written. See the dated note under §3.5.
3+
**Status**: Revised (2026-06-13) — supersedes the original *per-item collision detection* framing. The runtime guard shipped under the original proposal is **retained as a same-package authoring backstop**; the strategic direction for the app-marketplace era is revised below. · **Superseded in part by [ADR-0146](./0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md)** (2026-10-31) — §3.3's sentence that a context-free read "is best-effort: it returns the first match": a read of a name a code package ships has an owner — that package, or the declared override its paired `<base>.custom` publishes — and a same-named item of a package outside that closure is served only under its own address (ADR-0146 D8); and §3.4's coexistence "by construction", for a base's dependency closure only: the paired `<base>.custom`'s item bearing a base item's name is a declared override or is refused at publish — declare or rename — and any other dependent package's item bearing a base item's name is refused at publish — rename; only the pair declares (ADR-0146 D2, ruled 2026-10-31 on #28364 and #28390). Packages with no dependency relation keep §3.3 and §3.4 as written; qualify-to-cross, the same-package overwrite and the 2026-10-08 addendum stand. · **Amended** (2026-11-07, [#30794](https://github.com/objectstack-ai/objectstack/issues/30794)): §3.5 — rename-on-install ships for **unmanaged** copies, through the one rename engine `duplicatePackage` applies; a managed install is never renamed and §3.2 stands for it as written. See the dated note under §3.5. · **Amended by [ADR-0150](./0150-behavioural-metadata-addressed-by-package-objects-keep-the-literal-prefix.md)** (proposed 2026-11-08): the 2026-11-06 addendum's one-holder refusal retires type by type as each type's readers carry the package; it stays for platform-requested bare names and, as ruled (ADR-0150 Q2 = A, record [`6724050556`](https://github.com/objectstack-ai/objectstack/issues/31527#issuecomment-6724050556) on #31527), for positions. This line takes effect when ADR-0150 is accepted.
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0003](./0003-package-as-first-class-citizen.md) (package as first-class citizen), [ADR-0005](./0005-metadata-customization-overlay.md) (artifact vs runtime overlay precedence), [ADR-0008](./0008-metadata-repository-and-change-log.md) (metadata repository, `MetaRef` identity), [ADR-0010](./0010-metadata-protection-model.md) (package provenance / `_packageId` stamping)
66
**Consumers**: `@objectstack/objectql` (`SchemaRegistry.registerItem`, `ObjectQL.registerApp`, install path), `@objectstack/console` / `objectui` (routing + metadata resolution), package authors, CLI/CI install path, the app marketplace registry.

0 commit comments

Comments
 (0)