Skip to content

feat(metadata): package-scoped customization overlays (ADR-0048 #1824) - #1827

Merged
xuyushun441-sys merged 1 commit into
mainfrom
feat/adr-0048-package-scoped-overlays
Jun 13, 2026
Merged

feat(metadata): package-scoped customization overlays (ADR-0048 #1824)#1827
xuyushun441-sys merged 1 commit into
mainfrom
feat/adr-0048-package-scoped-overlays

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Closes#1824.

Problem

A sys_metadata customization overlay was keyed by (type, name, organization_id) — a UNIQUE index allowing one physical row per name per org. So when two installed packages ship an item of the same type/name, customizing one shadowed both, and a package-scoped read fell back to whichever row existed. (This is the env-overlay limitation called out at the end of #1822.)

Fix

Make overlays package-aware end-to-end:

  • Indexidx_sys_metadata_overlay_active / …_draft now include package_id. The runtime migration (ensureOverlayIndex) uses COALESCE(package_id, '') so package-less (global) overlays stay unique among themselves (a plain unique index treats NULLs as distinct). DROP-then-CREATE, idempotent; existing rows migrate safely.
  • WriteSysMetadataRepository.whereFor/put/get scope the upsert to the requested package, so a save bound to package B no longer finds and overwrites package A's same-name overlay. A package-less save targets the global row.
  • ReadgetMetaItem / getMetaItemLayered overlay lookups already prefer the package-scoped row (feat(runtime): package-scoped single-item metadata resolution via ?package= (ADR-0048) #1816/fix(metadata): package-scope the Studio editor read via ?package= (ADR-0048) #1819); the fallback now resolves only the global (package_id IS NULL) overlay, never a different package's row. Package-less readers unchanged (match-any, back-compat).

Frontend counterpart (objectui): the Studio editor's post-save refresh scopes its layered/draft re-read to the same package (the save already binds ?package=).

Verification

Live, real collision (two packages each shipping page/showcase_task_workbench), against the migrated DB:

  • PUT …?package=com.objectstack.studio and PUT …?package=com.example.showcasetwo rows coexist in sys_metadata (the old index would have rejected/overwritten the second).
  • GET …?package=com.objectstack.studio → "STUDIO OVERLAY EDIT"; ?package=com.example.showcase → "SHOWCASE OVERLAY EDIT".
  • GET …?layers=true&package=com.objectstack.studio (Studio editor view) → overlay/effective = "STUDIO OVERLAY EDIT".
  • Index migrated in place: … (type, name, organization_id, COALESCE(package_id, '')) WHERE state = 'active'.

606 objectql tests green (added a per-package write-scoping test; existing ADR-0005 overlay tests updated for the package_id discriminator).

Known follow-up (not this PR)

The unscoped list (GET /meta/:type with no ?package=) still dedupes by bare name, so when two packages both carry an overlay on the same name the list collapses them — the per-package single-item and Studio editor paths are unaffected. This is the same byName-dedup limitation noted in #1822; tracked for the list-dedup-by-name work.

🤖 Generated with Claude Code

Key sys_metadata overlays by (type, name, organization_id, package_id) so two
installed packages shipping the same name each carry their own overlay, instead
of one physically shadowing both.
- Index idx_sys_metadata_overlay_active/_draft include package_id; runtime
migration uses COALESCE(package_id,'') so package-less globals stay unique.
- SysMetadataRepository.whereFor/put/get scope the upsert per package (a save
for B no longer overwrites A's same-name overlay; package-less → global row).
- getMetaItem/getMetaItemLayered overlay fallback resolves only the global
(package_id IS NULL) row on a scoped miss, never another package's row.
Verified live (two packages each shipping page/showcase_task_workbench): two
overlay rows coexist; ?package= single reads + ?layers= editor view each return
their own overlay; index migrated in place. objectql 606 green.
Follow-up: unscoped list still dedupes by bare name (collapses overlay
collisions); per-package single-item + editor paths unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Jun 13, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 13, 2026 10:08pm

Request Review

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0048 follow-up: package-scoped metadata overlays (env-wide overlay can't disambiguate same-name collisions)

2 participants

@xuyushun441-sys@os-zhuang