# Включение роутера-наставника — пошаговая инструкция для владельца

**Дата:** 2026-06-11. **Кодовая фраза эпика:** «роутер-наставник».
**Состояние на момент написания:** Фаза B собрана полностью (модули A, B, D, E, C, C2, F) +
финревью 5-скил (FR-1..FR-4, VA-1..VA-3 починены). Коммиты на main: `...437f4f8e` (радары F) +
`57b811b3` (финревью). Регрессия tools: 3716 passed + 2 skipped, 0 падений.
**Источник истины контрактов:** план C2 `2026-06-10-router-mentor-subplan-c2-integration-manifest.md`,
раздел W7 (таблица инъекций).

**Главное простыми словами:** наставник — это советник, который читает каждый мой план
и спорит со мной ДО того, как я начну что-то менять. Сейчас он собран, но спит:
не подключён ни к настройкам, ни к ИИ. Пока ты не пройдёшь этапы ниже — в твоей
работе ничего не меняется.

---

## Этап 1. Строительная мини-сессия (делаю Я, ты только говоришь «вперёд»)

**Зачем:** у собранного оркестратора (`tools/on-plan-write.mjs`) нет «входной двери» —
файла-обёртки, который Claude Code может запускать как хук. Это проверено по коду:
в `on-plan-write.mjs` нет `main()`, ни один существующий хук его не импортирует.
Также по контракту F-C2-4 (план C2, W7) при активации строится персист журнала
переговоров (файл `~/.claude/runtime/mentor-journal-<сессия>.jsonl`, atomic-запись).

**Что делать тебе:**

1. Открой новую сессию Claude (или продолжи текущую).
2. Скажи: **«роутер-наставник. Строим активационную обёртку по runbook
   2026-06-11-router-mentor-activation-runbook.md, этап 1»**.
3. Дальше — обычная наша цепочка: я спрошу согласование, буду просить эскейпы
   на каждый файл, гонять тесты RED→GREEN, в конце дам коммит-блок.

**Что построит эта сессия (по контрактам W7, ничего лишнего):**

- обёртку-хук (рабочее имя `tools/enforce-mentor-on-plan-write.mjs`): читает событие,
  достаёт шаги плана, грузит журнал задачи (F-C2-4: загруженная цепь, не пустой список),
  зовёт `onPlanWrite` с реальными инъекциями из таблицы W7 — `llmCall` через
  `callAnthropicAPI` (та же инфраструктура, что у классификатора/судьи), `graphSection`
  из B, `readFileImpl` через `path.resolve(REPO_ROOT, f)` (C-3);
- персист журнала переговоров (atomic temp→rename, как у task-id F-C1);
- рубильник: наставник работает ТОЛЬКО при флаге + ключе (как у судьи: без флага — $0,
  ноль вызовов ИИ). Точное имя флага определится на стройке и будет вписано сюда.

**Результат этапа:** в этом runbook появятся точные строки для этапов 2-3
(сейчас вместо них — пометки «даст Этап 1»).

---

## Этап 2. Ключ и флаг (делаешь ТЫ, 2 минуты) — СТРОКИ ГОТОВЫ (Этап 1 выполнен 2026-06-12)

**Зачем:** наставник звонит ИИ за свой счёт только когда ты явно разрешил.

**«Оба строго» (решение владельца 2026-06-12):** судья и наставник едят ТОЛЬКО со своих
ключей; общий `ROUTER_LLM_KEY` остаётся классификатору и НЕ подхватывается фолбэком.
Раскладка переменных (все — User variables, через `rundll32 sysdm.cpl,EditEnvironmentVariables`):

| Переменная | Кто на ней | Значение |
|---|---|---|
| `ROUTER_LLM_KEY` | классификатор (служебные вызовы) | свой `sk-…` (был) |
| `ROUTER_JUDGE_LLM_KEY` | судья (строго; нет → судья unavailable, печати нет) | свой `sk-…` |
| `ROUTER_MENTOR_LLM_KEY` | роутер-наставник (строго; нет → inert $0) | свой `sk-…` |
| `ROUTER_MENTOR_SEAM_ENABLED` | рубильник наставника | ровно `1` |
| `ROUTER_LLM_BASE_URL` | оператор API для ВСЕХ (классификатор/судья/наставник) | базовый URL оператора, напр. `https://api.aitunnel.ru`; не задан → захардкоженный proxyapi-дефолт |

