8c74659d91
Ядро Фазы 1б. Реестр (#294) шарил только метаданные (имя/схема/описание/tier), но НЕ execute-логику — у каждого shared-тула было ДВА рукописных execute-тела с копией маппинга аргументов (MCP registerShared в index.ts; in-app sharedTool в ai-chat-tools.service, зеркалящий MCP-транспорт вручную). Корень повторяющихся parity-багов (f46d89eadrawio,f8d26420stashPage,fc9088b7node-args): добавление одного тула = 7-9 согласованных ручных правок в двух пакетах. - SharedToolSpec расширен: канонический execute(client, args) (чистый JS — свободно пересекает zod-мажорную границу v3/v4) + оверрайды mcpExecute/inAppExecute/mcpOnly/inAppOnly для ОСОЗНАННЫХ per-layer различий. client: DocmostClientLike (Pick из #446). Канон возвращает СЫРЬЁ, каждый хост накладывает свой конверт (MCP jsonContent, in-app как есть); override владеет результатом хоста целиком. - Оба хоста → циклы по Object.values(SHARED_TOOL_SPECS): index.ts registerShared 39→0 (цикл), ai-chat-tools.service sharedTool ~40→1 (цикл). Добавление спека автоматически регистрирует тул в ОБОИХ хостах — сценарий PR #434 невозможен по построению. - Осознанные различия через overrides (ни одно не сплющено к одному хосту): оба mcpExecute+inAppExecute — createPage/movePage/deletePage/ exportPageMarkdown/createComment (guardrails, конверты, проекции, тексты ошибок); execute+inAppExecute — getPage/renamePage/resolveComment; execute+mcpExecute — stashPage (resource_link+structuredContent), checkNewComments (since-guard только на MCP). - Оставлены inline (по делу): update_comment/delete_comment (MCP-only, in-app не даёт хард-правку/удаление комментов), search/transformPage (per-transport дивергенция — hybrid RRF / без deleteComments), table_get (noun-vs-verb naming clash — уедет после camelCase #412), getCurrentPage/updatePageContent/ listSidebarPages/getComment/getPageHistory (in-app-only, per-request state). - Guard-тесты (contract-parity, phantom-catalog) сохранены — теперь инварианты, не «последняя линия». Внутреннее ревью: APPROVE, построчная BEFORE/AFTER-сверка по каждому shared-тулу на обоих хостах — ни одного изменённого per-host поведения (порядок/дефолты аргументов, guard'ы, конверты, проекции сохранены), множества тулов побайтово совпадают (48 in-app, 45 MCP), кросс-zod-граница чистая (нет z. в execute), 611 mcp + 260 server тестов зелёные. Ядро Фазы 1б, стоит на #446 — мержить после. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>