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

vercel Bot commented Jun 13, 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 13, 2026 10:08pm

Request Review

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/m tests tooling

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