**Смена оператора (2026-06-12, переход на aitunnel):** транспорт один на всех
(`callAnthropicAPI`), оператор переключается переменной `ROUTER_LLM_BASE_URL`
(env-фолбэк зашит в сам дефолт транспорта — судья/наставник зовут его без
`baseUrl` и до фикса были прибиты к proxyapi). Ключи берутся у оператора
(aitunnel: `sk-aitunnel-…`, заголовок Bearer поддержан, модель
`claude-sonnet-4-6` в каталоге под тем же id). После правки env — полный
перезапуск VS Code.

⚠️ Флаг — ровно `1` (не `true`, без пробелов) — иначе наставник ТИХО останется
выключенным (W-4: зеркало судьи, опечатка = inert без ошибки). Не путать с судейской
`ROUTER_MENTOR_JUDGE_ENABLED` — это разные рубильники.
После правок — перезапустить VS Code (чтобы окружение подхватилось).

**Проверка:** без флага/своего ключа наставник молчит и не тратит ни копейки
(`tools/mentor-gate-config.mjs`: флаг=1 И `ROUTER_MENTOR_LLM_KEY`, иначе $0); судья без
`ROUTER_JUDGE_LLM_KEY` пишет в лог `judge_unavailable` с именем нужной переменной.

---

## Этап 3. Регистрация хука в настройках (делаешь ТЫ, 5 минут) — БЛОК ГОТОВ

**Зачем:** Claude Code зовёт хуки только из `settings.json` — мне этот файл
менять запрещено (Read-deny), поэтому строку вставляешь ты.

1. Открой `.claude/settings.json` (в корне проекта).
2. В секцию `"PostToolUse"` (по образцу соседних хуков) добавь блок:

   ```json
   {
     "matcher": "Write",
     "hooks": [
       { "type": "command", "command": "node tools/enforce-mentor-on-plan-write.mjs", "timeout": 60 }
     ]
   }
   ```

   (timeout 60 — наставник ждёт ответа ИИ; хук сам фильтрует «только план-файлы»
   и НИКОГДА не блокирует — он производитель, блокирует печать freeze-gate в судье.)
3. Сохрани, перезапусти Claude-сессию.

**Откат в одну строку:** удалить этот блок из settings.json (или выключить флаг
Этапа 2) — наставник снова спит. Печать планов при выключенном наставнике идёт
как раньше (гейт подключается только при активном рубильнике — не кирпич).

---

## Как выглядит НОРМАЛЬНЫЙ цикл после включения (W-1 — прочитай, чтобы не испугаться)

Судья проверяет план ДО записи, а наставник выносит вердикт ПОСЛЕ записи. Поэтому:

1. Claude пишет план (с блоками `steps-json` И `verified-context-json` — без них
   печать отклоняется, это дисциплина «не строй на непроверенном»).
2. **Первая запись:** печать отклонена с причиной «mentor freeze-gate: вердикт не
   привязан…» — это НЕ поломка: наставник ещё не успел высказаться по этой версии.
   Сразу после записи наставник выносит вердикт (виден в журнале).
3. Claude правит план по рекомендации наставника (или повторяет запись без правок,
   если вердикт хороший) — **вторая запись той же версии печатается**: вердикт
   совпал по хешу (binding), контекст-цитаты сверены побайтово.

Это и есть переговорный круг «план → вердикт → правка → печать». Непроверенный
план не печатается НИКОГДА — направление всегда к строгости.

