Skip to content

The i18n catalog is resolved ahead of the document, so a tenant's own Studio rename never reaches either /meta/object read — and neither do extension scalar overrides #8284

Description

@huangyiirene

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_account200 OK):

SurfaceLabel
GET /meta/object (list)Account
GET /meta/object/:nameAccount
layers.overlayCustomer ← what the tenant actually saved
layers.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) — mergeObjectDefinitions touches six keys in three merge kinds, and the fold has zero divergences across all of them:

PropertyMerge kindReads agree?
fieldskey-keyed spread (idempotent)
validationsconcatenated (not idempotent, #8027)
indexesconcatenated (not idempotent, #8027)
labelscalar, last-writer-winsdiverges
pluralLabelscalar, last-writer-wins⚠️ same exposure, not triggered on main
descriptionscalar, 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:

CaseArm 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions