Repository navigation
feat(objectql)!: a picklist, mapping or dataset name holds one package (bare-name phase 1, 1/4) - #30836
Conversation
Once the cold boot has sealed the registry (the engine plugin, at kernel:bootstrapped), a package whose picklist, mapping or dataset bears a name another installed package (or a built-in) already holds is refused at the package door (installPackage, ahead of every mutation) and at the item seam (registerItem), with the NAMESPACE_CONFLICT 422 envelope naming both holders and the namespace-prefixed rename. These consumers resolve the name with no package, so with two holders every caller was served the first-registered one (measured, stage 1). During a boot the doors judge nothing; a deployment whose packages already share such a name boots and warns once (BARE_NAME_COLD_BOOT_MODE 'warn', the switch the maintainer rules on). A package already holding a name is never a new holder. The environment catalog is never a holder (ADR-0005 overlay). ADR-0048 §3.4 narrowed (addendum 2026-11-06). Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1EuHiCqvh8VEz81QD
📓 Docs Drift CheckThis PR changes 1 package(s): 26 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 21 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 64d63e745efdf0c17f48205969c04ad459542594 && git checkout 64d63e745efdf0c17f48205969c04ad459542594
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7d9682ffd7115b89a7f4c5c8dfe14691afc1ce7b 417ed9b9c5afe55d27978b4738c76f1a20714f83 && git checkout -B drift-repro 7d9682ffd7115b89a7f4c5c8dfe14691afc1ce7b && git merge --no-ff 417ed9b9c5afe55d27978b4738c76f1a20714f83
node scripts/docs-audit/affected-docs.mjs --json 7d9682ffd7115b89a7f4c5c8dfe14691afc1ce7b
|
…der each package's namespace The five fixture packages, installed into one kernel, all declared the mapping `orders_pull`; a mapping name now holds one package per deployment, so the control's install was refused as a second holder. Each package now declares `<namespace>_orders_pull` (and the typo cases name `<namespace>_orders_pul`), so each case still measures only its own pull binding. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1EuHiCqvh8VEz81QD
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 Contract reviewServed-tier: Inputs read: card #30803 body and its three comments (triage 6677272063, seat claim 6677636083, os-dev-report 6679284054); decision card #30798 body and ruling C (6676670839); the dispatch order ① Derived judgments
② Semver levelBoth carriers read Check-runs on ③ Boundary flags
④ Design judgment
Findings (none must-fix):
Implemented-by: VERDICT: PASS |
…door only and keeps the .custom override path Contract-review follow-up on the bare-name phase 1 change: - the §3.4 narrowing note and P1.2 say a built-in's name is judged at managed install only, as the item seam asks no built-in; - P1.4 states that a paired `.custom` package's declared override is a stored row bound to its pair, never a package holder; - the changeset's measured producers name the two dataset names HotCRM and the examples share (`task_metrics`, `opportunity_metrics`), and its `Skills-docs:` line uses the claim template's `none` spelling. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1EuHiCqvh8VEz81QD
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 Contract review — delta on record 6679538573Served-tier: Inputs read: the commit message of ① Derived judgments
② Semver levelUnchanged. Check-runs on ③ Boundary flags
④ Design judgmentThe three ADR edits make the text say less, not more: a built-in is judged where the code judges it, and the one override path the narrowing could be misread as closing is named as open. The changeset's paragraph now carries the one cross-set coincidence a Who-moves reader needs. Holds. Findings (none must-fix):
Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 ACCEPT · #30803 (PR 1 of 4) · 2026-11-06T07:47Z · Head-sha: 维护者速读(席位校对)
席位意见:同意落地。代码面按安全目录先例(#22197)泛化,范围严格限于席位裁定的三类;其余八类待维护者确认安全重叠后进 PR2–PR4。 席位核对
|
…-crfe5o # Conflicts: # docs/adr/0048-cross-package-metadata-collision.md
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 ACCEPT addendum · #30803 (PR 1 of 4) · 2026-11-06T11:22Z · Head-sha: Carries ACCEPT 6679952314 (on
The contract review's judgments (6679538573, delta 6679688246) stand: no code line of this PR moved. |
Requested by Elon · project thread
domain:server#3 · os-elon-musk · session_016WV4d1EuHiCqvh8VEz81QD
Refs #30803 (PR 1 of 4; the last PR of the series carries
Fixes)Clause-②: no (narrowing) — the engine's package door (
SchemaRegistry.installPackage) and item seam (SchemaRegistry.registerItem) refuse, after the cold boot, a package registration they accepted before: one whose picklist, mapping or dataset bears a name another installed package holds. Nothing is widened.Metadata-protocol: narrowed
Skills-docs: none —
skills/**does not teach that two packages may share a picklist, mapping or dataset name, and the naming rule is phase 2's (#30805); the prescription ships in the refusal and warning text and in ADR-0048's 2026-11-06 addendum.docs/adr/0048-cross-package-metadata-collision.md(§3.4 narrowing note + addendum). The seat routes the approver.维护者速读
kernel:bootstrapped)后,第二个包再声明同名项,在安装入口和注册入口都被拒(422NAMESPACE_CONFLICT),提示里点出两个持有包和改名建议(命名空间前缀,如beta_status)。BARE_NAME_COLD_BOOT_MODE默认'warn',等你裁决是否改成拒绝启动。OS_METADATA_COLLISION=warn不降级。packages/objectql/src/bare-name-one-holder.ts(规则与开关)、registry.ts的refuseBareNameConflicts/sealBareNameHolders、plugin.ts的kernel:bootstrapped封印、ADR-0048 新附录。What changes
packages/objectql/src/bare-name-one-holder.ts(new) — the rule and its vocabulary, the security catalog's sibling (security-catalog-namespace.ts) generalised: the covered types (picklist,mapping,dataset), the declared names a manifest carries (declaredBareNames, the engine's own collection sources, nestedplugins[]one level), the holders (a package; a built-in, none today), the rename (suggestedBareNameRename: the namespace prefixed), and the one cold-boot switchBARE_NAME_COLD_BOOT_MODE('warn').packages/objectql/src/registry.ts—BareNameConflictError: the ADR-0112 envelope (code: 'NAMESPACE_CONFLICT',status: 422), every conflict in one refusal,conflicts[]of{ type, name, incomingPackageId, existingHolder, rename }.installPackageasksrefuseBareNameConflictsahead of every mutation (after the security catalog's and the default record page's refusals), records the package's claims, anduninstallPackageforgets them. No co-owner exemption, nocollisionPolicydowngrade.registerItemrefuses a package-bound picklist, mapping or dataset over another package's name.bareNameHoldersOtherThan): empty until sealed, empty when the incoming package already holds the name; holders are composite<packageId>:<name>slots in serving order plus install claims. ⛔ The bare slot (the environment catalog) is never read.sealBareNameHolders()(the seal + every name the boot left held twice, sorted) and the module-levelsealBareNameHoldersAtColdBoot(registry, mode, logger)(warn once, or refuse withdoor: 'cold-boot').packages/objectql/src/plugin.ts— the engine plugin seals atkernel:bootstrapped(after thekernel:readyinstall-local rehydrate), underBARE_NAME_COLD_BOOT_MODE.packages/objectql/src/security-catalog-namespace.ts— one header sentence ("for every other type §3.4's coexistence stands") points at the sibling.docs/adr/0048-cross-package-metadata-collision.md(Tier H) — a "Narrowed (2026-11-06)" note under §3.4, an Addenda-line entry, and the addendum: ruling C's text and phasing verbatim (record 6676670839), the measurement, what is refused, the cold boot and its switch, what stays as §3.4 has it, the types to follow, where it is implemented. It states that after phase 3 the refusal remains only as the backstop for namespace-less packages, the sharedbase/system/sysnamespaces and third-party packages in the deprecation window.scripts/adr-anchors/packages__objectql__src__bare-name-one-holder.ts.json— the anchor..changeset/30803-bare-name-one-package-holder.md—@objectstack/objectqlminor, both declaration lines, BREAKING Before / Now / Who moves / Unchanged,adr-0087: not-required (no-migration-prescription), measured producers.Why (measured, stage 1)
Booted showcase + two
AppPluginpackages sharing one bare name per type, both registration orders, onc17719dd(stage1-measurement.mdin the seat's project folder):picklistpicklist: NAME,/meta/objectoptions + write validatorVALIDATION_FAILEDmappingPOST /data/OBJ/import{ mappingName }400 MAPPING_TARGET_MISMATCHdatasetPOST /analytics/dataset/query{ datasetName }Verification
packages/objectql/src/registry-bare-name-one-holder.test.ts(new, 40 cells, realObjectKernel+ObjectQLPlugin, themanifestservice door): per type × door — package door (both holders, rename, no record left), item seam, nested plugin, uninstall releases, same-package reload (CONTROL), environment registration never judged and never a holder (CONTROL); per type at the cold boot — boots and warns once naming both holders and the rename; both holders keep their name across a reload while a third is refused naming both;'refuse'refuses with the doors' envelope; the cold boot's reading (served holder as existing, sorted, one warning); no-clash boot is silent; every conflict in one refusal; no namespace, no rename; a built-in name; CONTROLs: a non-covered type (page) coexists, a picklist extension is no holder,collisionPolicy: 'warn'does not downgrade.packages/qa/dogfood/test/bare-name-one-holder.dogfood.test.ts(new, booted showcase + twoAppPluginpackages +MarketplaceInstallLocalPlugin): the cold boot boots and logs ONEWARNline naming all three names, both holders and the renames;POST /marketplace/install-localof a third package declaring the held picklist name is422 PLUGIN_REGISTER_FAILEDnaming both holders andgamma_…, package list unchanged; a package redeclaring the showcase's mapping and dataset names is refused namingcom.example.showcase; CONTROL: fresh names install. 4 passed.@objectstack/objectqldist (dogfood imports it through dist) → 3 of 4 red (no warning line; both install-local cases200), the fresh-names CONTROL green; green after rebuilding the dist.@objectstack/objectqlfull suite: 556 files / 11108 tests passed;pnpm --filter @objectstack/objectql typecheck(incl. the test-typecheck ratchet) exit 0.dispatch-gates --commands, 81 derived + 5 roster families, reconciled with--ran) and the full dogfood battery at this head (75d1339f,origin/mainmerged): both were running when this PR opened; their results are in the os-dev report on Bare-name phase 1: refuse a second package holding a non-object name whose consumers resolve without package context (ruling C on #30798) #30803.Kept (the card's three)
objectName:name; jobs stay keyedappId:jobName(app-artifact-handlers.jobs.test.ts); the unmanaged import's all-type refusal is untouched. Every existing security-catalog pin is unchanged (registry-security-catalog-namespace.test.tsand its siblings green in the full suite).Series and out of scope
api, object-lessaction, engine function registry;email_template,webhook,connector,flow;agent,skill. A type the security seat claims lands there instead (asked of the maintainer by the seat).toolandrag_pipelineregister by name likeagentat the artifact door; included only on the seat's word.service-ai: agents, skills,action_NAMEtool names, ToolRegistry) live inobjectstack-ai/cloud, not in this repo, and were NOT MEASURED here.analytics_cube(a non-host package's cube never reaches the analytics service, 404CUBE_NOT_FOUNDboth orders: a separate reach gap).Deviations
kernel:bootstrappedhook, not the registry's own doors: during a boot every package registers again, so the doors judge nothing until every package of the boot has registered. A kernel withbootPhaseHooks: falsenever seals (its doors never refuse); stated in the module doc and the ADR.Human Minimum
packages/specholds no runtime logic. (not touched)main, one pull request (first of a series by type family, per the seat).packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code