feat(footnotes): reuse semantics + import diagnostics (#166) #169
Reference in New Issue
Block a user
Delete Branch "feat/footnote-reuse-and-warnings"
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?
Closes #166 (data-integrity core). Multi-backlink editor UI split to #168.
Проблема
Сноски были строго 1:1: повторная
[^a]ссылка считалась коллизией и переименовывалась вa__2, а ссылка без определения синтезировала собственную пустую сноску — поэтому статья агента с переиспользованными ярлыками давала десятки пустыхkowiki__N.Решение — семантика Pandoc REUSE + диагностика импорта
Reuse (ядро).
resolveCollisions(footnote-sync): повторные ссылки с одним id — это REUSE (записываются один раз в порядке появления, НИКОГДА не переименовываются) → один номер, одно определение. Только дубль-ОПРЕДЕЛЕНИЕ переименовывается детерминированно (deriveFootnoteId) и, не имея своей ссылки, отбрасывается существующей orphan-политикой (first-wins).CollisionPlan.refReidsтеперь всегда пуст (безвредный no-op ниже по течению).extractFootnoteDefinitions(marked) иextractFootnotes(MCP): дубль-определения — FIRST-WINS (первое сохраняется, остальные отбрасываются); маркеры ссылок не переписываются. Удалён переписыватель маркеров и ставший мёртвым зеркальныйderiveFootnoteId+ хелперы в MCP.Диагностика.
analyzeFootnotes()(MCP): fence-aware чистый скан → dangling-ссылки, пустые/дублирующиеся определения, маркеры[^id]в строках таблиц.create_page/update_page/import_page_markdownтеперь возвращаютfootnoteWarnings(только если непусто) — агент видит проблему и чинит разметку; страница всё равно создаётся.Paste-reuse.
footnotePastePluginремапит id только если вставляемый слайс ОПРЕДЕЛЯЕТ конфликтующий id; вставка одиночной ссылки на существующий id сохраняет id (reuse).Тесты
Reuse/first-wins переписаны в
footnote.test,footnote-markdown.test,footnote.marked.orphan.test, MCPfootnotes.test; новыеfootnote-paste.test(editor-ext) иfootnote-analyze.test(MCP). Удалёнderive-id-parity.test.mjs(MCP больше не выводит id; editor-extderiveFootnoteIdсохраняет свой golden-тест). editor-ext 128, MCP 299, server roundtrip 2, client views 3; client+server tsc чисто.Ревью
Ревью-сабагент: трасса
[^d][^d]+ два[^d]:сходится за один проход (SYNC_META), детерминизм Yjs сохранён, зеркала marked/MCP идентичны, удаление мёртвого кода безопасно. Вердикт APPROVE with suggestions — обе косметические правки (устаревший комментарий + формулировка предупреждения) применены.Границы / следующий шаг
Мульти-бэклинки в редакторе (определение возвращает ко ВСЕМ своим ссылкам ↩ a b c) вынесены в #168 — это UI-слой с визуальной проверкой. Forward-ссылки и нумерация уже корректно reuse'ятся; обратная стрелка пока ведёт к первой ссылке.
🤖 Generated with Claude Code
можно мержить после исправления