# Роадмап эпика «роутер-наставник» — состояние на 2026-06-12

**Кодовая фраза:** «роутер-наставник». **Статус:** Фаза 8 переезда М7 ЗАВЕРШЕНА,
Этап 5 обкатки пройден (оба 2026-06-12). Судья и наставник ЖИВЫЕ в main (судья — live-block).
Двухуровневые переговоры + арбитраж владельца СОБРАНЫ (7 волн, 3800 GREEN). Оператор API — aitunnel.
Этот файл — единый список оставшихся работ эпика; вычёркивать по мере закрытия.

## Печать M7 — дедлок снят + наблюдаемость (2026-06-13, закоммичено `d786c273`)

Печать плана больше не зависает. Цикл «спека→печать→план→печать→правка» замыкается.

- **Наблюдаемость (была дыра №1):** каждая запись плана/спеки пишет в
  `~/.claude/runtime/seal-attempts.jsonl` — судья активен? вердикт wired? печать пыталась?
  встала? и ДОСЛОВНУЮ причину провала. Модуль `tools/seal-log.mjs`, проводка в `runJudgeTurn`.
- **Корень (доказан логом):** печать ПЛАНА падала на freeze-gate — судья (Pre) грузил вердикт
  наставника, а тот рождается в Post (после судьи) → вердикт чужого плана (stale, нах.F4).
  Спека печаталась нормально; прошлые «спека не печаталась» = перемежающийся degraded судьи.
- **Фикс:** судья (Pre) теперь лишь СОХРАНЯЕТ подписанный GO (`tools/judge-go-store.mjs`,
  домен `JUDGE_GO` в `receipt-sign.mjs`); печать плана делает наставник (Post) СВЕЖИМ вердиктом
  (`tools/post-seal.mjs`). Правлены `enforce-judge-gate.mjs` + `enforce-mentor-on-plan-write.mjs`.
- **Доказательство:** сквозной прогон реальных хуков → `frozen-plan` родился ВПЕРВЫЕ.
  Регрессия tools-only **3820 GREEN** (+20 тестов). Все правки в `d786c273`.
- **Метод (важно для будущих ремонтов машины):** стену опускали в **maintenance-режим**
  (минимальный `settings.json` с `"hooks": {}` + права на npx/node/git) — иначе discipline-код
  править нельзя (LAW-детектор), а цикл печати дедлочит сам себя (нельзя запечатать план
  ремонта через сломанную печать). Чинить машину — ТОЛЬКО со снятой стеной + TDD.

**Хвосты печати:**

- [ ] **🔴 ВЕРНУТЬ M7** (`settings - Copy.json` → `settings.json` + рестарт) — стена сейчас
  ОПУЩЕНА (maintenance). До возврата дисциплины нет. Шаг владельца.
- [ ] Финальный живой прогон ПОД стеной: спека+план+Edit проходит весь цикл (sanity).
- [ ] Перемежающийся degraded судьи (`wired:false` в `seal-attempts.jsonl`) — редкий сбой
  транспорта/таймаут. Теперь ВИДЕН. Мелкая задача: retry / поднять таймаут судьи 60→120с.
- O9 был пробником цикла (не реализован; файлы-пробники удалены).

## Завершено (переезд + обкатка)

- [x] ~~**Этап 5 runbook — обкатка наставника.**~~ Пройдена (владелец, 2026-06-12):
  переезд состоялся, наставник и судья обкатаны в live-block в main.

## Фаза 8 переезда М7 — ЗАВЕРШЕНА 2026-06-12

**Переезд состоялся:** М7-кодовая база на main, М1–М6 зарегистрированы, зоопарк уволен,
судья (`enforce-judge-gate`) и наставник (`enforce-mentor-on-plan-write`) ЖИВЫЕ в main
(судья — live-block, реально блокирует запись планов/спек на NO-GO). Worktree `brainrepo`
заморожен (стейл-снимок 09.06). Все подпункты ниже закрыты.

- [x] Тест-гейт §9.2 перед переездом. **Пред-условие выполнено 12.06 ~11:08: 3754 passed
  - 2 skipped (полный tools-only прогон).** ⚠️ Пусковой рецепт регрессии — ТОЛЬКО
  `npx vitest run --root app --config vitest.config.tools.mjs` (npx-кэш, vitest 4.1.8);
  запуск через `node app/node_modules/vitest/vitest.mjs` ПАДАЕТ всеми suites — lock-пин
  vitest 4.1.5 имеет баг на out-of-root тестах (`../tools/*`), «починка npm ci» не лечит.
  Опционально в Ф8: поднять пин vitest до ^4.1.8 в `app/package.json` + lock.
- [x] Cherry-pick `d1ad4e85` (sealedPlanCoversEdit live-wiring). **Эффект в main verified
  (grep `enforce-normative-content-rules.mjs` 12.06):** `DISCIPLINE_SOURCE_RE` (стр. 46)
  покрывает `enforce-`/`judge-`/`floor-` + `sealedPlanCoversEdit` проброшен в `decide()`
  (стр. 66/155/186/287–299). Правки `tools/enforce-*.mjs` ad-hoc → ЗАКОН → escape М6;
  build-loop под запечатанным планом → КАРТА.
- [x] ~~Регистрация М1-М6 в `.claude/settings.json`~~ (выполнено владельцем 12.06).
- [x] ~~Увольнение v4-зоопарка (§10): старые enforce-хуки заменяются машинами.~~
- [x] ~~План отката.~~ Бэкап `.claude/settings.json.bak-phase8-2026-06-12-flip` снят.

Фаза 8 попутно закрыла занозы:

- судья ожил в основном репо → печать планов и «зубы» freeze-gate наставника открыты;
- зоопарковые `llm-judge-*` со своими хардкодами оператора ушли с увольнением зоопарка.

⚠️ Остаётся отдельным хвостом (не закрылось Фазой 8): сломанный канал git-одобрений
(`enforce-askuser-answer-parser` не пишет `askuser-decisions-<sess>.jsonl` — обнаружено
12.06, коммиты доделывали вручную в терминале владельца).

## Шаг владельца (до или после Фазы 8)

- [ ] **ИИ-проводка судьи М4** (пункт А памяти М7): реальный llm-транспорт в
  `runJudgeGate` + флаг + обкатка shadow → block. Ключ `ROUTER_JUDGE_LLM_KEY` уже стоит.

## После обкатки наставника (по очереди, отдельные согласования)

- [ ] **Живой граф B** — снять маркер «КАРТА РАЙОНОВ ОТСУТСТВУЕТ»
  (`graphSectionImpl: () => null` в `enforce-mentor-on-plan-write.mjs` →
  parseGraph → buildDistrictMap → staleness).
- [ ] **E-S1** — производители «цель достигнута» (владелец/судья gate-3) для закрытия петли.
  ⚠️ Чистый хелпер `loopTerminationDecision` готов (`loop-termination.mjs`), но живой wiring
  в петлю — sub-plan C, не воткнут.
- [ ] **O9** — лимит доли INFERRED в проверенном контексте (спека §5.5, открытый вопрос).
- [x] ~~**VA-3-wiring**~~ — СДЕЛАН (финревью 3/5, `mentor-seam.mjs:116–122`): при непустой
  карте неизвестный/выдуманный район → отказ (VA-3), молчаливой эскалации к пустой детали нет.

## Мелкие хвосты (15-30 мин, любая сессия)

- [x] ~~Хардкод proxyapi в `tools/observer-self-assessment-api.mjs:109`~~ — уже закрыт
  коммитом `95bb6b17` (строка 151: `process.env.ROUTER_LLM_BASE_URL || DEFAULT_BASE_URL`;
  захардкоженный proxyapi остался только дефолт-фолбэком, как у транспорта).
