Skip to content

docs(adr): ADR-0028 metadata naming & namespace isolation + ADR-0029 kernel object ownership#1431

Merged
os-zhuang merged 5 commits into
mainfrom
claude/lowcode-metadata-naming-conflicts-nrnaT
Jun 1, 2026
Merged

docs(adr): ADR-0028 metadata naming & namespace isolation + ADR-0029 kernel object ownership#1431
os-zhuang merged 5 commits into
mainfrom
claude/lowcode-metadata-naming-conflicts-nrnaT

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Two Proposed design ADRs (docs-only, no code) addressing metadata naming conflicts as more marketplace packages get installed. Today only object is collision-protected — and by the wrong mechanism (hand-written crm_account literal prefix); the other ~23 metadata kinds collide silently (connectors literally logger.warn('… replaced') then overwrite).

Developed collaboratively; grounded in a codebase scan (with file:line citations) and mainstream-platform practice (Salesforce 2GP / ServiceNow scoped apps / Dataverse / VS Code / Kubernetes).

ADR-0028 — Metadata Naming & Namespace Isolation

  1. Namespace as identity dimension — short authored names; identity = (namespace, type, name). crm.send_email / todo.send_email stop colliding by construction.
  2. Physical names derived at the storage boundary — invert the existing StorageNameMapping pass-through; the prefix becomes invisible to authors/AI (the concrete answer to the hallucination concern that motivated the literal prefix).
  3. Namespace as an addressing segment at transport/api/v1/data/{namespace}/{object} + namespaced generated GraphQL/OData/SDK/MCP.
  4. Apps sandboxed — no cross-app references (security boundary); only app → kernel is legal.
  5. Unified kernel contract (sys) — one well-known import (sys.user); ownership distributed (see ADR-0029).

Breakage-controlled migration: per-package namingMode: 'literal' | 'short' flag + idempotent dual-read resolveTableName + never force-republishing sealed artifacts ⇒ no flag day; the only breaking step (P4) is per-package opt-in and codemod-driven (os migrate namespace).

ADR-0029 — Kernel Object Ownership & platform-objects Decomposition (prerequisite)

Carved out from 0028 because it is a distinct, lower-risk, template-transparent concern that 0028's D5/D6 depend on, and should ship first.

  • First-party capabilities become plugins that own their sys_* objects and behavior (fixing the monolith where plugins declare namespace:'sys' but objects are defined centrally in platform-objects).
  • Small core (identity/org hub + metadata) vs capability plugins (audit/jobs/email/approvals/sharing/ai/webhooks).
  • Shared reserved sys namespace, single-owner-per-object (not per-namespace, not a monolith owner).
  • Hub + dependencies/loadOrder instead of centralization; decompose platform-objects behind a re-export facade.
  • Phased K0–K4 with exit gates; sequenced before 0028's naming flip.

Sequencing

ADR-0029 (kernel ownership, template-transparent) → ADR-0028 P0–P1 (foundations + warn-only) can run in parallel → ADR-0028 P3–P4 (transport + authoring flip).

Open questions for reviewers

  • Qualified kernel-reference syntax (sys.user vs sys:user).
  • Confirm kernel = unified sys (vs domain-partitioned) — argued from the cross-reference graph (~60 lookups converge on sys_user/sys_organization).
  • Home of the security/RBAC objects (plugin-sharing vs a dedicated plugin-rbac); base tier as plugin-identity vs platform-objects-base.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4

Propose retiring the hand-written namespace-prefix authoring rule
(only objects are protected today; ~23 other metadata kinds collide
silently, e.g. connectors last-wins-overwrite) in favor of:

- namespace as an identity dimension (short authored names; identity
  = (namespace, type, name))
- physical table names derived at the storage boundary, invisible to
  authors/AI (inverting the existing StorageNameMapping pass-through)
- namespace as an addressing segment at transport surfaces (data API,
  metadata API, generated GraphQL/OData/SDK/MCP)
