feat(mcp): drawio стадия 3 — семантические тулы fromGraph/fromMermaid/editCells (#425) #472
Reference in New Issue
Block a user
Delete Branch "feat/425-drawio-graph"
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
Drawio стадия 3 — семантические инструменты: модель описывает диаграмму графом (узлы/группы/связи с иконками), а координаты, стили, иконки и лейаут считает сервер. Весь класс ошибок налезаний/пустых иконок/кривых интервалов уходит by construction. Сырой XML остаётся escape-hatch для экзотики. closes #425
Стоит стопкой на #443 (
feat/443-get-page-context, PR #470) — база указывает на неё, в диффе только стадия 3. Переиспользует конвейер #423 и shape-index/elkjs/линтер #424.Инструменты (camelCase)
drawioFromGraph(главный) — граф → резолв иконок (#424; неизвестная → generic поkindс подписью, не пустой квадрат) → стили из пресета → elkjs-layered с compound-группами → линтер-чистый ассемблер XML. Хинтыpinned/sameLayerAs/layerиlayout: none|full|incremental— детерминированным post-pass'ом (ELK-констрейнты оказались ненадёжны в прямом тестировании). Пресетыdefault/dark/colorblind-safe(Okabe-Ito) — данные.drawioFromMermaid— чистый парсер flowchart (без браузера/CLI) → тот же graph-конвейер. Формы/направление/пунктир=async/subgraph→группы/цепочки; не-flowchart отвергает внятно. (Сделан, не дропнут — парсер тянется как чистый TS.)drawioEditCells— ID-basedadd/update/delete, delete каскадит на детей контейнера и связанные рёбра;baseHashобязателен (optimistic lock).Безопасность / устойчивость (важно — вход от LLM)
MAX_GRAPH_EDGES=1000/GROUPS=500(узлы уже 500), mermaidMAX_CHARS=200k/LINES=20k/GROUPS=500, chain 500 — все с быстрымthrowДО лейаута/ассемблера. Критично: ассемблер и graph→ELK-маппер работают вне 5-сек ELK-таймаута, без границ граф в 200k рёбер клал воркер по OOM (поймано адверсарно на ревью).mergeExistingCells) — «добавь один узел» не стирает ручную расстановку человека.sameLayerAs/layerпосле снапа раскладываются по перпендикулярной оси с зазором ≥150 → 0 quality-warnings;pinned— точные пользовательские координаты (clamp ≥0; гарантия «0 by construction» относится к авто-лейауту, явные пины — user-directed).0/1защищены отdelete.How verified
mcptscчисто;node --test— 782/782 (57 новых): приёмка (15+ узлов / 2 вложенные группы / AWS-иконки → 0 lint, 0 quality-warnings, все иконки резолвятся, группы прозрачные, направление; hints; incremental без сдвига существующих; edit_cells update-one / cascade-delete / stale-baseHash; снапшоты пресетов + colorblind-safe Okabe-Ito; mermaid ветвление+subgraph) + регрессии на все DoS-границы (быстрый reject, без запуска OOM-кейса).prosemirror-markdowntsc чисто;apps/serverjest (shared-tool-specs.contract+ai-chat-tools.service) — 290/290 (3 тула заведены in-app).Внутренний цикл
2 прохода. Первый (адверсарно, с эмпирическими пробниками на реальном линтере/ELK) поймал 2 CRITICAL DoS (нет лимита рёбер → OOM воркера; mermaid без границы входа → тот же путь) + 2 WARNING (post-pass хинтов ломал заявленный «0 warnings»; incremental тихо терял ручную расстановку) + защиту сентинелов. Все закрыты с регрессионными тестами. Второй проход (прямое адверсарное чтение новой merge/spread-логики) — чисто.
Checklist
Drawio стадия 3 (closes #425). Стоит стопкой на #443 (PR #470, feat/443-get-page-context) — база указывает на неё, в диффе только стадия 3. Мержить после #470.
Что сделал: три семантических тула — fromGraph (граф→сервер считает иконки/стили/лейаут/координаты, ошибки налезаний уходят by construction), fromMermaid (чистый парсер flowchart, НЕ дропнут — тянется без браузера), editCells (ID-based правки с каскадным delete). from_mermaid — как ты просил.
Проверка: mcp node --test 782/782 (57 новых, вся приёмка + DoS-регрессии), tsc чисто, server jest 290/290.
Внутренний цикл: 2 прохода. Первый (адверсарно, эмпирические пробники на реальном линтере/ELK) поймал 2 CRITICAL DoS — граф от LLM без лимита рёбер клал воркер по OOM (5-сек ELK-таймаут не спасал: ассемблер и маппер вне него), и mermaid без границы входа давал тот же путь; плюс 2 WARNING — post-pass хинтов ломал заявленный «0 quality-warnings», а incremental тихо стирал неперечисленные существующие ячейки (главный кейс «добавь узел»). Все закрыты с регрессионными тестами. Второй проход (прямое чтение новой merge/spread-логики) — чисто.
Это последний пункт из собранного на сейчас: прод-фикс + breaking-окно + #443 (3 тула) + drawio-3. Всё стопкой, все approved кроме этого (на ревью). Дальше по роадмапу — #449 (concurrency-инварианты).
Ревью — #472
feat(mcp): drawio стадия 3 — семантические тулы fromGraph/fromMermaid/editCells (#425)Вердикт: PASS ✅
Полный веер из 9 аспектов по изолированной дельте (база — ветка #443-get-page-context/#470; ревьюил
5dc7a270..2a951df0= 16 файлов, +3254/−1). Гейт зелёный на CI-проверках, DoS-капы адверсариально пришпилены.Security/DoS/корректность — проверено (клик)
3 семантических тула: модель описывает граф → сервер считает координаты/стили/иконки/лейаут → линтер-чистый XML. Сырой XML остаётся escape-hatch.
xmlEscapeAttrэкранирует&<>"\nна ВСЕХ агентских строках (id/label/parent/edge from/to/label); стили сервер-генерируемые (пресеты/каталог), не агентские. mermaid-регексы ReDoS-safe (без вложенных квантификаторов). baseHash обязателен+строгая проверка (пустой→reject). SENTINEL_IDS {"0","1"} защищены от delete/collision (гард + линтер duplicate-id).if(existing.has(id)) continue); cascade-delete без orphan-рёбер; unknown icon → generic-by-kind с подписью (не пустой квадрат); mermaid non-flowchart → внятный reject. Аддитивно (стадии 1/2 не тронуты, #424 elk/shape-index/линтер переиспользованы).⛔ DROP — кодеру НЕ делать (лог калибровки; расследование server-tsc)
[out-of-scope]low[stability]pnpm --filter server exec tsc --noEmitКРАСНЕЕТ на import.meta в mcp-ИСХОДНИКЕ (drawio-presets.ts:66 новый + drawio-shapes.ts:246 из #424). Расследовал: НЕ CI-проверка — CI гоняет server только через jest (pnpm -r test/test:int/ точечный jest; server tsconfig без typecheck-скрипта). Runtime чист (mcp собран как ESM, import.meta работает), собранный.d.tsБЕЗ import.meta, server jest 280/280 зелёный. Корень — server tsconfigmodule:commonjs+moduleResolution:None(classic-node) резолвит workspace@docmost/mcpв SOURCE, а не в build/.d.ts → tsc компилит mcp-src с import.meta под commonjs (TS1343). Латентно/пред-существующе (drawio-shapes.ts import.meta принят в #424; develop зелён т.к. его type-usage не форсит резолв src-модуля с import.meta; #472 форсит новыми tool-типами). Фикс — server moduleResolution→node16/nodenext (чтоб honor mcpexports.types→build/.d.ts), это server-config/mcp-integration концерн, шире #472. НЕ блок (не CI, не runtime). Флагаю для осознанности.[below-threshold]low[test-coverage]xmlEscapeAttrне покрыт ОТДЕЛЬНЫМ unit-тестом (корректен + косвенно через linter-pass тесты). Дёшево добавить.[below-threshold]low[security] validateGraph мог бы реджектить id "0"/"1" раньше (сейчас ловит линтер) — defense-in-depth UX-мелочь.[below-threshold]low[documentation] нет CHANGELOG (паттерн с #468/#469/#470).DO/ESCALATE нет. DoS-капы (fail-fast, адверсариально), XML-эскейп на всех агентских строках, sentinel/baseHash, «0 warnings by construction» на реальном линтере, incremental/cascade/mermaid-reject — подтверждены. Единственный красный (server tsc) — НЕ CI-проверка, workspace-source-резолв латентного import.meta-паттерна (#424), runtime/CI/
.d.tsчисты. Approve.Стадия 3 drawio #425, на #470. 9 аспектов (security+DoS, stability+correctness+coherence, testcov+conv+docs+simp+arch — все LGTM). Капы load-bearing (cap-lower→12 тестов краснеют); XML-эскейп/sentinel/baseHash/0-warnings-real-linter проверены. server-tsc-red расследован → out-of-scope (не CI, classic-node резолв mcp-src, import.meta-паттерн из #424; фикс = server moduleResolution, шире PR). Merge-order: после #470. Escalate нет.