- [x] ~~Smoke-план: поле «Связано:» в шаблон `docs/bugs.md`~~ — выполнено при smoke
  Этапа 4 (строка 17 шаблона). ⚠️ Сам `docs/bugs.md` untracked — закоммитить журнал
  при ближайшем docs-коммите.
- [ ] Memory-записи эпика: итог Этапа 4 + смена оператора aitunnel + сломанный
  askuser-парсер (нужен ход с активным claude-md-management + `direct:memory-sync`).
- [ ] `/graphify --update` — граф помечен needs_update post-commit-хуком.

## Вне эпика (отдельная тема, здесь только напоминание)

- GitHub-аккаунт suspended → 305 локальных коммитов без пуша; до решения —
  бэкап `git bundle create ... --all`.

## Инцидент 12.06 утро (разобран ревизией 5-скил цепочки, та же дата)

Фаза-8-сессия зашла по устаревшему handoff #5 в стейл-worktree `brainrepo` (база 09.06,
−21 коммит к main) → впечатление «всё потёрто» (файлов наставника 10–12.06 там нет).
Следы: 08:13 откат `.claude/settings.json` к состоянию 09–10.06 (оба «бэкапа» phase8-06-10/06-12
побайтово равны текущему — довосстановочное состояние НЕ сохранилось, регистрация хука
наставника слетела); 08:45 коммит `d1ad4e85` на стейл-базе; 08:48 merge в main не прошёл
(конфликт, main не сдвинулся). **Потерь в git нет** — main `95bb6b17` цел, рефлог чистый.
Восстановлено 12.06: хук наставника возвращён владельцем в PostToolUse (Этап 3 runbook);
вход Фазы 8 переписан выше; handoff #5 получил баннер «устарело». `d1ad4e85` ждёт Фазу 8.

## Двухуровневые переговоры + арбитраж владельца — СОБРАНЫ 2026-06-12 (вечер)

Спека `2026-06-12-router-mentor-two-level-negotiation-design-v2.md` (якорная, с §12) +
планы реализованы TDD, 7 волн на main, регрессия tools-only **3800 GREEN**:

| Коммит | Волна |
|---|---|
| `5a7370df` | фикс producer Windows (`execFileSync npx` → `execSync` — verify-gate был неудовлетворим на Windows) |
| `48e8111c` | В1 — счётчик NO-GO наставника (L1, зеркало `bumpJudgeNoGo`) |
| `6c6d6d2e` | В2 — `arbitration-card.mjs` (карточка) |
| `8918190b` | В3 — `negotiation-section.mjs` (парсер раздела «Переговоры») |
| `eca9be46` | В4-5 — `objection-format.mjs` (форматтеры судья+наставник; имя без `judge-` префикса — иначе discipline-source гейт) |
| `9f939cd4` | В6 — проводка: эскалация судьи 3 NO-GO → карточка (правка ЖИВОГО `enforce-judge-gate.mjs`) |
| `4073164d` | В7 — проводка наставника: surface замечания + счётчик + эскалация → карточка (правка ЖИВОГО `enforce-mentor-on-plan-write.mjs`) |
| `cefb1b96` | docs — спека-v2 + планы |

**Живо в main сейчас** (судья live-block): замечание на блок-сообщении (П1), 2 независимых счётчика NO-GO (потолок 3), карточка арбитража на 3-м отказе (дословные обе стороны + 3 выбора + «объясни подробнее»), решение владельца — escape/вейвер.

**Находки про машину (важно для эпика):**

- **Escape на кириллических `write:`-путях НЕ честится** (перекодировки NFC через terminal→paste→AskUser→parser). Из-за этого правки discipline-source (`enforce-*`) волн 6-7 применял ВЛАДЕЛЕЦ в терминале (ungated node-аппликатором), а не Claude через escape. Нужен рабочий sealed-plan build-loop ИЛИ починка escape-токена под не-ASCII пути.
- **Печать (seal) флапает** — `frozen-artifact-<sess>` в сессии не родился (судья давал degraded-GO?); seal-провал **нигде не логируется** (дыра наблюдаемости — добавить лог попыток печати).
- **router-gate недетерминирован** — то требует `approve_git_operation` на git add/commit, то нет; этот канал не честился (а `floor_escape` для `bash:` — честился). Коммиты доделывали в терминале владельца.
- **lefthook-стэш конфликтует на авто-churn `STATUS.md`** — обходили `git restore`/стейджем авто-файлов.
- **cspell засорён обрезками** (`econo`/`критерийного`/`доказательст`) — корень: observer обрезает слова в STATUS.md.
- **producer Windows-баг ПОЧИНЕН** (`5a7370df`).