- app sandboxing: no cross-app references (security boundary); only
  app -> kernel references are legal
- a single unified reserved kernel namespace (sys) whose contract is
  unified but whose object ownership is distributed across first-party
  capability plugins (single-owner-per-object), decomposing the
  platform-objects monolith

Grounded in a codebase scan (current-state findings + kernel
cross-reference graph) and mainstream platform practice
(Salesforce 2GP / ServiceNow scoped apps / Dataverse). Includes a
phased, non-breaking migration plan.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 1, 2026 3:02am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation size/m labels Jun 1, 2026
ADR-0004 is "Cloud Control Plane", not the object-namespace-prefix rule
(which lives only in manifest.zod.ts / stack.zod.ts, with no standalone
ADR). Replace the broken ./0004-object-namespace-prefix.md link with a
Supersedes note pointing at the actual source files.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
Rework the migration section around three compatibility mechanisms so
existing templates are never broken in a flag-day cutover:

- per-package `namingMode: 'literal' | 'short'` manifest flag (old and
  new packages coexist in one instance; migration is opt-in per package)
- idempotent, namespace-aware `resolveTableName` (dual-read) so adding
  derivation does not turn `crm_account` into `crm_crm_account`
- sealed artifacts are never force-republished (codemod rewrites source
  templates only)

Each phase (P0 foundations → P5 remove-legacy) now has an explicit exit
gate; the only breaking step (P4) is per-package opt-in and driven by an
`os migrate namespace` codemod. Kernel refactor (P2) is decoupled and
template-transparent.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
…reference it

Carve the kernel-decomposition concern out of ADR-0028 into its own ADR:
first-party capabilities become plugins that own their sys_* objects +
behavior (correcting the platform-objects monolith where plugins declare
namespace:'sys' but the objects are defined centrally).

ADR-0029:
- small core (identity/org hub + metadata) vs capability plugins
  (audit/jobs/email/approvals/sharing/ai/webhooks) that each own their objects
- shared reserved `sys` namespace, single-owner-PER-OBJECT (not per-namespace,
  not a monolith owner)
- hub + dependencies/loadOrder instead of centralization
- decompose platform-objects behind a re-export facade
- template-transparent, independently shippable, sequenced BEFORE the 0028
  naming flip; phased K0-K4 with exit gates

ADR-0028: Phase 2 now delegates to ADR-0029; header gains it as a
prerequisite; D5 points to ADR-0029 as the authoritative source for the
ownership mechanics (0028 keeps only the naming/contract decision).

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@github-actions github-actions Bot added size/l and removed size/m labels Jun 1, 2026
@os-zhuang os-zhuang changed the title docs(adr): ADR-0028 — Metadata Naming & Namespace Isolation docs(adr): ADR-0028 metadata naming & namespace isolation + ADR-0029 kernel object ownership Jun 1, 2026
…etup app (D7)

Decomposing platform-objects breaks the premise that lets the `setup`
admin app be a static monolith (it hard-references every sys_* object;
its own comment notes it was made static *because* the objects were
centralized, and the runtime-assembling plugin-setup was deleted).
manifest.contributes.menus exists but is consumed nowhere, and there is
no app-extension analog to objectExtensions.

Add D7: setup becomes a base-owned "shell + group slots"; each capability
plugin contributes its nav entries via a declarative navigation
contribution (the UI analog of objectExtensions), merged by group +
priority, each entry gated by the existing requiresObject /
requiredPermissions nav fields (which doubles as the disable mechanism).
Wire it through the migration plan (K1 builds the shell + mechanism; K2
moves each domain's nav entries out as contributions) and record the
schema choice as an open question.

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4
@os-zhuang os-zhuang marked this pull request as ready for review June 1, 2026 03:02
@os-zhuang os-zhuang merged commit 64a1ced into main Jun 1, 2026
11 checks passed
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants