Filed by the PM on behalf of the #8037 dev agent, which measured this but could not file it — GitHub API 403 for its whole session. It invented no issue number anywhere in its commit; this is the first number attached to this defect.
⚠️This supersedes #8037's framing.#8037 asked for a label leak "in the object-extension fold". The fold is correct on all six properties. The defect is one layer down, in i18n resolution — and its more severe half has nothing to do with extensions at all.
The mechanism
translateObject (packages/spec/src/system/i18n-resolver.ts) resolves label / pluralLabel / description as catalog ?? document. The catalog is keyed by object name and consulted first, so it overwrites whatever the resolution produced.
GET /meta/object (list) and GET /meta/object/:name are translated. ?layers=true is deliberately untranslated — its own comment says so: "Not translated and not cached, both deliberately: this is a diagnostic." That is the entire reason #8037 presented as "onto ?layers=true only".
Traced live, showing the fold is uniform and the loss is downstream:
[BYNAME] fold(showcase_account) baseLabel="Account" -> outLabel="Account (Success Overlay)"
[BYNAME] served label = "Account" ← discarded downstream
[LAYERS] fold(showcase_account) baseLabel="Account" -> outLabel="Account (Success Overlay)"
[LAYERS] served code="Account (Success Overlay)" effective="Account (Success Overlay)"
⛔ The severe half — a tenant's rename is silently discarded
The extension-override case (#8037) is the milder one. Because the catalog is keyed by object name and wins unconditionally, it also defeats the tenant's own customisation. Measured on a deployment-shaped boot, renaming via the ordinary Studio round-trip (PUT /meta/object/showcase_account → 200 OK):
| Surface | Label |
|---|
GET /meta/object (list) | Account |
GET /meta/object/:name | Account |
layers.overlay | Customer ← what the tenant actually saved |
layers.effective | Account (Success Overlay) |
The tenant's rename reaches neither read that a writable form derives from. One object serves three different labels, and the only surface showing the saved value is the one explicitly documented as a diagnostic.
⭐ This is another instance of this lane's recurring shape — a 200 that means nothing happened (now the sixth: #7893, #8018, reassignOrphanedMetadata, #7823, #7736, this).
Scope: three scalars, one mechanism
From the enumeration landed in 3a27c46edb (PR #8258) — mergeObjectDefinitions touches six keys in three merge kinds, and the fold has zero divergences across all of them:
| Property | Merge kind | Reads agree? |
|---|
fields | key-keyed spread (idempotent) | ✅ |
validations | concatenated (not idempotent, #8027) | ✅ |
indexes | concatenated (not idempotent, #8027) | ✅ |
label | scalar, last-writer-wins | ❌ diverges |
pluralLabel | scalar, last-writer-wins | ⚠️ same exposure, not triggered on main |
description | scalar, last-writer-wins | ⚠️ same exposure, not triggered |
All three scalars share one mechanism; label is simply the only one the shipped showcase catalog currently triggers (showcase_account has a catalog label, no catalog description).
⛔ Why the obvious fix is a regression in disguise
The dev reverse-verified the card's proposed "easy half" (make the fold drop scalars) as arm B, and it is the load-bearing row:
| Case | Arm A (revert #8045) | Arm B (card's "fold drops scalars") | Arm C (revert #7556 fold) |
|---|
| New sweep (19 cases) | 🔴 3 | 🔴 1 (anti-vacuity only) | 🔴 7 |
| #8015 agreement pin | 🟢 | 🟢 | 🔴 2 |
| #8045 fold pin | 🔴 2 | 🟢 | 🔴 4 |
Arm B is invisible to both existing pins — caught only by the new anti-vacuity case. It makes the symptom disappear by deleting a documented ObjectExtensionSchema feature, and leaves the tenant-rename defect completely untouched. ⛔ That is why no behavioural change was shipped.
The ruling needed
Should the catalog lose to an explicit override — which requires carrying scalar provenance through the fold, so "this value was explicitly set" is distinguishable from "this value is the packaged default" — or should the untranslated diagnostic be reconciled with the translated reads?
Both candidate directions change behaviour well outside #8037's declared region, which is why this is a decision card rather than a dispatch.
Reproduction
Pinned in packages/qa/dogfood/test/showcase-object-extension-scalar-divergence.dogfood.test.ts (landed in 3a27c46edb): two it.fails cases that turn red when this is fixed. ⛔ Do not "repair" them to green — inverting them is the signal the fix worked.
Related
#8037 (the card this came from; premise corrected) · PR #8258 = 3a27c46edb (the enumeration + repro pins) · #7556/#8015 · #8027/#8045 · #8268 (nameField, a scalar in the same last-writer-wins class — ⚠️ read together) · ADR-0029 D9.2
Filed by the PM on behalf of the #8037 dev agent, which measured this but could not file it — GitHub API 403 for its whole session. It invented no issue number anywhere in its commit; this is the first number attached to this defect.
labelleak "in the object-extension fold". The fold is correct on all six properties. The defect is one layer down, in i18n resolution — and its more severe half has nothing to do with extensions at all.The mechanism
translateObject(packages/spec/src/system/i18n-resolver.ts) resolveslabel/pluralLabel/descriptionascatalog ?? document. The catalog is keyed by object name and consulted first, so it overwrites whatever the resolution produced.GET /meta/object(list) andGET /meta/object/:nameare translated.?layers=trueis deliberately untranslated — its own comment says so: "Not translated and not cached, both deliberately: this is a diagnostic." That is the entire reason #8037 presented as "onto?layers=trueonly".Traced live, showing the fold is uniform and the loss is downstream:
⛔ The severe half — a tenant's rename is silently discarded
The extension-override case (#8037) is the milder one. Because the catalog is keyed by object name and wins unconditionally, it also defeats the tenant's own customisation. Measured on a deployment-shaped boot, renaming via the ordinary Studio round-trip (
PUT /meta/object/showcase_account→ 200 OK):GET /meta/object(list)AccountGET /meta/object/:nameAccountlayers.overlayCustomer← what the tenant actually savedlayers.effectiveAccount (Success Overlay)The tenant's rename reaches neither read that a writable form derives from. One object serves three different labels, and the only surface showing the saved value is the one explicitly documented as a diagnostic.
⭐ This is another instance of this lane's recurring shape — a 200 that means nothing happened (now the sixth: #7893, #8018,
reassignOrphanedMetadata, #7823, #7736, this).Scope: three scalars, one mechanism
From the enumeration landed in
3a27c46edb(PR #8258) —mergeObjectDefinitionstouches six keys in three merge kinds, and the fold has zero divergences across all of them:fieldsvalidationsindexeslabelpluralLabelmaindescriptionAll three scalars share one mechanism;
labelis simply the only one the shipped showcase catalog currently triggers (showcase_accounthas a cataloglabel, no catalogdescription).⛔ Why the obvious fix is a regression in disguise
The dev reverse-verified the card's proposed "easy half" (make the fold drop scalars) as arm B, and it is the load-bearing row:
Arm B is invisible to both existing pins — caught only by the new anti-vacuity case. It makes the symptom disappear by deleting a documented
ObjectExtensionSchemafeature, and leaves the tenant-rename defect completely untouched. ⛔ That is why no behavioural change was shipped.The ruling needed
Should the catalog lose to an explicit override — which requires carrying scalar provenance through the fold, so "this value was explicitly set" is distinguishable from "this value is the packaged default" — or should the untranslated diagnostic be reconciled with the translated reads?
Both candidate directions change behaviour well outside #8037's declared region, which is why this is a decision card rather than a dispatch.
Reproduction
Pinned in
packages/qa/dogfood/test/showcase-object-extension-scalar-divergence.dogfood.test.ts(landed in3a27c46edb): twoit.failscases that turn red when this is fixed. ⛔ Do not "repair" them to green — inverting them is the signal the fix worked.Related
#8037 (the card this came from; premise corrected) · PR #8258 =⚠️ read together) · ADR-0029 D9.2
3a27c46edb(the enumeration + repro pins) · #7556/#8015 · #8027/#8045 · #8268 (nameField, a scalar in the same last-writer-wins class —