# Дизайн: подключение детерминированного судьи дисциплины навыков

## Проблема

Модуль `tools/domain-skill-discipline.mjs` реализует проверку «рекомендованный/объявленный
навык реально вызван (по журналу M1) → иначе блок (K2)», но **ни один живой хук его не
импортирует** (подтверждено grep по импортам: единственный live-импорт — `skill-contract-registry`
→ `skill-contract`; ядро дисциплины не подключено нигде). Поэтому план может объявить навык в
`skills-json` и не вызвать его — никто не остановит. Наставник (LLM) судит *выбор* навыков при
печати; детерминированной проверки *исполнения* в живой системе нет.

Вторая половина проблемы — дедлок: навык, объявленный в плане (напр. `claude-md-management`),
сейчас под стеной **нельзя вызвать** — `enforce-supreme-gate` пускает только литеральные шаги
(op/object) и узкий фикс-список seed (`SEED_SKILLS`); вызов объявленного навыка ни туда, ни сюда
не попадает → блок. Значит «обязан вызвать» физически невыполнимо.

## Вход дисциплины: опломбированный план

Источник истины «какие навыки обязательны» — **`skills-json` опломбированного плана**
(`frozenPlan`). Обоснование: печать неизменяема и подписана; её `skills-json` уже прошёл наставника
(Р8 сверил объявленное с рекомендацией роутера) — то есть это уже *одобренный* набор. Стена и так
грузит `frozenPlan` (`loadFrozenPlan`), отдельных источников не нужно. Хитрость «объявлю пусто»
ловит наставник на печати, а не этот судья.

## Часть A — стена пускает объявленные навыки

`enforce-supreme-gate` (`decide`/`decideMode`) получает новое разрешающее правило: вызов Skill,
чьё имя входит в `skills-json` активного `frozenPlan`, **разрешён** (наравне с seed), пока этот
план активен. Парс `skills-json` — существующий `parsePlanSkills` (`tools/plan-skills.mjs`).
Сопоставление по суффиксу имени навыка (как `skillSuffix` для seed). Это снимает дедлок:
объявил → можешь вызвать.

Границы: правило действует только при наличии валидного `frozenPlan`; в разговорном режиме —
прежнее поведение (только seed/observe/authoring). Вызов навыка указатель шага НЕ двигает (это не
шаг плана, а его обеспечение).

## Часть B — детерминированный судья исполнения

Новый PreToolUse-хук-обёртка `enforce-domain-skill-discipline` поверх готового
`domain-skill-discipline.mjs`. Матчер — мутирующие инструменты (`Edit|Write|MultiEdit|Bash`).
Логика: перед **первым мутирующим** шагом активного плана сверить объявленные в `skills-json`
печати навыки с журналом фактических вызовов (`loadJournal` + `extractSkillCalls`). Есть
объявленный, но не вызванный навык → блок с перечнем недостающих (текст из `domainCallDiscipline`).
fail-CLOSE (сбой → блок). Рубильник как у прочих enforce-хуков.

«Первый мутирующий» — простейшая надёжная точка: если навык не вызван к моменту первой реальной
правки, дальше уже поздно. (Развитие — привязка «навык ↔ шаг» через `skill-contract` needs/produces;
вне scope первой версии, YAGNI.)

## Поток данных

1. План опломбирован → `frozenPlan.skills-json` = обязательные навыки.
2. Контроллер вызывает навык → стена пускает (часть A) → `enforce-skill-journaler` пишет вызов в журнал M1.
3. Контроллер идёт на первый мутирующий шаг → судья (часть B) сверяет skills-json печати с журналом
   → все вызваны → allow; есть невызванный → block.

## Регистрация и безопасность реализации

Регистрация нового хука в `settings.json` — шаг владельца. **Критично:** часть A правит ЖИВОЙ код
стены (`enforce-supreme-gate.mjs`). Каждый вызов инструмента поднимает свежий процесс хука, читающий
текущий файл; битая правка → стена уходит в fail-CLOSE и кирпичит сессию (саму правку тоже не
сделать). Поэтому правка стены — при СНЯТОЙ стене (maintenance) либо предельно осторожно, с прогоном
тестов до возврата стены. Часть B (новый отдельный файл) этого риска не несёт.

## Тестирование (TDD)

- Часть A: `decide`/`decideMode` пускает Skill из `frozenPlan.skills-json`; не пускает Skill вне
  списка; в разговорном режиме (нет плана) — прежнее поведение; указатель не двигается.
- Часть B: `decide` обёртки блокирует мутирующий при объявленном-невызванном навыке; пропускает,
  когда все вызваны; пропускает при пустом `skills-json`; fail-CLOSE на сбой журнала.
- Регрессия tools-only зелёная, 0 регрессий.
