2f23cf4b65
Боевой инцидент: diffDocs синхронно зовёт recreateTransform (rfc6902), чей array-diff — O(n·m) по числу нод и O(w²) по длине прогонов слов, и который НИКОГДА не бросает. Поэтому существующий catch→coarseDiff не спасает event loop: большая агентская правка блокирует воркер на секунды→минуты (бенч: 401 нода→1.3с, 801→5.5с, 1601→OOM; отдельная ось — байт-тяжёлый вход: 309КБ→OOM), а это душит все остальные запросы воркера. Отсюда живой прод-хэнг. Фикс — pre-flight size-guard ПЕРЕД дорогим вызовом: - readPositiveIntEnv(name, dflt): парс env каждый вызов; мусор/пусто/≤0/не-finite → дефолт, так что guard нельзя случайно отключить. - exceedsDiffSizeGuard(old, new): срабатывает по max(old,new) на ОБЕИХ осях — countNodes и JSON.stringify().length — так что асимметричная пара (крошечный new / огромный old и наоборот) тоже триггерит. Дёшево: один обход + один stringify на документ. - Дефолты MCP_DIFF_MAX_NODES=150, MCP_DIFF_MAX_BYTES=12288 (12 КиБ), env-переопределяемы. Подобраны бенчмарком под бюджет ~200мс на ЛЮБОЙ форме входа на границе cap'а (исходные догадки тикета 3000-5000 нод / 512КБ-1МБ были в 20-30× завышены — пропускали бы многоминутные блоки). - Рефактор: выделены coarseDiffTally (единый источник формы fellBack:true) и preciseDiffTally. Новый поток diffDocs: computeIntegrity (без изменений, для обоих путей) → если exceedsDiffSizeGuard → coarseDiffTally(fellBack=true) → иначе try preciseDiffTally / catch → coarseDiffTally. Обе деградации дают идентичную форму результата. Fail-closed: единственный путь к recreateTransform — ветка else, достижимая только когда guard НЕ сработал; огромный документ физически до transform не доходит. computeIntegrity (корректностный сигнал) считается ВСЕГДА. Все три call-site потребляют DiffResult как информационный preview, не гейтят запись — coarse-фоллбэк контракт-сохраняющий. Тесты (11): over/under-порог, обе асимметрии, байт-ось (нод-мало/байт-много), env-override низким cap'ом, garbage-env→дефолт (guard не отключается), integrity корректен на tripped-документе; behavioral-proxy что transform пропущен над cap'ом (guarded ~5мс vs caps-raised ~3.3с, ≥5× + <200мс бюджет). node --test 688/688. tsc --noEmit чисто. Только packages/mcp, внешний симлинк не тронут. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>