[refactor][git-sync] Три копии схемы/конвертера (editor-ext / mcp / git-sync) — устранить дрейф, порождающий потерю данных #293
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Проблема
Схема документа и конвертер ProseMirror ↔ Markdown существуют в проекте в трёх рукописно синхронизируемых копиях:
packages/editor-extpackages/mcp/src/libdocmost-schema.ts~1287,markdown-converter.ts~903packages/git-sync/src/libdocmost-schema.ts~1532,markdown-converter.ts~1065mcpиgit-sync— это разошедшиеся форки одного и того же конвертера, оба вручную зеркалят каноническую схему изeditor-ext. Любое изменение схемы редактора требует трёх синхронных правок. Когда одна из копий отстаёт — данные молча теряются на каждом синке.Почему это важно (а не «просто дубль кода»)
Это уже сработавший и повторяющийся класс потери данных. История только этого одного эпика (#119):
7abce935— потеря spoiler-марка (#259) и подписи картинки (#221): git-sync-копия отстала от editor-ext;32e99c6e— потеря атрибутаopenу details;fe4adf23— схлопывание типа callout.И ещё два свежих бага того же класса, найденных при ревью PR #119 (подтверждены живым round-trip repro):
<div align="center">, но не парсится обратно → теряется первым же stabilize-проходом (packages/git-sync/src/lib/docmost-schema.ts:236-243);columnsи multi-block ячейки деградируют в литеральный текст (packages/git-sync/src/lib/markdown-converter.ts:963-982,:443-454).Почему текущие гейты не ловят дрейф
packages/git-sync/test/schema-editor-ext-contract.test.ts— сверка только на уровне имён узлов: новый атрибут существующего узла (ровно класс image-caption / alignment) проходит мимо;packages/git-sync/test/schema-surface-snapshot.test.ts— пиннит копию git-sync саму против себя, не против editor-ext;apps/server/src/collaboration/git-sync-converter-gate.spec.ts— рукописный корпус фикстур: новая поверхность схемы проверяется, только если кто-то не забыл добавить фикстуру;mcp-копии вообще нет механической связи с editor-ext (об этом прямо сказано в комментарии контракт-теста — отложено).Историческая причина зеркала git-sync (внешний вендоринг из
docmost-sync) уже устранена интернализацией движка (коммит5da12e89): тесты git-sync резолвят@docmost/editor-extуже сегодня (workspace-зависимость + shamefully-hoist).Варианты решения
Вариант A — оставить копии, но сделать гейт механическим на уровне атрибутов (effort: S)
Строить реальную схему из расширений
editor-ext(черезgetSchemaнад тем же набором, что используетapps/server/src/collaboration/collaboration.util.ts) и ассертить попарное совпадение ключей атрибутов каждого узла/марка против каждого зеркала, с закоммиченным allowlist осознанных расхождений. Добавить идентичный тест вpackages/mcp.parseHTML/renderHTML(баг detailsopen) не покрывается сравнением имён/атрибутов.Вариант B — вынести единый headless-конвертер, потребляемый git-sync и mcp (effort: L)
Один пакет (
@docmost/prosemirror-markdownили подобн.) с зеркалом схемы + конвертером md↔ProseMirror, контракт-тестируемый против editor-ext. Изменение схемы становится двумя местами с механическим контрактом между ними вместо трёх ручных зеркал. Это и есть «единое framework-free ядро схемы», уже упомянутое как отложенный план в комментариях тестов и AGENTS.md.Вариант C — git-sync импортирует схему напрямую из editor-ext; mcp оставляет зеркало (effort: M)
Рекомендация
Вариант A сейчас — маленький follow-up, оправданный собственным баг-паттерном этого эпика: механический attribute-level контракт закрывает большинство дрейфа в CI немедленно.
Вариант B — как заявленная цель: он уже наполовину обещан в комментариях кода и AGENTS.md; превратить отложенность из комментария в трекаемую задачу, чтобы не гнила.
Вариант C — полушаг, который я бы пропустил в пользу B.
Связанные
Выделено из ревью PR #119. Форвард-луки, мержу #119 не блокирует — но это самый высоколивереджный структурный долг, который фича оставляет за собой.