fix(mcp): make i18n:false delete and meta cleanup safe#86
Merged
Conversation
Two bugs on i18n:false collections, plus a source-hygiene fix. content_delete: passing a non-default `locale` to an i18n:false model was destructive. The locale mapped onto data.json and the default-locale meta, so it emptied the shared content and deleted the wrong meta while the named locale's stray meta survived — the opposite of the request. planContentDelete and the legacy deleteContent now reject a locale-scoped delete on an i18n:false model with a clear error (omit locale; content is locale-agnostic). validate fix:true: the "Meta layout mismatch" warning had no remediation, so fixed stayed 0. checkStrayNonI18nMeta now consolidates deterministically — prune redundant strays when the default-locale meta is authoritative, migrate a lone stray to the default path when no default exists, and leave ambiguous multi-stray cases for a human. It runs before the orphan-content pass and gates that pass's draft fabrication, so a real published record is never replaced by a fabricated draft and then deleted on a later run. validator/project.ts held two raw NUL bytes (a Map-key separator) that made tools classify the file as binary and broke grep/diff. Replaced with a unicode escape: identical runtime, plain-text source. Tests: planContentDelete guard (non-i18n reject / i18n allow), legacy delete guard, and disk-backed validate fix:true stray-meta cases (prune, migrate, ambiguous no-op, idempotency). Docs, rules, and skills updated.
✅ Deploy Preview for contentrain-ai ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Two bugs a project hit while cleaning up an
i18n: falsecollection (reported against@contentrain/[email protected]), plus a source-hygiene fix found along the way.Issue 1 —
content_delete(locale=<non-default>)oni18n: falsewas destructiveOn a non-i18n model, content is locale-agnostic (
data.json) with a single meta record pinned to the default locale. Passing a non-defaultlocalesilently mapped ontodata.jsonand the default-locale meta, so the call:data.json(deleted the real, published entry),tr.json),en.json) untouched,and reported a
files_removedpath (.../en.json#<id>) that never existed — the opposite of the request.Fix:
planContentDelete(and the legacydeleteContent) now reject a locale-scoped delete on ani18n: falsemodel with a clear error. Omitlocaleto delete the entry.Issue 2 —
validate fix:truewould not remove the stray meta it warns aboutThe "Meta layout mismatch" warning had no remediation path, so
fixedstayed0.checkStrayNonI18nMetanow consolidates deterministically and never decides a status:Subtle interaction handled: the orphan-content pass fabricates a draft default-locale meta for content lacking meta at the default locale. Consolidation now runs before that pass and gates its fabrication while a stray is unresolved — otherwise a lone published stray would be shadowed by a fabricated draft and then deleted on a later run. The remediation is idempotent.
Issue 3 (found while investigating) — raw NUL bytes in
validator/project.tsThe document validator used two literal NUL bytes as a
Map-key separator, which madefile/grep/diff classify the source as binary (and silently broke the first grep of this investigation). Replaced with a unicode escape — identical at runtime, plain-text source.Tests & gates
planContentDeleteguard (non-i18n reject / i18n allow / locale-free allow), legacydeleteContentguard, disk-backedvalidate fix:truestray-meta cases (prune, migrate, ambiguous no-op, idempotency).tsc --noEmit: 0 errors ·oxlinton all changed files: 0 warnings.contentrain_content_deletetool test passes in isolation (17/17).branch-lifecycle,bulk,identity, …) time out under load from serial-git slowness, unrelated to this change (bulk delete_entriespasses no locale, so the guard never fires).Docs, rules, and skills updated (
docs/packages/mcp.md,rules/shared/mcp-usage.md,contentrain-validate-fixskill + workflow,mcp-tools.md, tool description string).