Open
agent_coder
wants to merge 2 commits from
fix/503-hardbreak-canon into develop
pull from: fix/503-hardbreak-canon
merge into: vvzvlad:develop
vvzvlad:main
vvzvlad:feat/529-search-lexical
vvzvlad:feat/506-apikeys-ui
vvzvlad:feat/395-work-time
vvzvlad:feat/502-mcp-md-modes
vvzvlad:feat/492-incremental-render
vvzvlad:fix/549-hardbreak-md-canon
vvzvlad:feat/542-resolve-undo
vvzvlad:fix/505-drawio-strip-comments
vvzvlad:docs/471-breadcrumb-permission
vvzvlad:fix/534-mcp-spaceid
vvzvlad:feat/481-version-coherence
vvzvlad:fix/535-list-seam-coalesce
vvzvlad:develop
vvzvlad:fix/520-recovery-escalation
vvzvlad:fix/525-insert-unloaded
vvzvlad:fix/522-internal-links
vvzvlad:fix/507-drawio-cyrillic
vvzvlad:fix/541-reconnect-timeout
vvzvlad:feat/501-mcp-apikey-auth
vvzvlad:feat/493-converter
vvzvlad:feat/370-page-versioning
vvzvlad:fix/515-code-emphasis
vvzvlad:fix/523-tree-dnd
vvzvlad:fix/517-toasts
vvzvlad:feat/495-tails
vvzvlad:feat/494-mcp-registry-guards
vvzvlad:feat/496-suggestions-audit
vvzvlad:integ/497-wave1
vvzvlad:feat/488-client-fsm
vvzvlad:ci/476-publish-gates
vvzvlad:feat/489-tool-resilience
vvzvlad:feat/487-terminal-lifecycle
vvzvlad:fix/486-iter1-prod-fires
vvzvlad:refactor/347-client-md-paste
vvzvlad:perf/479-getpage-cache
vvzvlad:refactor/449-write-invariants
vvzvlad:refactor/450-split-client
vvzvlad:feat/443-get-page-context
vvzvlad:feat/443-get-tree
vvzvlad:feat/443-search
vvzvlad:docs/415-lossy-descriptions
vvzvlad:feat/413-markdown-default
vvzvlad:refactor/412-camelcase-tool-names
vvzvlad:feat/425-drawio-graph
vvzvlad:fix/464-diffdocs-cpu-guard
vvzvlad:refactor/411-update-page-markdown
vvzvlad:feat/424-drawio-rules
vvzvlad:fix/409-invalid-node-validation
vvzvlad:refactor/448-generate-inventory
vvzvlad:refactor/445-execute-mapping
vvzvlad:refactor/446-derive-client-types
vvzvlad:fix/447-registry-stamp-ci
vvzvlad:fix/444-agent-loop-guards
vvzvlad:fix/396-sendnow-detached-run
vvzvlad:fix/452-immutable-cache-control
vvzvlad:docs/footnote-authoring-comment-cleanup
vvzvlad:feat/437-error-diagnostics
vvzvlad:fix/442-cursor-pagination
vvzvlad:feat/419-footnote-normalize
vvzvlad:perf/399-comment-resolve-async
vvzvlad:perf/435-collab-token-cache
vvzvlad:perf/344-background-rerenders
vvzvlad:perf/348-backend-lowhanging
vvzvlad:feat/423-drawio-crud
vvzvlad:perf/401-collab-hotpath
vvzvlad:feat/430-live-reconnect
vvzvlad:perf/400-collab-session
vvzvlad:refactor/414-dedup-node-ops
vvzvlad:feat/417-new-comments-signal
vvzvlad:feat/408-createcomment-hints
vvzvlad:feat/407-persist-tool-errors
vvzvlad:feat/420-footnote-render
vvzvlad:dummy-review/mcp-hang-fix
vvzvlad:dummy-review/mcp-hang-base
vvzvlad:feat/381-resumable-sse-pr1
vvzvlad:feat/git-sync-2
vvzvlad:docs/agents-workspace-build-order
vvzvlad:feature/offline-sync
vvzvlad:perf/342-code-splitting
vvzvlad:perf/346-compression-cache
vvzvlad:feat/196-multi-cursor
vvzvlad:perf/343-typing-latency
vvzvlad:docs/how-to-test
vvzvlad:fix/ai-sdk-partial-output-oom
vvzvlad:test/351-generative-converter
vvzvlad:feat/371-roles-catalog
vvzvlad:refactor/345-server-converter
vvzvlad:refactor/294-spec-registry-cont
vvzvlad:fix/363-migration-order
vvzvlad:fix/e2e-callout-and-gate-build
vvzvlad:fix/docker-re2-toolchain
vvzvlad:feat/git-sync
vvzvlad:fix/media-roundtrip-stability
vvzvlad:fix/340-comment-panel-perf
vvzvlad:fix/332-deferred-tools
vvzvlad:fix/329-ephemeral-suggestions
vvzvlad:fix/330-search-in-page
vvzvlad:fix/328-resolved-anchor-spam
vvzvlad:fix/331-intraline-diff
vvzvlad:fix/324-coverage-gate
vvzvlad:fix/325-mobile-390
vvzvlad:feat/293-A-git-sync-package
vvzvlad:feat/300-avatar-oklch
vvzvlad:fix/321-banner-mobile
vvzvlad:feat/300-avatar-colors
vvzvlad:feat/315-comment-suggestions
vvzvlad:feat/scroll-restore-stable-wait
vvzvlad:feat/300-agent-avatar-stack
vvzvlad:feat/300-avatar-polish
vvzvlad:refactor/294-tool-spec-registry
vvzvlad:feat/scroll-restore-ux
vvzvlad:fix/responsive-tablet-sidebar
vvzvlad:feature/ai-chat-page-change-observability
vvzvlad:image-inline-center
vvzvlad:fix/283-short-remap-title
vvzvlad:fix/283-slash-layout
vvzvlad:image-inline-row
vvzvlad:feat/276-ai-chat-dock
vvzvlad:fix/269-table-menu-refocus
vvzvlad:docs/dev-stand-guide
vvzvlad:feat/266-scroll-position
vvzvlad:fix/260-collab-docname-slugid
vvzvlad:test/244-phase2-tail
vvzvlad:fix/262-reindex-progress-realtime
vvzvlad:fix/258-changelog-compare-links
vvzvlad:fix/244-dataloss-bugs
vvzvlad:feat/246-spoiler
vvzvlad:feat/221-image-captions
vvzvlad:test/244-part-b
vvzvlad:feat/251-intentional-clear
vvzvlad:fix/embeddings-reindex-progress
vvzvlad:refactor/193-tool-spec-registry
vvzvlad:fix/255-ws-redis-adapter-leak
vvzvlad:fix/252-e2e-open-handles
vvzvlad:feat/229-catalog-yaml
vvzvlad:feat/243-blob-sandbox
vvzvlad:feat/228-inline-footnotes
vvzvlad:fix/qa-ui-bugs-216-218
vvzvlad:feature/agent-roles-catalog
vvzvlad:fix/share-alias-rename
vvzvlad:fix/ai-chat-empty-render
vvzvlad:feat/191-chat-doc-binding
vvzvlad:feat/201-temporary-notes
vvzvlad:feat/198-interrupt-agent
vvzvlad:feat/ai-chat-full-history
vvzvlad:feat/199-ai-generate-title
vvzvlad:feat/205-share-aliases
vvzvlad:batch/issues-189-187-170
vvzvlad:feat/170-mcp-test-button
vvzvlad:feat/189-context-badge
vvzvlad:feat/198-interrupt-agent-send-now
vvzvlad:fix/issues-190-159
vvzvlad:fix/ai-chat-new-chat-during-stream
vvzvlad:fix/ai-chat-stream-perf
vvzvlad:batch/issues-2026-06-25
vvzvlad:feat/ai-chat-persistent-history
vvzvlad:fix/ai-chat-copy-chat-wysiwyg
vvzvlad:fix/ai-stream-reset-resilience
vvzvlad:fix/ai-stream-undici-timeout
vvzvlad:fix/footnote-review-1227-followup
vvzvlad:fix/ai-chat-token-counter-realtime
vvzvlad:docs/manual-qa-test-plan
No Reviewers
Labels
Clear labels
epic
needs-human
review/approved
review/changes-requested
review/needs
возможно, баг
Large multi-phase effort spanning many changes
эскалация: нужно решение человека
в последнем ревью нет открытых blocking-находок
последнее ревью оставило открытые blocking-находки
head не ревьюился (head != reviewed_head)
Похоже на баг, но требует подтверждения/воспроизведения; чиним, если повторится
No Label
review/approved
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: vvzvlad/gitmost#544
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "fix/503-hardbreak-canon"
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
Разбор #503: баг НЕ воспроизводится на текущем develop — hardBreak уже представим в markdown-каноне в ОБЕ стороны. Закрываю как «уже починено #293 STEP 5» + добавляю регресс-гард. closes #503.
Почему диагноз в ишью устарел
Ишью указывает на
packages/mcp/src/lib/markdown-converter.tsкак на сериализатор — но это 15-строчный re-export shim с #293 STEP 5 (конвертер консолидирован в@docmost/prosemirror-markdown). Та консолидация уже закрыла обе стороны:markdown-converter.ts:834case "hardBreak": return " \n"(+:1352<br>для HTML-пути);<br>→generateJSONматериализует hardBreak-ноду (спец-хендлерbrне нужен, HTML→PM мост уже покрывает).Проверил репро через РЕАЛЬНЫЕ пути (собранный пакет, не заглушка):
{text, hardBreak, text}→"line one \nline two"— НЕ дропается;[text, hardBreak, text]— не сплитится, не теряется;updatePageJson): PM JSON →TiptapTransformer.toYdoc→fromYdocсохраняет hardBreak;Что в PR
Кода конвертера НЕ трогал (чинить нечего). Добавлен ОДИН интеграционный гард —
hardBreak survives the MCP canon in BOTH directions (#503)вdocmost-md-roundtrip.test.mjs— покрывает единственный шов, не гарденный end-to-end ранее: точный сценарийupdatePageJsonчерез все три стыка (pm→md точные байты, md→pm one-paragraph, P2 фикспойнт, Yjs-энкод round-trip).How verified
\n→\n) → гард краснеет; нейтрализация импортёра (strip<br>до generateJSON) → гард краснеет. Обе восстановлены, src нетронут.\n(два трейлинг-спейса) — backslash-newline тоже импортится в hardBreak, но переключение вычернило бы весь #351 byte-fixpoint корпус ради нулевого функционального выигрыша.Примечание
Резолюция «уже починено #293 STEP 5, закрываю с регресс-гардом», а не код-фикс. Если предпочитаешь оставить #503 открытой как «подтверждено-не-баг» без PR — скажи, закрою PR.
Checklist
Ревью — регресс-гард hardBreak (#503). Вердикт: changes-requested
Диагноз верный: #503 реально починен #293 (сериализатор эмитит
\n, mid-paragraph round-trips, и РЕАЛЬНЫЙ updatePageJson-путьapplyDocToFragment→updateYFragmentсохраняет hardBreak — проверено на 5 подслучаях). Закрытие #503 обосновано. Гейт mcp 861/861. Тест не вакуумен (мутация сериализатора\n→\nроняет seam 1). Security/stability/regressions/architecture/simplification — LGTM. Эскалаций нет.Открыто (оба мелкие):
docmostExtensions.📋 F1, F2 + вне-scope
F1 [warning · test-coverage/documentation] seam 3 тестит другой энкодер —
packages/mcp/test/unit/docmost-md-roundtrip.test.mjs:3(комментарий) + строка seam 3Коммент называет seam 3 «the literal updatePageJson mechanism», но он использует
buildYDoc=TiptapTransformer.toYdoc. РЕАЛЬНЫЙ write-путь updatePageJson —applyDocToFragment=PMNode.fromJSON+updateYFragment(collab-session.ts). Кодовая база сама помечает их как ДВА РАЗНЫХ энкодера (collaboration.ts:35-36: «shared by both encode paths (buildYDoc→toYdoc и applyDocToFragment→updateYFragment)»). Оба сегодня сохраняют hardBreak (ложного прохода НЕТ), но регресс-гард, чья заявленная цель — стеречь updatePageJson-путь, не прогоняет энкодер, через который течёт симптом #503: будущая регрессия вupdateYFragmentпроскочит мимо. Fix: переключи seam 3 наapplyDocToFragment(реальный путь), ЛИБО поправь коммент, что seam 3 стережёт toYdoc-путь, а не updatePageJson. Заодно усиль ассерт seam 3 (сейчас голыйfind(...,"hardBreak")— добавь проверку окружающего текста / единственного абзаца).F2 [suggestion · conventions] мёртвый импорт —
packages/mcp/test/unit/docmost-md-roundtrip.test.mjs:11import { docmostExtensions } from "@docmost/prosemirror-markdown";не используется (fromYdocзовётся с 2 аргументами;buildYDocтянет свой внутренний импорт). Снести (тронул быno-unused-vars, если lint покрывает.mjs).9 аспектов (свёрнуты в верификацию диагноза+гарда + мой разбор). Гейт: pm-markdown build + mcp node:test 861/861 (новый тест включён, зелёный). Дерево после мутаций чисто. База develop
03eafa6c.Вне scope (НЕ часть этого вердикта, отдельная задача): завёл #549 — markdown-канон теряет hardBreak в trailing/consecutive/table-cell (pm→md→pm через
updatePageMarkdown/экспорт; НЕ регрессия #503 — JSON-путь их сохраняет). Всплыло при этом ревью.e867478626to03af65dd06Обе находки закрыты (
dead5fa8+ мерж develop03af65dd). Внутренний цикл: 1 фикс-проход, seam 3 подтверждён мутационной изоляцией.F1: fixed— seam 3 теперь гоняет РЕАЛЬНЫЙ энкодер updatePageJson. Переключил его сbuildYDoc/TiptapTransformer.toYdocнаapplyDocToFragment(new Y.Doc()→PMNode.fromJSON(docmostSchema)+updateYFragment→ чтение черезfromYdoc) — ровно то, что зовётcollab-session.tsна путиupdatePageJson → replacePage → mutatePageContent → session.mutate → applyDocToFragment. Коммент исправлен (называет реальный путь и отличает его отtoYdoc). Ассерт усилён: было голоеfind(...,"hardBreak"), сталоfindAll(paragraph).length===1+content.map(type)строго["text","hardBreak","text"](break сохранён между двумя ранами в ОДНОМ абзаце, не потерян/не расщеплён). Мутация (вырезать hardBreak прямо вapplyDocToFragment) роняет РОВНО seam 3 (860 pass / 1 fail) — seams 1-2 на этот энкодер не завязаны, изоляция доказана.F2: fixed— мёртвый импортdocmostExtensionsудалён; новые импорты (Y,applyDocToFragment, сохранённыйTiptapTransformerдля чтения) все используются.📋 Проверка
mcp suite 861/861; pm-markdown + mcp build tsc 0. Правка только в тест-файле
docmost-md-roundtrip.test.mjs(+71/−1),context.ts/сериализатор не тронуты. node_modules 0. Влил develop. (Диагноз #503 не изменился — уже починено #293, PR = регресс-гард; вне-scope #549 не трогал.)Ре-ревью — регресс-гард hardBreak (#503), раунд 2. Вердикт: approved ✅
Обе находки закрыты:
const ydoc = new Y.Doc(); applyDocToFragment(ydoc, doc)(PMNode.fromJSON + updateYFragment — тот самый collab-write-путь, не buildYDoc/toYdoc), коммент исправлен (:242-244). Ассерт УСИЛЕН сверх просьбы: не голыйfind(hardBreak), аparagraph.length===1(не сплитит) + точная последовательность["text","hardBreak","text"]. Гард стережёт ровно тот энкодер, через который тёк симптом #503.docmostExtensionsснят.Гейт: mcp node:test 867/867 (тест с реальным энкодером зелёный). Дерево чисто. Готово к мержу. (Вне-scope markdown-потеря hardBreak — отдельная задача #549, не блокирует.)
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.