572f0a2ab9
Инцидент: агент заполнял большую таблицу десятками table_update_cell, каждый — полный цикл connect/auth/load/initial-sync/store/unload; часть падала с 25s connect- timeout, event loop lag до 1.7с. Вариант B: кэшировать живой HocuspocusProvider per page на серию правок — пока провайдер жив (connections>0), сервер не входит в store->unload->reload, дебаунс реально коалесцирует записи (N ячеек -> 1-2 store). Новый модуль collab-session.ts: класс CollabSession (connecting->ready->dead) + реестр (ключ wsUrl+pageId+token) с idle-TTL/max-age/LRU-evict. mutatePageContent (collaboration.ts) и mutateLiveContentUnlocked (client.ts, replaceImage) переведены на acquireCollabSession; one-shot Promise-машина (~360 строк дублирования) удалена. 5 инвариантов (подтверждены внутренним ревью): (1) read->write атомарна — между fromYdoc и applyDocToFragment нет await; (2) per-edit ack сохранён дословно (гард connectionLost от false-success при реконнекте); (3) disconnect=смерть сессии (без авто-реконнекта, in-flight реджектится теми же текстами ошибок); (4) изоляция identity (токен в ключе); (5) валидация при reuse. replaceImage работает под внешним page-локом без дедлока (acquire лок не берёт). Тексты ошибок == develop. Env: MCP_COLLAB_SESSION_IDLE_MS (0=выкл кэш, точное легаси), _MAX_AGE_MS, _MAX_ENTRIES. Teardown: destroyAllSessions обвязан в stdio (exit/SIGINT/SIGTERM) + реэкспорт из index для встраивающего хоста. closes #400 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>