gate fixes: frontmatter elements keep their id through Upload/Copy

readElement returns id OUTSIDE .fields; the Phase B guards in Export and Copy
returned parsed.fields bare, so on a fully-migrated world Upload World would
silently skip EVERY element (the sweep's if-no-id-continue) while reporting
success, and Copy World would emit id-less JSON that breaks paste dedupe.
Both guards now return { id, ...fields }. Smoke step 17 strengthened to check
the uploaded count and spot-check ids in copied JSON, so this class stays dead
at the user layer too.

Gate: tsc clean, build clean, npm test 21/21 (orchestrator hands).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Titus 2026-07-16 13:54:47 +02:00
parent c11db83d57
commit 4b056022eb
3 changed files with 15 additions and 4 deletions

View file

@ -142,7 +142,12 @@ export class CopyWorldCommand {
let parsedElement: Record<string, any>;
if (!isSpanFormat(fileContent) && file instanceof TFile) {
const parsed = await readElement(this.app, file);
parsedElement = parsed ? parsed.fields : await this.parseTemplate(fileContent, worldName);
// id rides OUTSIDE parsed.fields — without it the copied
// JSON loses element identity and paste-dedupe breaks
// (gate finding, 2026-07-16).
parsedElement = parsed
? { id: parsed.id, ...parsed.fields }
: await this.parseTemplate(fileContent, worldName);
} else {
parsedElement = await this.parseTemplate(fileContent, worldName);
}

View file

@ -212,7 +212,10 @@ export class ExportWorldCommand {
const fileContent = await fs.read(file.path);
if (!isSpanFormat(fileContent) && file instanceof TFile) {
const parsed = await readElement(this.app, file);
if (parsed) return parsed.fields;
// id rides OUTSIDE parsed.fields — without it the upload
// sweep's `if (!id) continue` silently skips every
// frontmatter element (gate finding, 2026-07-16).
if (parsed) return { id: parsed.id, ...parsed.fields };
}
return await this.parseTemplate(fileContent, worldFolder);
}));

View file

@ -84,8 +84,11 @@ a pre-2.4 note).
17. In a world with BOTH migrated (frontmatter) and un-migrated (span) notes:
- [ ] **Upload World** completes and pushes both correctly (frontmatter via the
reader, span via the legacy parser).
- [ ] **Copy World to Clipboard** completes without error.
reader, span via the legacy parser). **Check the count in the upload
notice matches your element count** — a silent skip looks like success
(this exact class was caught and fixed at the gate; verify it stays dead).
- [ ] **Copy World to Clipboard** completes; paste the clipboard into a text
editor and spot-check elements carry their `id`.
Known limitation to eyeball, not a blocker: **Validate World** is still a span-format
linter. Run on a migrated (frontmatter) world it will report false "missing Id / Name"