Repository navigation
Description
Filed by the domain:metadata PM seat on behalf of #7556's dev, which measured this while fixing that card but could not file it — the objectstack-ai org has no Claude GitHub App connection, so cloud dev containers get 403 on the GitHub API. Text is the dev's.
Measured, not inferred; distinct from #7556 and not caused by its fix (PR #8015).
The defect
When a sys_metadata overlay row exists for an object — e.g. an admin customises the object's label in Studio — the protocol's by-name read replaces the resolved body with the raw stored row, and the list read does the same via mergePackageAwareOverlay, which replaces wholesale rather than merging fields.
objectExtensions field.
Measured
One extend contributor (loyalty_tier, linkedin_url, csat_score), one env-wide overlay row, no metadata service:
byName fields: name, industry + system columns — no extension fields
listed fields: identical — no extension fields
layers.code: has all 3 (correct, ADR-0029 D9.6)
layers.overlay: the raw row (correct)
layers.effective: no extension fields
⇒ A single ?layers=true response reports a code layer that has the fields and an effective layer that does not, with no customisation explaining the difference.
Why this looks wrong
ADR-0029 D9.2 states the base layer is overlay ?? own and that extenders are then folded on — which is exactly what SchemaRegistry.resolveObject does for overlays it knows about. The protocol's sys_metadata path never folds.
The practical consequence mirrors #7556: an admin who customises an object's label silently removes three extension-contributed fields from every writable form, while the data API keeps accepting and persisting them.
Suggested shape — ⚠️ and a trap in it
Deliberately not implemented in #7556: it widens blast radius beyond that card and needs its own reverse-verification.
Treat the overlay row as the base layer and fold extenders onto it, matching D9.2, rather than serving it as the resolved schema.
mergeObjectDefinitions concatenates validations and indexes, so the fold is NOT idempotent. Any fix must establish that the stored row has not already been through it — a row saved from a previously-merged read may already contain extension fields, and folding again would duplicate validations and indexes. This is the part most likely to be missed.
Reproduction
Register an object own plus an extend contributor, seed one sys_metadata row (type:'object', state:'active', organization_id:null) whose body is the un-extended object, then compare getMetaItem / getMetaItems / getMetaItemLayered.
Activity
fix(metadata-protocol): an object's overlay row is a base layer, not …
test(rest,dogfood): enumerate the object-extension fold's property cl…
fix(objectql): a tenant's rename beats a package extension's scalar i…