Found while re-testing #7556 on a baseline that contains its fix. #7556 / PR #8015 is genuinely fixed — do not revert it.GET /meta/object/:name now serves all three account.extension.ts overlay fields, ?layers=true carries them on both code and effective, the edit form renders real controls for them, and a PATCH round-trip persists. Every acceptance clause of platform-core.metadata-registry-serving passes. This is a smaller, adjacent inconsistency the same fold introduces.
Symptom
One object, three metadata reads, two different labels:
| read | label |
|---|
GET /api/v1/meta/object (list) | Account |
GET /api/v1/meta/object/showcase_account | Account |
GET /api/v1/meta/object/showcase_account?layers=true → code.label / effective.label | Account (Success Overlay) |
Reproduced 3/3 on baseline 279ee48a (git merge-base --is-ancestor e3c8ed0f8675 HEAD → in build).
Mechanism
examples/app-showcase/src/data/extensions/account.extension.ts:15 declares a label alongside its additive fields:
exportconstAccountExtension=defineObjectExtension({label: 'Account (Success Overlay)',
...
priority: 210,});mergeObjectDefinitions (packages/objectql/src/registry.ts:137-140) merges fields/validations/indexes additively but overrides the scalars last-writer-wins:
// Override scalar props (last writer wins)if(extension.label!==undefined)merged.label=extension.label;if(extension.pluralLabel!==undefined)merged.pluralLabel=extension.pluralLabel;if(extension.description!==undefined)merged.description=extension.description;
Before #8015 the code layer served the raw MetadataService body straight from the artifact (objects.showcase_account.label = 'Account', 12 fields) and never went through the fold, so the overlay label had nowhere to surface. Now the fold runs on that layer — which is exactly what fixed the fields — and it carries the scalar override with it, while the list and by-name reads still report the base label.
Why the existing pins can't see it
Both agreement pins added with the fix compare field names only — packages/rest/src/meta-object-extension-agreement.test.ts and packages/qa/dogfood/test/showcase-object-extension-meta-read.dogfood.test.ts reconcile via fieldNamesOf. Both are green (6/6 and 5/5 on this baseline). A scalar divergence is structurally outside what they assert.
Reproduction
- Boot the showcase:
node packages/cli/bin/run.js dev -a dist/objectstack.json --seed-admin -d file:/tmp/x.db -p <port> - Sign in as
admin@objectos.ai (Origin: http://localhost:<port>), take the bearer from set-auth-token. GET /api/v1/meta/object/showcase_account → item.label is Account.GET /api/v1/meta/object/showcase_account?layers=true → code.label and effective.label are Account (Success Overlay).
Expected one object, one label, on every read. Actual the layered read disagrees with the other two.
Which side is right is a decision, not a given
Both readings are defensible and the fix should pick one deliberately:
- The overlay label should win everywhere —
defineObjectExtension accepts label, mergeObjectDefinitions documents last-writer-wins, and priority 210 says the extension outranks the base. Then the list and by-name reads are the ones under-reporting. - The base label should win everywhere — an additive overlay arguably has no business renaming the object it extends, and the showcase's
(Success Overlay) suffix is a fixture affordance, not a product intent. Then the fold should not carry scalars, or defineObjectExtension should refuse a label.
Whichever way it goes, the gate that keeps it from drifting again is extending the two agreement pins past fieldNamesOf to the scalar triple (label, pluralLabel, description) across all three reads.
Scope
Extension-independent objects are unaffected: across all 96 registered objects, no object other than showcase_account gains an extension field, and showcase_task (unextended) serves 24 fields with none of the overlay fields. The fold is also not double-applying — validations stayed at 5 with zero duplicates across three consecutive reads, so the non-idempotency PR #8015 warns about is not firing here.
Source
Extracted from the checklist retest of platform-core.metadata-registry-serving (framework 279ee48a, console 6314e87f2d49).
Found while re-testing #7556 on a baseline that contains its fix. #7556 / PR #8015 is genuinely fixed — do not revert it.
GET /meta/object/:namenow serves all threeaccount.extension.tsoverlay fields,?layers=truecarries them on bothcodeandeffective, the edit form renders real controls for them, and aPATCHround-trip persists. Every acceptance clause ofplatform-core.metadata-registry-servingpasses. This is a smaller, adjacent inconsistency the same fold introduces.Symptom
One object, three metadata reads, two different labels:
labelGET /api/v1/meta/object(list)AccountGET /api/v1/meta/object/showcase_accountAccountGET /api/v1/meta/object/showcase_account?layers=true→code.label/effective.labelAccount (Success Overlay)Reproduced 3/3 on baseline
279ee48a(git merge-base --is-ancestor e3c8ed0f8675 HEAD→ in build).Mechanism
examples/app-showcase/src/data/extensions/account.extension.ts:15declares a label alongside its additive fields:mergeObjectDefinitions(packages/objectql/src/registry.ts:137-140) merges fields/validations/indexes additively but overrides the scalars last-writer-wins:Before #8015 the
codelayer served the rawMetadataServicebody straight from the artifact (objects.showcase_account.label = 'Account', 12 fields) and never went through the fold, so the overlay label had nowhere to surface. Now the fold runs on that layer — which is exactly what fixed the fields — and it carries the scalar override with it, while the list and by-name reads still report the base label.Why the existing pins can't see it
Both agreement pins added with the fix compare field names only —
packages/rest/src/meta-object-extension-agreement.test.tsandpackages/qa/dogfood/test/showcase-object-extension-meta-read.dogfood.test.tsreconcile viafieldNamesOf. Both are green (6/6 and 5/5 on this baseline). A scalar divergence is structurally outside what they assert.Reproduction
node packages/cli/bin/run.js dev -a dist/objectstack.json --seed-admin -d file:/tmp/x.db -p <port>admin@objectos.ai(Origin: http://localhost:<port>), take the bearer fromset-auth-token.GET /api/v1/meta/object/showcase_account→item.labelisAccount.GET /api/v1/meta/object/showcase_account?layers=true→code.labelandeffective.labelareAccount (Success Overlay).Expected one object, one label, on every read. Actual the layered read disagrees with the other two.
Which side is right is a decision, not a given
Both readings are defensible and the fix should pick one deliberately:
defineObjectExtensionacceptslabel,mergeObjectDefinitionsdocuments last-writer-wins, and priority 210 says the extension outranks the base. Then the list and by-name reads are the ones under-reporting.(Success Overlay)suffix is a fixture affordance, not a product intent. Then the fold should not carry scalars, ordefineObjectExtensionshould refuse alabel.Whichever way it goes, the gate that keeps it from drifting again is extending the two agreement pins past
fieldNamesOfto the scalar triple (label,pluralLabel,description) across all three reads.Scope
Extension-independent objects are unaffected: across all 96 registered objects, no object other than
showcase_accountgains an extension field, andshowcase_task(unextended) serves 24 fields with none of the overlay fields. The fold is also not double-applying —validationsstayed at 5 with zero duplicates across three consecutive reads, so the non-idempotency PR #8015 warns about is not firing here.Source
Extracted from the checklist retest of
platform-core.metadata-registry-serving(framework279ee48a, console6314e87f2d49).