Skip to content

fix(objectql): apply D7 setup-nav contributions in the protocol serving path (empty Setup menu)#1457

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

fix(objectql): apply D7 setup-nav contributions in the protocol serving path (empty Setup menu)#1457
os-zhuang merged 7 commits into
mainfrom
claude/lowcode-metadata-naming-conflicts-nrnaT

Conversation

@os-zhuang
Copy link
Copy Markdown
Contributor

Problem

The Setup menu is completely empty. After ADR-0029 D7 reduced setup.app.ts to a shell of empty group anchors and moved menu entries into navigation contributions (merged lazily on read), the contributions never reached the rendered app.

Root cause

The merge logic (applyNavContributions) lived only in SchemaRegistry.getApp / getAllApps. But the REST app endpoints don't read through those helpers — they read through protocol.getMetaItems / getMetaItem, which return raw registry items via listItems / getItem. So the served Setup app was the bare shell → every group rendered empty.

Fix

Apply the contribution merge in both protocol read paths for the app/apps types, mirroring getApp/getAllApps:

  • getMetaItems (list) — maps each served app through registry.applyNavContributions.
  • getMetaItem (single-app fetch, GET /meta/app/<name>) — same, after the item is resolved.

The stored app is never mutated (structuredClone), so reads stay idempotent. SchemaRegistry.applyNavContributions is promoted private → public so the protocol can reach the same logic.

Tests

Adds a regression describe block (app navigation contributions (ADR-0029 D7)) in protocol-meta.test.ts covering both getMetaItems({type:'app'}) and getMetaItem({type:'app'}), including the idempotency assertion that the stored shell is not mutated. Full protocol-meta suite green (64 passed).

https://claude.ai/code/session_01Tv6F1Ub6bhCedrx3r8sZM4


Generated by Claude Code

claude added 7 commits June 1, 2026 02:38
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
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
…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
…ta-naming-conflicts-nrnaT

# Conflicts:
#	docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md
The Setup app is a shell of empty group anchors (ADR-0029 D7); menu
entries are injected as navigation contributions and merged lazily on
read. `SchemaRegistry.getApp` / `getAllApps` did the merge, but the REST
app endpoints read through `protocol.getMetaItems` / `getMetaItem`, which
returned the raw shell — so every Setup menu group rendered empty.

Apply the contribution merge in both protocol read paths (list + single)
for the `app`/`apps` types, mirroring `getApp`/`getAllApps`. The stored
app is never mutated (structuredClone), so reads stay idempotent.

`SchemaRegistry.applyNavContributions` is promoted from private to public
so the protocol can reach the same merge logic.

Adds regression tests covering both `getMetaItems({type:'app'})` and
`getMetaItem({type:'app'})`.
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

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 8:08am

Request Review

@os-zhuang os-zhuang marked this pull request as ready for review June 1, 2026 08:33
@os-zhuang os-zhuang merged commit 9a1205f into main Jun 1, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants