refactor(tools): execute-маппинг в SHARED_TOOL_SPECS + автопроводка обоих хостов (#445) #459

Open
agent_coder wants to merge 3 commits from refactor/445-execute-mapping into develop
Collaborator

Summary

Ядро Фазы 1б роадмапа #416 — третий линк стопки. closes #445

Реестр SHARED_TOOL_SPECS (#294) шарил между хостами только метаданные (имя/схема/описание/tier), но НЕ execute-логику: у каждого shared-тула было ДВА рукописных execute-тела с копией маппинга аргументов (внешний MCP registerShared в index.ts; in-app sharedTool в ai-chat-tools.service.ts, зеркалящий MCP-транспорт ВРУЧНУЮ). Это корень повторяющихся parity-багов (f46d89ea drawio, f8d26420 stashPage, fc9088b7 node-args) — добавление одного тула = 7-9 согласованных ручных правок в двух пакетах.

  • SharedToolSpec расширен: канонический execute(client, args) — чистый JS, свободно пересекает zod-мажорную границу (v3 в mcp / v4 на сервере) — плюс оверрайды mcpExecute/inAppExecute/mcpOnly/inAppOnly для ОСОЗНАННЫХ per-layer различий. client: DocmostClientLike (Pick из #446). Канон возвращает СЫРЬЁ, каждый хост накладывает свой конверт (MCP jsonContent, in-app как есть); override владеет результатом хоста целиком.
  • Оба хоста → циклы по реестру: index.ts registerShared 39→0, ai-chat-tools.service sharedTool ~40→1. Добавление спека автоматически регистрирует тул в ОБОИХ хостах — сценарий PR #434 («спек есть, in-app проводки нет») невозможен по построению.
  • Осознанные различия через overrides (ни одно не сплющено): оба mcpExecute+inAppExecute — createPage/movePage/deletePage/exportPageMarkdown/createComment; execute+inAppExecute — getPage/renamePage/resolveComment; execute+mcpExecute — stashPage/checkNewComments.
  • Оставлены inline (по делу): update_comment/delete_comment (MCP-only), search/transformPage (per-transport дивергенция), table_get (naming clash — войдёт после camelCase #412), getCurrentPage/updatePageContent/listSidebarPages/getComment/getPageHistory (in-app-only, per-request).
  • Guard-тесты (contract-parity, phantom-catalog) сохранены — теперь инварианты, не «последняя линия».

How verified

Стенд (symlink @docmost/mcp временно на пакет ветки для серверных шагов, восстановлен):

  • pnpm --filter @docmost/mcp build → чисто.
  • node --test test/unit/* test/mock/*611 pass (вкл. stash-page e2e, server-instructions, schema — mock-тесты гоняют MCP execute-тела).
  • tsc --noEmit -p apps/server → без новых ошибок.
  • jest ai-chat-tools.service shared-tool-specs.contract tool-tiers comment-signal-inapp260 pass.
  • Acceptance-grep: in-app sharedTool(=1 (было ~40), registerShared(=0 (было 39).

Внутренний цикл: 1 проход (APPROVE). Ревьюер сделал ПОСТРОЧНУЮ BEFORE/AFTER-сверку по каждому shared-тулу на ОБОИХ хостах: ни одного изменённого per-host поведения (порядок/дефолты аргументов, guard'ы movePage/createComment/checkNewComments, конверты resource_link/bare-line/single-text, проекции результатов — сохранены), множества тулов побайтово совпадают (48 in-app, 45 MCP), кросс-zod-граница чистая (нет z. в execute-телах).

Порядок мержа

Третий линк Фазы 1б, стоит на #446 (PR #458). Мержить: #457 (#447) → #458 (#446) → этот#448. См. комментарий с порядком в #416. Следующий #448 (генерация инвентаря) встанет сюда.

Checklist

  • добавление спека в tool-specs.ts делает тул доступным в обоих хостах без правок index.ts/ai-chat-tools.service.ts
  • ни одного дублированного маппинга аргументов между хостами (только override-случаи)
  • сценарий PR #434 невозможен по построению
## Summary Ядро Фазы 1б роадмапа #416 — третий линк стопки. closes #445 Реестр `SHARED_TOOL_SPECS` (#294) шарил между хостами только метаданные (имя/схема/описание/tier), но НЕ execute-логику: у каждого shared-тула было ДВА рукописных execute-тела с копией маппинга аргументов (внешний MCP `registerShared` в `index.ts`; in-app `sharedTool` в `ai-chat-tools.service.ts`, зеркалящий MCP-транспорт ВРУЧНУЮ). Это корень повторяющихся parity-багов (`f46d89ea` drawio, `f8d26420` stashPage, `fc9088b7` node-args) — добавление одного тула = 7-9 согласованных ручных правок в двух пакетах. - **`SharedToolSpec` расширен**: канонический `execute(client, args)` — чистый JS, свободно пересекает zod-мажорную границу (v3 в mcp / v4 на сервере) — плюс оверрайды `mcpExecute`/`inAppExecute`/`mcpOnly`/`inAppOnly` для ОСОЗНАННЫХ per-layer различий. `client: DocmostClientLike` (Pick из #446). Канон возвращает СЫРЬЁ, каждый хост накладывает свой конверт (MCP `jsonContent`, in-app как есть); override владеет результатом хоста целиком. - **Оба хоста → циклы по реестру**: `index.ts` `registerShared` 39→0, `ai-chat-tools.service` `sharedTool` ~40→1. Добавление спека автоматически регистрирует тул в ОБОИХ хостах — сценарий PR #434 («спек есть, in-app проводки нет») невозможен по построению. - **Осознанные различия через overrides** (ни одно не сплющено): оба mcpExecute+inAppExecute — createPage/movePage/deletePage/exportPageMarkdown/createComment; execute+inAppExecute — getPage/renamePage/resolveComment; execute+mcpExecute — stashPage/checkNewComments. - **Оставлены inline** (по делу): update_comment/delete_comment (MCP-only), search/transformPage (per-transport дивергенция), table_get (naming clash — войдёт после camelCase #412), getCurrentPage/updatePageContent/listSidebarPages/getComment/getPageHistory (in-app-only, per-request). - Guard-тесты (contract-parity, phantom-catalog) сохранены — теперь инварианты, не «последняя линия». ## How verified Стенд (symlink `@docmost/mcp` временно на пакет ветки для серверных шагов, восстановлен): - `pnpm --filter @docmost/mcp build` → чисто. - `node --test test/unit/* test/mock/*` → **611 pass** (вкл. stash-page e2e, server-instructions, schema — mock-тесты гоняют MCP execute-тела). - `tsc --noEmit -p apps/server` → без новых ошибок. - `jest ai-chat-tools.service shared-tool-specs.contract tool-tiers comment-signal-inapp` → **260 pass**. - Acceptance-grep: in-app `sharedTool(`=1 (было ~40), `registerShared(`=0 (было 39). Внутренний цикл: 1 проход (APPROVE). Ревьюер сделал ПОСТРОЧНУЮ BEFORE/AFTER-сверку по каждому shared-тулу на ОБОИХ хостах: ни одного изменённого per-host поведения (порядок/дефолты аргументов, guard'ы movePage/createComment/checkNewComments, конверты resource_link/bare-line/single-text, проекции результатов — сохранены), множества тулов побайтово совпадают (48 in-app, 45 MCP), кросс-zod-граница чистая (нет `z.` в execute-телах). ## Порядок мержа Третий линк Фазы 1б, **стоит на #446** (PR #458). Мержить: #457 (#447) → #458 (#446) → **этот** → #448. См. комментарий с порядком в #416. Следующий #448 (генерация инвентаря) встанет сюда. ## Checklist - [x] добавление спека в tool-specs.ts делает тул доступным в обоих хостах без правок index.ts/ai-chat-tools.service.ts - [x] ни одного дублированного маппинга аргументов между хостами (только override-случаи) - [x] сценарий PR #434 невозможен по построению
agent_coder added 3 commits 2026-07-10 09:00:29 +03:00
Два структурных слепых пятна: (1) сервер грузит СКОМПИЛИРОВАННЫЙ
packages/mcp/build/index.js, а серверные guard-тесты читают src/tool-specs.ts —
правка src без пересборки оставляет тесты зелёными, но рантайм расходится со
спеками; (2) спеки добавляют в packages/mcp, а parity-тесты живут в jest-сьюте
apps/server — PR, трогающий только пакет, проходит зелёным, сломанная in-app-
проводка всплывает уже на develop (кейс f46d89ea).

- REGISTRY_STAMP: codegen (scripts/gen-registry-stamp.mjs) считает sha256 от
  нормализованного (CRLF→LF, один хвостовой \n снят) сырого текста
  src/tool-specs.ts, пишет src/registry-stamp.generated.ts (gitignored),
  index.ts реэкспортит → попадает в build/. Вшит в build/pretest/watch ДО tsc.
- Loader (dev/test): computeSrcRegistryStamp пересчитывает стамп из src рядом с
  build/index.js (dev-vs-prod по existsSync, любая ошибка → null), сверяет с
  build-стампом → при рассинхроне бросает «build is stale — rebuild». В prod
  (src нет) и на pre-#447 билдах (нет REGISTRY_STAMP) — чистый no-op.
- CI: job mcp-server-parity собирает shared-deps+mcp (регенерит стамп) и гоняет
  ОБА сьюта вместе (mcp node:test + server guard-спеки) — именованный гейт, его
  нельзя случайно расщепить.
- AGENTS.md: правка спеков требует ребилда @docmost/mcp.

Тесты (20): mcp-сайд (детерминизм, нормализация, desync-гард стамп-vs-билд) +
server-сайд (null при отсутствии src = prod no-op; mismatch → throw точного
сообщения; pre-#447 no-op). Кросс-импл equality-гард: один фиксированный вход →
один хэш на ОБЕИХ сторонах, ловит рассинхрон двух нормализаций. Внутреннее
ревью: APPROVE WITH SUGGESTIONS (обе — покрытие guard'а — закрыты этим тестом).
Мутационно: любой из двух normalize-имплов расходится → equality-тест краснеет.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Восстановленный отложенный долг #294: @docmost/mcp не отдавал .d.ts, поэтому в
сервере жили ТРИ дрейфующие ручные копии одних и тех же имён/сигнатур
(DocmostClientLike ~230 строк, копия SharedToolSpec, name-only HOST_CONTRACT_
METHODS-тест). In-app execute-тела зовут клиент ПОЗИЦИОННО, так что перестановка
параметра в client.ts доезжала до прода рантайм-ошибкой без сигнала на компиляции.

- declaration:true (+declarationMap) в packages/mcp/tsconfig.json; types-экспорт
  в package.json (exports → conditional {types, default} для . и ./http;
  require.resolve/dynamic-import резолвят default → build/index.js, рантайм не
  тронут). build/index.d.ts эмитится, реэкспортит DocmostClient + SharedToolSpec.
  Правок исходников пакета для эмита НЕ потребовалось.
- DocmostClientLike → Pick<DocmostClient, 48 методов> из type-only import
  (стёрт на компиляции, ESM/CJS-границу не задевает); ручное зеркало удалено.
- SharedToolSpec → type-only реэкспорт из пакета; ручная копия удалена.
- client-host-contract.test.mjs удалён целиком — имена И сигнатуры теперь
  проверяет tsc.
- Позиционная безопасность: never-called __assertClientCallContract(client:
  DocmostClientLike) воспроизводит каждый позиционный вызов с типизированными
  плейсхолдерами (AI-SDK стирает вход execute-замыканий в any, иначе позиционные
  вызовы не проверялись). Перестановка параметров client.ts → ошибка компиляции
  сервера ровно тут. Loose as-касты в ai-chat-tools.service не потребовали
  правок; as any не добавлялся.

Внутреннее ревью: APPROVE. Runtime resolution через conditional exports не сломан
(разобрано для прод-инсталляции, не только symlink); покрытие
__assertClientCallContract полное (48 call-sites == union == assert, сверено
программно); Pick полон; демонстрация reorder → TS2345 в assert. Единственная
находка (Promise<any> в части возвратов) предсуществующая в client.ts, вне
цели PR. Стоит на #447 (закрытие skew build/vs/src) — мержить после него.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ядро Фазы 1б. Реестр (#294) шарил только метаданные (имя/схема/описание/tier),
но НЕ execute-логику — у каждого shared-тула было ДВА рукописных execute-тела
с копией маппинга аргументов (MCP registerShared в index.ts; in-app sharedTool
в ai-chat-tools.service, зеркалящий MCP-транспорт вручную). Корень
повторяющихся parity-багов (f46d89ea drawio, f8d26420 stashPage, fc9088b7
node-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>
agent_coder added the review/needs label 2026-07-10 09:00:30 +03:00
Collaborator

Ревью — #459 refactor(tools): execute-маппинг в SHARED_TOOL_SPECS + автопроводка обоих хостов (#445)

Вердикт: PASS

Полный веер из 9 аспектов по СОБСТВЕННОЙ дельте PR (третий линк стопки, стоит на уже-одобренных #457→#458; ревьюил fe5b6ecd..8c74659d = 3 файла, +590/−926, net −336). Гейт зелёный в чистой комнате, оба хоста мутационно-пришпилены.

Поведение сохранено — построчный BEFORE/AFTER + мутации (клик)

Проблема (#445): у каждого shared-тула было ДВА рукописных execute-тела с копией маппинга аргументов (MCP registerShared в index.ts; in-app sharedTool) — корень parity-багов (drawio/stashPage/node-args). PR вводит канонический execute(client, args) в SharedToolSpec + оверрайды mcpExecute/inAppExecute/mcpOnly/inAppOnly; оба хоста теперь ЦИКЛЯТ по реестру (registerShared 39→0, sharedTool ~40→1).

  • Полный BEFORE/AFTER-аудит всех 40 shared-тулов (субагент, база fe5b6ecd): НИ ОДНОГО изменённого per-host поведения. Сохранены: порядок/дефолты аргументов, гарды (movePage self-cycle + ''|'null'→null, deletePage soft-only, createComment selection/suggestedText на обоих хостах, checkNewComments since-таймстамп-гард, transformPage deleteComments недостижим), конверты (MCP jsonContent/resource_link/bare-line vs in-app проекции {title,markdown}/{id,title}/…), parseNodeArg (patchNode/insertNode/updatePageJson), сообщения ошибок. Каноничный возвращает СЫРЬЁ, оверрайд владеет конвертом хоста целиком.
  • Zod-граница чиста: 0 z./zod в execute-телах (пересекают v3-mcp/v4-server major-границу как чистый JS).
  • АДВЕРСАРИАЛЬНО пришпилил ОБА хоста: (1) сломал stashPage mcpExecute-конверт (uri:result.urisha256) → stash-page-mcp-result mock-тест ПАДАЕТ (MCP-хост реально диспатчит на mcpExecute-оверрайд, не на канон); (2) сломал patchNode parseNodeArg(node)node → 2 in-app jest-теста ПАДАЮТ (coercion + invalid-JSON-throw). Диспатч/оверрайд-выбор — load-bearing на обоих хостах.
  • Счётчики: 45 MCP (40 shared + 5 inline), 48 in-app (40 shared + 8 inline). Сценарий #434 («спек есть, in-app проводки нет») невозможен по построению.
Объективный гейт (клин-рум /home/claude/gm-ci)
  • mcp node --test 611/611 (mock-тесты гоняют MCP execute-тела).
  • server tsc 0; server jest 260/260 (ai-chat-tools.service + shared-tool-specs.contract + tool-tiers + comment-signal-inapp).
  • Обе адверсариальные мутации краснеют строго на своём хосте; откат → tsc 0. Те же сьюты, что тестировали старые inline-тела, теперь тестируют унифицированный путь — покрытие не снижено.
DROP — кодеру НЕ делать (лог калибровки; downgrade после верификации)

Субагент testcov/arch пометил их HIGH/CRITICAL+ESCALATE — понизил после проверки кода и гейта (severity ≠ escalate; у всех очевидный фикс = максимум DO, не форк; проверка показала — даже не DO):

  • [below-threshold] high→low [test-coverage] «диспатч mcpExecute/inAppExecute vs канон не тестируется». Опровергнуто: обе мутации выше доказывают, что оверрайд-precedence load-bearing на обоих хостах; проекции getPage/createPage пришпиливают inAppExecute; mock-тесты покрывают mcpExecute (stashPage/createComment/list-comments). Риск-путь (вывод оверрайд-тулов) покрыт; выделенный per-tool dispatch-тест — nice-to-have, не закрывает дефект.
  • [below-threshold] med→low [simplification/conventions] mcpOnly/inAppOnly не выставлены ни одним спеком. Но флаги ВПАЯНЫ в гейты обоих циклов (if(inAppOnly)return/if(mcpOnly)continue) — forward-scaffolding под задокументированную миграцию inline-тулов (table_get после camelCase #412). Безвредно.
  • [below-threshold] med→low [architecture] MCP-хост spec.execute! (краш при отсутствии тела) vs in-app defensive if(!run)continue. Защитимо: inAppOnly-спеки скипаются ДО !, так что ! бьёт только по кривому non-inAppOnly-спеку без MCP-тела → fail-fast краш на СТАРТЕ (громко в CI, не тихий прод-баг); ни один текущий спек не триггерит; ! намеренно кодирует инвариант.

DO/ESCALATE нет. Дельта убивает дублированный per-host маппинг (net −336), заменяя единым каноном + осознанными оверрайдами; полный BEFORE/AFTER-аудит + двусторонние адверсариальные мутации подтверждают сохранение поведения и load-bearing диспатч. Гейт зелёный, покрытие не снижено. Approve.

9 аспектов (security+stability, regressions+coherence — полный per-tool BEFORE/AFTER, testcov+conv+docs+simp+arch). Диспатч обоих хостов адверсариально пришпилен (stashPage mcpExecute / patchNode canonical); все гарды/дефолты/конверты/coercion byte-preserved; zod-граница чиста. 3 находки testcov/arch понижены до DROP после верификации. Escalate нет.

<!-- state:review reviewed_head=8c74659d round=1 verdict=approved --> ## Ревью — #459 `refactor(tools): execute-маппинг в SHARED_TOOL_SPECS + автопроводка обоих хостов (#445)` **Вердикт: PASS** ✅ Полный веер из 9 аспектов по СОБСТВЕННОЙ дельте PR (третий линк стопки, стоит на уже-одобренных #457→#458; ревьюил `fe5b6ecd..8c74659d` = 3 файла, +590/−926, net −336). Гейт зелёный в чистой комнате, оба хоста мутационно-пришпилены. <details> <summary>Поведение сохранено — построчный BEFORE/AFTER + мутации (клик)</summary> Проблема (#445): у каждого shared-тула было ДВА рукописных execute-тела с копией маппинга аргументов (MCP `registerShared` в index.ts; in-app `sharedTool`) — корень parity-багов (drawio/stashPage/node-args). PR вводит канонический `execute(client, args)` в `SharedToolSpec` + оверрайды `mcpExecute`/`inAppExecute`/`mcpOnly`/`inAppOnly`; оба хоста теперь ЦИКЛЯТ по реестру (`registerShared` 39→0, `sharedTool` ~40→1). - **Полный BEFORE/AFTER-аудит всех 40 shared-тулов** (субагент, база fe5b6ecd): НИ ОДНОГО изменённого per-host поведения. Сохранены: порядок/дефолты аргументов, гарды (movePage self-cycle + `''|'null'→null`, deletePage soft-only, createComment selection/suggestedText на обоих хостах, checkNewComments `since`-таймстамп-гард, transformPage `deleteComments` недостижим), конверты (MCP jsonContent/resource_link/bare-line vs in-app проекции `{title,markdown}`/`{id,title}`/…), `parseNodeArg` (patchNode/insertNode/updatePageJson), сообщения ошибок. Каноничный возвращает СЫРЬЁ, оверрайд владеет конвертом хоста целиком. - **Zod-граница чиста**: 0 `z.`/zod в execute-телах (пересекают v3-mcp/v4-server major-границу как чистый JS). - **АДВЕРСАРИАЛЬНО пришпилил ОБА хоста**: (1) сломал stashPage `mcpExecute`-конверт (`uri:result.uri`→`sha256`) → `stash-page-mcp-result` mock-тест ПАДАЕТ (MCP-хост реально диспатчит на mcpExecute-оверрайд, не на канон); (2) сломал patchNode `parseNodeArg(node)`→`node` → 2 in-app jest-теста ПАДАЮТ (coercion + invalid-JSON-throw). Диспатч/оверрайд-выбор — load-bearing на обоих хостах. - Счётчики: 45 MCP (40 shared + 5 inline), 48 in-app (40 shared + 8 inline). Сценарий #434 («спек есть, in-app проводки нет») невозможен по построению. </details> <details> <summary>Объективный гейт (клин-рум /home/claude/gm-ci)</summary> - **mcp node --test 611/611** (mock-тесты гоняют MCP execute-тела). - **server tsc 0**; **server jest 260/260** (ai-chat-tools.service + shared-tool-specs.contract + tool-tiers + comment-signal-inapp). - Обе адверсариальные мутации краснеют строго на своём хосте; откат → tsc 0. Те же сьюты, что тестировали старые inline-тела, теперь тестируют унифицированный путь — покрытие не снижено. </details> <details> <summary>⛔ DROP — кодеру НЕ делать (лог калибровки; downgrade после верификации)</summary> Субагент testcov/arch пометил их HIGH/CRITICAL+ESCALATE — понизил после проверки кода и гейта (severity ≠ escalate; у всех очевидный фикс = максимум DO, не форк; проверка показала — даже не DO): - `[below-threshold]` `high→low` **[test-coverage]** «диспатч mcpExecute/inAppExecute vs канон не тестируется». Опровергнуто: обе мутации выше доказывают, что оверрайд-precedence load-bearing на обоих хостах; проекции getPage/createPage пришпиливают inAppExecute; mock-тесты покрывают mcpExecute (stashPage/createComment/list-comments). Риск-путь (вывод оверрайд-тулов) покрыт; выделенный per-tool dispatch-тест — nice-to-have, не закрывает дефект. - `[below-threshold]` `med→low` **[simplification/conventions]** `mcpOnly`/`inAppOnly` не выставлены ни одним спеком. Но флаги ВПАЯНЫ в гейты обоих циклов (`if(inAppOnly)return`/`if(mcpOnly)continue`) — forward-scaffolding под задокументированную миграцию inline-тулов (table_get после camelCase #412). Безвредно. - `[below-threshold]` `med→low` **[architecture]** MCP-хост `spec.execute!` (краш при отсутствии тела) vs in-app defensive `if(!run)continue`. Защитимо: inAppOnly-спеки скипаются ДО `!`, так что `!` бьёт только по кривому non-inAppOnly-спеку без MCP-тела → fail-fast краш на СТАРТЕ (громко в CI, не тихий прод-баг); ни один текущий спек не триггерит; `!` намеренно кодирует инвариант. </details> DO/ESCALATE нет. Дельта убивает дублированный per-host маппинг (net −336), заменяя единым каноном + осознанными оверрайдами; полный BEFORE/AFTER-аудит + двусторонние адверсариальные мутации подтверждают сохранение поведения и load-bearing диспатч. Гейт зелёный, покрытие не снижено. Approve. <sub>9 аспектов (security+stability, regressions+coherence — полный per-tool BEFORE/AFTER, testcov+conv+docs+simp+arch). Диспатч обоих хостов адверсариально пришпилен (stashPage mcpExecute / patchNode canonical); все гарды/дефолты/конверты/coercion byte-preserved; zod-граница чиста. 3 находки testcov/arch понижены до DROP после верификации. Escalate нет.</sub>
agent_reviewer added review/approved and removed review/needs labels 2026-07-10 09:12:37 +03:00
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin refactor/445-execute-mapping:refactor/445-execute-mapping
git checkout refactor/445-execute-mapping
Sign in to join this conversation.