fix: preserve source locale when duplicating content - #2303
Conversation
duplicate() built the copy's slug against the source row's locale but inserted the row without one, so create()'s fallback landed every copy in 'en'. On locale-scoped admin lists the copy silently vanished, and the mis-scoped slug-uniqueness check could trip the UNIQUE(slug, locale) constraint when the generated slug already existed in 'en'. Pass the source locale through to create(). Duplicates keep their own fresh translation group — a copy is a new entry, not a translation — which create() already does by default; a test now locks that in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: de95137 The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
This is a narrowly-scoped, correct bug fix. ContentRepository.duplicate() already generated a unique slug scoped to original.locale, but it called create() without a locale, so SQLite's default en landed every copy in the wrong locale (and could cause a (slug, locale) unique-constraint collision when the generated slug existed in en). Passing locale: original.locale ?? undefined makes the slug check and the insert consistent.
I read the diff, the full repository implementation and input types, the duplicate route/handler, and the existing duplicate tests. The change:
- Uses the existing
CreateContentInput.localefield, so no type or API surface changes are needed. - Correctly handles
nulllocale values via?? undefined(the input type doesn't acceptnull). - Keeps the fresh-translation-group behavior, which
create()already enforces. - Adds three focused integration tests that reproduce the reported symptoms: missing copy in source locale, fresh translation group, and cross-locale slug collision.
No AGENTS.md conventions are violated: there are no new logged-out queries, no raw SQL interpolation, no UI string changes, the changeset is present and written for users, and the tests exercise observable behavior rather than asserting implementation details back at themselves. The code is clean.
6fae716 to
de95137
Compare
What does this PR do?
Fixes
ContentRepository.duplicate()dropping the source entry's locale. It generated the copy's unique slug scoped to the source locale, then calledcreate()without alocale, so the fallback landed every copy inenregardless of the source row's locale.Two observable consequences on non-English content, both hit in production on an es-canonical site (emdash 0.31.1):
enand is invisible in the list the editor is looking at. Stray-copyrows accumulate (18 on the audited production DB).en, duplicating could crash with aUNIQUE(slug, locale)constraint violation when the generated slug already existed inen.The fix passes
locale: original.locale ?? undefinedthrough tocreate(), which also makes the slug-uniqueness scoping consistent with the inserted row. Duplicates keep getting a fresh translation group (a duplicate is a new entry, not a translation) — that was alreadycreate()'s default behavior, and a new test locks it in so it can't regress.Found during a measured database audit of a production deployment (the Macabro festival site).
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain. — n/a: no admin UI strings changedAI-generated code disclosure
Screenshots / test output
Failing first (on
main, before the fix):After the fix:
🤖 Generated with Claude Code