|
5 | 5 | **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) |
6 | 6 | **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. |
7 | 7 | **Surfaced by**: ADR-0046 review (doc naming) → generalised to all bare-named metadata → re-examined through the app-marketplace install lens. |
8 | | -**Addenda**: [2026-08-08 — publish-time / marketplace namespace exclusivity](#addendum-2026-08-08-publish-time--marketplace-namespace-exclusivity) (**Accepted** 2026-08-08 — maintainer ruling on #1825, [acceptance comment 5226411687](https://github.com/objectstack-ai/objectstack/issues/1825#issuecomment-5226411687), landed by PR #6671; additive to §3.2, supersedes nothing). |
| 8 | +**Addenda**: [2026-08-08 — publish-time / marketplace namespace exclusivity](#addendum-2026-08-08-publish-time--marketplace-namespace-exclusivity) (**Accepted** 2026-08-08 — maintainer ruling on #1825, [acceptance comment 5226411687](https://github.com/objectstack-ai/objectstack/issues/1825#issuecomment-5226411687), landed by PR #6671; additive to §3.2, supersedes nothing); [2026-11-06 — bare names resolved without a package hold one package](#addendum-2026-11-06-bare-names-resolved-without-a-package-hold-one-package--34-narrowed) (**Accepted**, ruling C on #30798, phase 1; narrows §3.4 for picklists, mappings and datasets, supersedes nothing). |
9 | 9 |
|
10 | 10 | --- |
11 | 11 |
|
@@ -266,6 +266,21 @@ package — stays covered by the `naming/namespace-prefix` lint in `os lint`. |
266 | 266 | > above. Every other type keeps §3.4 as written. See |
267 | 267 | > [Addendum (2026-10-08)](#addendum-2026-10-08-the-security-catalog-holds-one-name-per-deployment--34-narrowed). |
268 | 268 |
|
| 269 | +> **Narrowed (2026-11-06) — picklists, mappings and datasets are out of §3.4.** |
| 270 | +> Their consumers resolve the name with no package — a field's `picklist`, an |
| 271 | +> import's `mappingName`, a dashboard's `datasetName` — so with two holders every |
| 272 | +> caller is served the first-registered one, measured on a booted two-package |
| 273 | +> deployment (#30803). Once the cold boot has sealed the registry, a package whose |
| 274 | +> picklist, mapping or dataset bears a name another installed package holds is |
| 275 | +> refused at managed install and at package registration (a built-in's name, at |
| 276 | +> managed install), naming both holders and the rename. ⛔ The environment catalog is never a |
| 277 | +> holder: an environment item under a package's name is the ADR-0005 overlay. |
| 278 | +> The other types the measurement found resolving without a package context — |
| 279 | +> `api`, the object-less `action`, the engine function registry, `email_template`, |
| 280 | +> `webhook`, `connector`, `flow`, `agent` and `skill` — are to follow, each in its |
| 281 | +> own change. Every other type keeps §3.4 as written. See |
| 282 | +> [Addendum (2026-11-06)](#addendum-2026-11-06-bare-names-resolved-without-a-package-hold-one-package--34-narrowed). |
| 283 | +
|
269 | 284 | ### 3.5 Namespace rename-on-install is deferred (non-goal) |
270 | 285 |
|
271 | 286 | Renaming a colliding namespace on install would require rewriting **every** |
@@ -869,3 +884,134 @@ for a package-bound registration that reaches the registry directly. Every packa |
869 | 884 | registration reaches the package door before any in-memory registrar runs, so the |
870 | 885 | boot of a conflicting composition — an artifact boot included — is refused in its |
871 | 886 | first phase. |
| 887 | + |
| 888 | +--- |
| 889 | + |
| 890 | +## Addendum (2026-11-06): bare names resolved without a package hold one package — §3.4 narrowed |
| 891 | + |
| 892 | +> **Status of this addendum: Accepted** — it records phase 1 of a maintainer |
| 893 | +> ruling. It **narrows §3.4 for three more metadata types and supersedes no other |
| 894 | +> text**: §§1–6, the 2026-08-08 addendum and the 2026-10-08 addendum stand |
| 895 | +> verbatim. |
| 896 | +> |
| 897 | +> **Ruling this addendum records** — #30798, answered by the maintainer |
| 898 | +> 「同意 c」 (ruling record 6676670839, 2026-11-06). The ruled option's text, |
| 899 | +> verbatim: |
| 900 | +> |
| 901 | +> > **C. Every name carries the namespace** — Extend ADR-0129's literal prefix to |
| 902 | +> > every package-authored top-level metadata name (`crm_send_email`), except |
| 903 | +> > names already derived from a prefixed object (fields, object extensions, |
| 904 | +> > container views) or already namespaced (translations `<ns>_<locale>`); |
| 905 | +> > enforced at `defineStack`/`os validate` and the save door; same-name clashes |
| 906 | +> > become impossible by construction |
| 907 | +> |
| 908 | +> and its phasing, verbatim: "**Now:** extend the existing registration refusal |
| 909 | +> (A's mechanism) to the silent types. It closes silent mis-resolution for |
| 910 | +> packages already in the field. After phase 3 it remains only as the backstop |
| 911 | +> for the cases C cannot reach: packages that declare no namespace, the shared |
| 912 | +> `base`/`system`/`sys` namespaces, and third-party packages inside the |
| 913 | +> deprecation window." Phase 1 is #30803; the lint, the templates and the skills |
| 914 | +> are phase 2 (#30804, #30805); `defineStack`, the save door and the ADR-0129 |
| 915 | +> amendment are phase 3 (#30806). |
| 916 | +
|
| 917 | +### P1.1 Why §3.4's premise does not hold for these three types |
| 918 | + |
| 919 | +§3.4 rests on every caller carrying its package id. These three consumers carry |
| 920 | +none when they resolve the name. Measured on a booted showcase with two packages, |
| 921 | +`alpha` and `beta`, each shipping one item per type under the same bare name, in |
| 922 | +both registration orders (#30803, stage 1): |
| 923 | + |
| 924 | +- **`picklist`** — a field's `picklist: NAME` resolves the first-registered |
| 925 | + package's list for both packages' objects: the second package's object is |
| 926 | + served the other package's options, and its own values are refused at write |
| 927 | + (`VALIDATION_FAILED`). |
| 928 | +- **`mapping`** — an import's `mappingName` resolves the first-registered |
| 929 | + package's mapping; the other package's import answers `400 |
| 930 | + MAPPING_TARGET_MISMATCH`. |
| 931 | +- **`dataset`** — a dashboard's `datasetName` query runs the first-registered |
| 932 | + package's dataset. |
| 933 | + |
| 934 | +Each answer flips with registration order, and none warns. |
| 935 | + |
| 936 | +### P1.2 What is refused |
| 937 | + |
| 938 | +Once the cold boot has sealed the registry (P1.3), a package registration is |
| 939 | +refused when a picklist, mapping or dataset it declares bears a name another |
| 940 | +holder already holds — at managed install (the package door, |
| 941 | +`SchemaRegistry.installPackage`, ahead of every mutation) and at package |
| 942 | +registration (the item seam, `SchemaRegistry.registerItem`, for a package-bound |
| 943 | +registration that reaches the registry directly). The holders: |
| 944 | + |
| 945 | +- **an installed package** — including a disabled one, which is still installed |
| 946 | + and still holds its names; a nested plugin declares under its parent package; |
| 947 | +- **a built-in** — none today for these types; the vocabulary keeps the holder so |
| 948 | + a platform-held name is refused the way the security catalog refuses one, and, |
| 949 | + as there, only the package door asks it. |
| 950 | + |
| 951 | +⛔ **Never the environment catalog** — the one difference from N.2. For these |
| 952 | +types an item authored in this environment under a package's name is the ADR-0005 |
| 953 | +overlay of that package's item, not a second holder; a registration with no |
| 954 | +package is never judged, and the registry's bare slot is never read as a holder. |
| 955 | + |
| 956 | +The error is N.2's envelope (`422`, the §3.2 namespace gate's code), lists every |
| 957 | +conflicting name in one refusal, names both holders, and carries the rename that |
| 958 | +clears it: the incoming package's namespace prefixed to the name (`beta_status`), |
| 959 | +the one naming rule ruling C ends at. A package with no namespace, or one in a |
| 960 | +shared `base`/`system`/`sys` namespace, is refused without a rename. |
| 961 | + |
| 962 | +### P1.3 The cold boot |
| 963 | + |
| 964 | +Every package registers again at every boot, through the same two doors, so a |
| 965 | +deployment that already holds a clash would stop booting if the doors refused |
| 966 | +then. During a boot the doors therefore judge nothing. Once every package of the |
| 967 | +boot has registered — `kernel:bootstrapped`, after the `kernel:ready` rehydrates — |
| 968 | +the engine plugin seals the registry, and every name two installed packages |
| 969 | +already hold is handled by one switch, `BARE_NAME_COLD_BOOT_MODE`: |
| 970 | + |
| 971 | +- **`'warn'`** — one warning line naming every such name, both holders and the |
| 972 | + rename; the boot continues and keeps serving the first-registered holder; |
| 973 | +- **`'refuse'`** — the boot is refused with the P1.2 envelope, as N.3's |
| 974 | + amendment refuses the security catalog's. |
| 975 | + |
| 976 | +The switch is `'warn'` until the maintainer rules on the cold boot of a |
| 977 | +deployment that already holds a clash. In either mode the doors refuse a NEW |
| 978 | +holder after the seal. A package that already holds a name is never a new holder |
| 979 | +of it: a reload, a re-install or an upgrade of a package that came through the |
| 980 | +boot beside another holder keeps its name, while a third package is refused, |
| 981 | +naming both. A kernel that withholds the boot-phase hooks never seals, and there |
| 982 | +the doors never refuse. |
| 983 | + |
| 984 | +### P1.4 What stays as §3.4 has it |
| 985 | + |
| 986 | +- **Same-package re-registration** is one holder, not two. |
| 987 | +- **Every type not listed** keeps §3.4's coexistence: two packages' `page/home` |
| 988 | + still coexist, an object-bound action stays keyed `objectName:name`, a job stays |
| 989 | + keyed `appId:jobName`, and the unmanaged import's own all-type refusal is |
| 990 | + unchanged. |
| 991 | +- **A picklist extension** (`picklistExtensions`) adds values to another |
| 992 | + package's list by design; it holds no name. |
| 993 | +- **A paired `.custom` package's declared override** (ADR-0146 D2) is a stored |
| 994 | + row bound to its pair, never a package holder, so the refusal does not close |
| 995 | + the override path. |
| 996 | +- **No downgrade.** `OS_METADATA_COLLISION=warn` softens the §3.2 namespace gate |
| 997 | + only. |
| 998 | + |
| 999 | +### P1.5 To follow |
| 1000 | + |
| 1001 | +The same measurement found these types resolving without a package context too: |
| 1002 | +`api` (the endpoint store is keyed by name — the other package's path answers |
| 1003 | +404), the object-less `action` (the last handler runs while `/meta` serves the |
| 1004 | +first declaration), the engine function registry (`functions:`, last wins, read by |
| 1005 | +flow `script` nodes and action `target` refs), `email_template`, `webhook` (the |
| 1006 | +second package's webhook is never subscribed), `connector`, `flow`, `agent` and |
| 1007 | +`skill`. Each joins P1.2's list in its own change under #30803, with its own pins, |
| 1008 | +narrowing §3.4 the same way; a type the security seat claims lands there instead. |
| 1009 | +`tool` and `rag_pipeline` share a reader with `agent` and were not measured. |
| 1010 | + |
| 1011 | +### P1.6 Where it is implemented |
| 1012 | + |
| 1013 | +`@objectstack/objectql`: the rule, the holders and the switch in |
| 1014 | +`bare-name-one-holder.ts`; the package door in `SchemaRegistry.installPackage` and |
| 1015 | +the item seam in `SchemaRegistry.registerItem`; the seal in |
| 1016 | +`SchemaRegistry.sealBareNameHolders`, called by the engine plugin at |
| 1017 | +`kernel:bootstrapped` through `sealBareNameHoldersAtColdBoot`. |
0 commit comments