**Формат блока контекста в плане** (Claude кладёт сам, тебе — только знать, как
выглядит): fenced-блок ` ```verified-context-json ` с массивом записей
`{id, kind: "EXTRACTED"|"INFERRED", claim, ref: "файл:строка", anchor: "подстрока-якорь",
derivation_ref}`. EXTRACTED-якоря машина сверяет с живыми файлами при печати.

**Известное ограничение первой сборки:** наставник пока судит без карты районов
графа (`graphSection = null` → в его промпте явный маркер «КАРТА РАЙОНОВ
ОТСУТСТВУЕТ», F-C6 — не тихо). Подключение живого графа B — отдельный шаг после
обкатки, по согласованию.

---

## Этап 4. Пробный прогон — smoke (вместе, 10 минут) — ✅ ПРОЙДЕН 2026-06-12

> Итог прогона 12.06: производитель работает end-to-end (вердикт `ok:true,wired:true`,
> `confidence:0.96`, binding `plan_hash` сошёлся; журнал переговоров пишется).
> По дороге закрыты 3 дефекта (TDD): (1) тихий catch в `mentor-verdict.mjs` не
> показывал причину сбоя транспорта; (2) `ROUTER_LLM_BASE_URL` уважал только
> классификатор — судья/наставник были прибиты к захардкоженному прокси;
> (3) промпт «статус+замечание» провоцировал массив объектов, а валидатор
> (F-C3/М1-М4) требует массив строк — контракт типа теперь в промпте явно.
> Пункт «печать без вердикта не проходит» (freeze-gate) проверить нельзя до
> активации судьи в этом репо (Фаза 8): печать живёт в пути судьи, судья inert —
> `judge-verdicts.jsonl` отсутствует, `frozen-plan-*` не создаётся вовсе
> (направление безопасное: ничего не печатается).

1. Открой новую сессию Claude, дай любую маленькую задачу с планом
   (например: «напиши план: поправь опечатку в docs/bugs.md»).
2. **Что ты должен увидеть, если всё работает:**
   - я пишу план → срабатывает хук → наставник возвращает вердикт
     (разбор плана по пунктам: `plan_points_addressed` / `reasoning` /
     `recommendation` / `confidence`);
   - появился файл журнала `~/.claude/runtime/mentor-journal-<сессия>.jsonl` —
     в нём строка переговоров с обоснованием;
   - печать плана без содержательного вердикта наставника НЕ проходит
     (freeze-gate: binding к хешу плана + проверенный контекст, FR-1).
3. **Если что-то не так:** скажи мне «smoke не прошёл, вот вывод» и приложи
   текст ошибки — разберём по systematic-debugging. Аварийный выход
   (FLOOR-ESCAPE через вопрос) работает всегда — заклинить тебя наставник не может.

---

## Этап 5. Обкатка (первые 2-3 дня)

- Наставник на каждый план — это новые вызовы ИИ. Цена за вызов сопоставима
  с классификатором/судьёй; если покажется дорого или шумно — выключи флаг,
  обсудим калибровку (прецедент: судью калибровали после over-block,
  commit `c9b9efd6`).
- Смотри на вердикты: если наставник «придирается не туда» — это материал
  для настройки промпта, говори мне.
- Открытые хвосты (не потеряны, записаны в плане C2/W7 и handoff):
  - **E-S1** — производители «цель достигнута» (владелец/судья gate-3) для
    закрытия петли — отдельное решение с тобой;
  - **O9** — лимит доли INFERRED в контексте — открытый вопрос спеки (§5.5);
  - **VA-3-wiring** — при дозапросе района контроллер обязан явно показывать
    пустую деталь («0 узлов»), это дисциплина, не код.

---

## Шпаргалка «если всё забыл»

| Хочу | Делаю |
|------|-------|
| Продолжить эпик | Новая сессия + фраза «роутер-наставник» |
| Включить наставника | Этапы 1→2→3→4 сверху по порядку |
| Выключить наставника | Убрать флаг (Этап 2) ИЛИ строку хука (Этап 3) |
| Понять, почему заблокировано | Читать причину в сообщении блока; аварийно — FLOOR-ESCAPE |
| Где контракты/детали | План C2, раздел W7 + спека R6.3 |