**Хвосты:** end-to-end смоук проводки (реальные 3 NO-GO → карточка) не делался; **`supreme-gate` был СНЯТ владельцем для inline-сборки — вернуть в settings.json.**

## Закрыто 2026-06-12 (для истории)

- ✅ Runbook Этап 4 smoke: наставник end-to-end (вердикт wired:true, confidence 0.96,
  binding plan_hash, журнал переговоров).
- ✅ 3 TDD-фикса: деталь ошибки транспорта в catch (`mentor-verdict.mjs`),
  env `ROUTER_LLM_BASE_URL` в дефолте `callAnthropicAPI` (смена оператора),
  контракт «массив строк» для `plan_points_addressed` в промпте (F-C3).
- ✅ Смена LLM-оператора proxyapi → aitunnel (env: base URL + 3 ключа), runbook-блок.
- ✅ Регрессия tools 3753 GREEN; commit `328ac009` на main.

## Наблюдаемость degraded судьи укреплена 2026-06-13 (maintenance)

Хвост из секции «Печать M7» — «перемежающийся degraded судьи (`wired:false`)» — разобран
по `superpowers:systematic-debugging` (root cause ДО фиксов). **Действующего бага НЕ нашли:**
env `ROUTER_JUDGE_LLM_KEY` SET, флаг=1, режим=block; `judge-verdicts.jsonl` 28/28 вердиктов
(0 `judge_unavailable`); 2 degraded-строки в `seal-attempts.jsonl` — **без парного WARN и без
времени** (`at:null` везде), т.е. два append-only лога не сверяемы и противоречат друг другу.
Дедукция: degraded-записи «быстрые» (зависание убило бы хук) → профиль `!apiKey`, не флапающий
транспорт; транспорт и так ретраит 4× + 30с/попытка. Гипотеза handoff «retry / таймаут 60→120с»
**не подтверждена** → таймаут НЕ трогали.

Вместо неподтверждённого фикса — закрыли слепоту (скиловое «add monitoring for future
investigation»), TDD, **под maintenance** (стена опущена):

- `callJudgeModel` различает причину: `no_key` (мгновенно, $0) vs `transport_error` +
  `errorType` из `classifyLLMError` (timeout/http_5xx/http_4xx/econnreset/other);
- причина протекает: `runJudgeGate` вердикт → `warnJudgeUnavailable` (+`cause`/`error_type`/`at`)
  и seal-запись (+`cause`/`error_type`/enrich degraded-reason);
- `at`-timestamp: `main()` передаёт `nowMs: Date.now()` → seal/verdict/warn перестали быть
  `at:null` (логи стали сверяемы по времени; `buildSealEntry` уже форвардил `nowMs`).

Файлы: `tools/seal-log.mjs`, `tools/enforce-judge-gate.mjs`. Тесты: +9 (7 judge-gate, 2 seal-log);
2 пред-существующих exact-match `toEqual({unavailable:true})` приведены к новому контракту.
**Регрессия tools-only 3829 GREEN** (база 3820), 0 регрессий. Коммит — шаг владельца (не сделан).

**Намеренно НЕ тронуто (TDD-минимализм):** `at` для записи `gate2-post` в
`enforce-mentor-on-plan-write.mjs` (потребует своего RED-теста; degraded рождается на judge-side,
цель покрыта) — опциональный follow-up. Транспортный таймаут/retry — не менялись (нет данных).
