Skip to content

fix(metadata-protocol): duplicatePackage no longer drops a locale from a two-tier i18n bundle (#7932) - #7993

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-7932-duplicate-package-locale-key
Aug 12, 2026
Merged

fix(metadata-protocol): duplicatePackage no longer drops a locale from a two-tier i18n bundle (#7932)#7993
huangyiirene merged 2 commits into
mainfrom
claude/issue-7932-duplicate-package-locale-key

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#7932

⚠️PR opened by the domain:metadata PM seat on the implementing dev's behalf. The dev pushed claude/issue-7932-duplicate-package-locale-key (fix + a 413-line test file, origin/main already merged in) but had no GitHub write access in its container — three of four sibling dispatches hit the same thing this shift. Body below is assembled from the dev's own changeset; its full report will be appended as a comment when it hands the text over. Flag any gap to me, not to them.

The defect

Duplicating a package as an org-scoped caller copied one locale of a two-locale email-template customization and reported success: true.

duplicatePackage's source-row scan deduplicates the scanned sys_metadata rows with a NUL-separated key built from the row's type and name only, keeping the org-scoped row over the env-wide one. That key is (type, name) with no discriminator, so for a type whose identity the spec declares as a pair, it collapses two rows that are two different things. EmailTemplateDefinitionSchema declares exactly such a type: multiple rows with the same name but different locale form an i18n bundle, resolved by (name, locale).

Why the exposure is narrow — and why it is nevertheless real

sys_metadata's overlay uniqueness is idx_sys_metadata_overlay_active = (type, name, organization_id, package_id), and the table has no locale column — an email_template's locale lives in the metadata JSON body. So:

Registry-shipped (code-authored) members are unaffected — this scan reads sys_metadata overlays only, so the exposure is limited to templates customized at both scopes.

The change

The dedup key now appends the canonical-normalized discriminator when the type declares one, and nothing otherwise — the same shape #7774 / PR #7931 (7372d46) gave metaItemKey and mergePackageAwareOverlay for the GET /meta/<type> list. Both tools were already in the file: itemDiscriminator from @objectstack/metadata-core and the local storedRowDiscriminator, which skips the JSON parse entirely for an undiscriminated type.

email_template is the only type in ITEM_KEY_DISCRIMINATORS today, so every other type's key is byte-identical to what it was before — this change's blast radius is provable rather than argued, which was the review criterion.

Precedence is unchanged wherever it was ever meaningful

  • an org row still overrides the env-wide row of the same bundle member;
  • a member declaring no locale is still keyed as the canonical (en-US) member, so the bundle-blind and bundle-aware answers continue to agree for a single-member "bundle";
  • only rows that were never the same thing stay separate;
  • the no-organizationId door never ran this dedup and is untouched.

Coverage

This path had no test coverage at all before this PR — git grep of the dedup key returned only protocol.ts itself. The new protocol.duplicate-package-bundle-key.test.ts is 413 lines, built to the standard PR #7931 met for its sibling: both directions, plus per-arm reverse-verification attributing which cases go red under which half of the revert.


Generated by Claude Code

…e i18n bundle discriminator (#7932)
`duplicatePackage`'s source-row scan deduped the scanned `sys_metadata` rows
with a NUL-separated key built from the row's `type` and `name` only, keeping
the org-scoped row over the env-wide one. That key is `(type, name)` with no
discriminator, so for a type whose identity the spec declares as a pair it
collapsed two rows that are two different things.
`EmailTemplateDefinitionSchema` declares such a type: rows sharing a `name` but
differing in `locale` form an i18n bundle resolved by `(name, locale)`. Within
one org the collapse cannot happen — overlay uniqueness is
`(type, name, organization_id, package_id)` and the table has no locale column
— but across the env-wide and org tiers it can, and this scan is where the two
tiers meet. An env-wide `auth.welcome` in `en-US` plus an org `auth.welcome` in
`zh-CN` are two members of one bundle; the duplicate shipped one of them and
reported success.
Append the canonical-normalized discriminator when the type declares one and
nothing otherwise — the shape #7774 gave `metaItemKey` — so every
undiscriminated type keeps a byte-identical two-component key. Precedence is
unchanged where it was ever meaningful: an org row still overrides the env-wide
row of the same member.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01553EtKW75AGxDcCMyqkSEs
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 12, 2026 9:49am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/metadata-protocol)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/metadata-protocol)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/metadata-protocol)

1 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/v9.mdx(via @objectstack/metadata-protocol)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 12, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review August 12, 2026 10:21
@huangyiirene
huangyiirene added this pull request to the merge queueAug 12, 2026
Merged via the queue into main with commit 40e8653Aug 12, 2026
26 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-7932-duplicate-package-locale-key branch August 12, 2026 10:40
@huangyiirene
huangyiirene restored the claude/issue-7932-duplicate-package-locale-key branch August 12, 2026 11:02
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

duplicatePackage's env-wide ⊕ org row scan dedups by (type, name) — an i18n bundle's two locales collapse to one

2 participants

@huangyiirene@claude