refactor(mcp): дедупликация конвертер-смежных хелперов (node-ops форк, footnote-*, parse-node-arg) #429
Reference in New Issue
Block a user
Delete Branch "refactor/414-dedup-node-ops"
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?
Summary
Дедупликация четырёх конвертер-смежных форков (рефактор; поведение тулов НЕ меняется, −985 строк нетто):
packages/mcp/src/lib/node-ops.ts(~960 строк) ↔ пакетная копия сведены в ОДНУ в@docmost/prosemirror-markdown. Живая mcp-версия — строгое надмножество замороженного #293-seed'а пакета (сверено по git-истории: seedd6d7dd82, mcp получил 3 позднейших коммита —assertUnambiguousMatch/findAnchorTextIndex+strip-fallback; ни одной уникальной пакет-side правки не потеряно). Новая пакет-копия байт-в-байт идентична прежней mcp-копии (diff= 0). Barrel-экспорт полной поверхности;client.ts/page-search.ts/transforms.ts/collaboration.tsимпортируют из пакета; тесты переехали.[^id]:диагностика): сведены к ОДНОМУ fence-aware предупреждению («reference-style footnotes → use^[...]», полезно для класса #410); footnote-lex удалён; footnote-analyze ~210→64 строки.footnoteContentKey/makeFootnoteDefinition/generateFootnoteIdперенесены в пакетныйfootnote.ts(одна реализация конвенции).@docmost/prosemirror-markdown(девиация от ТЗ: сервер CommonJS не может импортировать ESM-only@docmost/mcp, но нативно импортирует пакет — #345); обе копии удалены, консьюмеры перенаправлены.canonicalizeFootnotes/ ENFORCEMENT RULE #228 иcomment-anchor/json-edit/text-normalize(mcp) НЕ тронуты. API-поверхность node-ops оставлена чистой для #409 (findInvalidNode) и #413 (splice-варианты).closes #414
Осознанная остаточная дубликация (нит внутреннего ревью, LOW)
node-ops тянет
stripInlineMarkdown; пакет не может импортировать@docmost/mcp(цикл), а mcp-версияtext-normalize.ts— домен #408. Поэтому в пакет добавлена локальнаяtext-normalize.tsтолько с этим примитивом (байт-в-байт == mcp-версии; заголовок документирует дубликацию + источник истины). CI-guard'а против дрейфа пока нет — уйдёт при завершении #408. Флажу для решения; если хочешь, добавлю guard-тест паритета.How verified
Собрано с временным перенаправлением
@docmost/*симлинков на этот worktree (кросс-пакетное разрешение — главный риск), симлинки восстановлены после:pnpm --filter @docmost/prosemirror-markdown build+pnpm --filter @docmost/mcp build→ tsc чисто (mcp резолвит перенесённый node-ops из пакета — ключевое подтверждение проводки).node --test484 pass, serverparse-node-arg5/5.git diffтестов: только пути импорта (0 правок ассертов), кроме 3 авторизованных footnote-диагностических тестов.Внутренний цикл: coder → внутреннее ревреreview (byte-identity/superset по git, отсутствие цикла, footnote-редукция fence-aware, parse-node-arg-девиация валидна) → APPROVE WITH SUGGESTIONS (низкий нит про drift-guard).
Checklist
Ревью — #429
refactor(mcp): дедупликация конвертер-смежных хелперов (node-ops форк, footnote-*, parse-node-arg)Вердикт: PASS ✅
Полный веер из 9 аспектов по всему диффу PR (merge-base
d90c3b8b..4b2af3d3, −985 строк нетто). Гейт — clean-room с чистой пересборкой (/home/claude/gm-ci, чтобы stalebuild/не маскировал перенос): пересобрал@docmost/prosemirror-markdownс нуля → vitest 718 passed (39 файлов); собрал mcp (tsc 0) → node --test 485/485; server tsc — 0. Всё зелёное.Паритет переноса — проверено (клик)
Рефактор выносит дублированные хелперы из
packages/mcp/src/lib/*в общийpackages/prosemirror-markdown/src/lib/*и переимпортирует; направление зависимостей mcp → prosemirror-markdown, цикла нет.node-ops.ts(все экспорты +assertUnambiguousMatch/findAnchorTextIndex),parse-node-arg.ts(тело идентично, комментарий расширён; играет ESM/CJS-мост — сервер нативно тянет pm, не ESM-mcp), footnote-authoring хелперы (footnoteContentKey/makeFootnoteDefinition/generateFootnoteId),stripInlineMarkdown→text-normalize.ts.stripBalancedWrappersнамеренно оставлен в mcp (без цикла)./^(\s*)({3,}|~{3,})/читалmatch[2][0]; новый/^\s*({3,}|~{3,})/читаетmatch[1][0]. Ведущая(\s*)-группа убрана, маркер стал группой 1 — чтение согласовано, toggle открытие/закрытие идентичен. Проверил напрямую.sanitizeForYjs/findUnstorableAttr), server-tools (parseNodeArg) — все на@docmost/prosemirror-markdown, висячих ссылок на удалённые локальные хелперы нет.node-ops.test.ts/-extra+ mcp интеграция), parse-node-arg, footnote-canonicalize — все тесты на месте/переуказаны на пакет.Намеренное изменение поведения (не перенос) —
footnote-analyze: сужен сanalyzeFootnotes()(4 класса диагностик: dangling/duplicate/empty/in-table) до одного advisoryhasLegacyFootnoteDefinition(). Обоснование #414: со #293 STEP 5 legacy[^id]:инертен на импорте (остаётся литеральным текстом), поэтому детальные диагностики больше не описывают то, что строит импортёр — одно предупреждение «используй inline^[body]». Проверил: публичная формаfootnoteWarningsField()→{ footnoteWarnings?: string[] }СОХРАНЕНА, внешнего потребителя старых диагностик нет, основной кейс (автор пишет[^id]:-определения) по-прежнему предупреждается, документ не меняется, тесты переписаны и зелёные.⛔ DROP — кодеру НЕ делать (лог калибровки)
[below-threshold]low/high[conventions] типInsertOptionsне реэкспортится из barrelprosemirror-markdown/src/lib/index.ts(реэкспортится только функцияinsertNodeRelative) — сборка зелёная, текущие вызовы выводят тип из инлайн-литерала, потребителю имя типа не нужно; чистая мелочь удобства. Опц.:export type { InsertOptions } from "./node-ops.js";.[by-design]low/med[regressions] суженный footnote-advisory больше НЕ срабатывает на «висячей» ссылке[^id]без строки-определения[^id]:(старый warned про dangling/table-refs). Это осознанное решение #414 (advisory ключится на синтаксис определения); вернуть детекцию ссылок — расширять scope против #414. Отмечено для видимости, не правка.DO/ESCALATE нет. Паритет переноса подтверждён (байт-идентичность + мутационно-зелёный clean-room гейт), единственное поведенческое изменение (footnote-analyze) — намеренное #414 с сохранённой публичной формой. Approve.
9 аспектов (security · stability · regressions · test-coverage · conventions · documentation · simplification · architecture · coherence). Regressions/parity гонялся дольше прочих (line-by-line old-vs-new перенесённых хелперов + FENCE_RE-группа + footnote-сужение проверены отдельно мной). Escalate нет.