# Router discipline overhaul — машиночитаемый реестр + hard-enforcement

**Дата:** 2026-05-23
**Связано:** ADR-011 (brain governance), spec `2026-05-19-observer-factor-analysis-design.md`, Pravila §12/§14/§15/§16, PSR_v1 R0-R15, [[feedback_superpowers_hard_rule]], [[feedback_feature_via_writing_plans]]
**Brainstorming:** этот документ — итог brainstorming-диалога 23.05.2026 (13:00-14:00 MSK). 4 уточняющих вопроса, 3 варианта approach, выбран B (поэтапный rollout).

## Проблема

Факторный анализ 134 эпизодов мая 2026 показал систематический провал дисциплины роутера:

1. **73% эпизодов идут `direct`** — без вызова какого-либо skill/subagent.
2. **0% триггер-матча на feature/planning/memory-sync** — на этих типах задач я даже не сканирую реестр узлов.
3. **50% триггер-матча на analysis, 33% на bugfix** — способность находить узлы есть, но включается только на «явно профильных» задачах.
4. **79% эпизодов с пометкой `regulated` не применяют ни одной границы из ADR** — метка формальная.
5. **Hard-floor сработал только 14 раз из 134** (10%), и всегда только Pravila §12. §14 (Queen) и §15 (parallel sessions) — 0 следов в журнале.
6. **Парсер `candidates_considered` пишет туда булиты моих ответов** вместо имён узлов реестра — невозможно проверить, реально ли я смотрел альтернативы.
7. **Правила децентрализованы** — Pravila §12-15, PSR_v1 R0-R15, Tooling §4.X (9-attribute blocks на 83 узла), routing-off-phase.md, ADR-* — итого 5 источников. Чтобы выбрать узел, нужно прыгать по документам.

Текущая процедура `docs/router-procedure.md` v1.4 явно говорит «No forced-choice gate. Nodes that don't match triggers are silently skipped». Это намеренный дизайн, который выбирался при ADR-011 — но при текущем уровне дисциплины (27%) он не работает: я не сканирую реестр на болевых типах задач.

Заказчик 23.05.2026: «как заставить тебя не делать без скилов и плагинов?» → выбран **hard-enforcement**: точность на каждой задаче + запрет direct когда подходящий навык существует.

## Цель и границы

**Цель:**

1. На каждой классифицированной задаче берётся **правильный** навык (узел реестра).
2. **Запрет `direct`** когда подходящий навык существует.

**Исключение:** только сверх-мелкие правки (micro). Любая другая работа — через навык, если реестр предлагает подходящий узел.

**Что считается micro** (черновик, уточняется в этапе 3):

- ≤2 файла затронуто
- ≤20 строк изменено
- Тип ∈ {опечатка, переименование, удаление мёртвого кода, правка одного значения константы, форматирование}
- Классификатор явно метит `task_type=micro` (см. этап 3)

**Что НЕ входит в scope:**

- Изменение бизнес-логики продукта Лидерра.
- Новые навыки/инструменты в реестре сверх существующих 83.
- Замена `economy mode` хук-архитектуры (она остаётся, мы её дополняем).
- Изменение Pravila §16 наблюдателя (только потребляем его данные).

## Архитектура

Четыре независимых слоя:

```
┌─────────────────────────────────────────────────┐
│ Слой 4: НОРМАТИВКА (Pravila/PSR_v1/Tooling)     │
│ — сокращена до cross-refs на реестр + уникальное │
└─────────────────────────────────────────────────┘
              ↑ читают/делегируют
┌─────────────────────────────────────────────────┐
│ Слой 1: РЕЕСТР (registry.yaml, machine-readable)│
│ — единственный источник истины «task → node»     │
└─────────────────────────────────────────────────┘
              ↑ читают
┌─────────────────────────────────────────────────┐
│ Слой 3: КЛАССИФИКАТОР + PreToolUse hook         │
│ — regex pre-screen → Sonnet escalation → block   │
└─────────────────────────────────────────────────┘
              ↑ пишут/читают данные
┌─────────────────────────────────────────────────┐
│ Слой 2: ИЗМЕРЕНИЯ (parser fix + STATUS.md)      │
│ — baseline ДО enforcement, тренд ПОСЛЕ          │
└─────────────────────────────────────────────────┘
```

## Этапы

### Этап 1 — Справочник (фундамент)

**Цель:** машиночитаемый реестр всех 83 узлов в одном файле + auto-render существующих Markdown-страниц.

**Артефакты:**

- **`docs/registry/nodes.yaml`** — YAML-реестр узлов. Схема:

  ```yaml
  nodes:
    - id: "#19"
      name: "Superpowers v5.1.0"
      slug: "superpowers"
      category: "phase-2"
      subcategory: null
      triggers:
        - {keyword: "tdd", weight: 1.0}
        - {classification: "feature", weight: 1.0}
        - {classification: "planning", weight: 1.0}
        # ... все триггеры из Tooling §4.X
      boundaries:
        - {adr: "ADR-011", role: "hard-floor source"}
        - {pair: "#30", relation: "paired stack"}
      chain_membership: ["L1", "L8"]
      status: "active"   # active | dormant | deferred
      dormancy_reason: null
      attributes:
        # все 9 атрибутов из Tooling Прил. Н §0.1
  chains:
    L1:
      name: "Brainstorming chain"
      sequence: ["superpowers:brainstorming", "superpowers:writing-plans", "superpowers:executing-plans"]
      triggers: [...]
    # ... L1-L16
  ```

- **`docs/registry/schema.json`** — JSON Schema валидация YAML.
- **`tools/registry-render.mjs`** — pure script, читает `nodes.yaml` → рендерит:
  - `docs/Tooling_v8_3.md` §4.X (auto-region между маркерами `<!-- auto:registry -->...<!-- /auto:registry -->`)
  - `docs/routing-off-phase.md` §3 routing-таблица + §4 chains
  - `docs/registry/index.md` — индекс для людей
- **`tools/registry-load.mjs`** — pure module, экспортирует `loadRegistry()` → `{ nodes, chains, indexByTrigger, indexById }` для использования в хуках и анализаторе.
- **`tools/registry-load.test.mjs`** — unit-тесты (≥10): загрузка, ошибка YAML, поиск по триггеру, поиск по id, dormant exclusion.
- **lefthook job** `registry-render-check` (pre-commit, warn-only first week, then blocking): запускает render, проверяет что Markdown-регионы не дрейфят от реестра.

**Источник данных:** парсим текущий `docs/Tooling_v8_3.md` §4.X (83 узла × 9 атрибутов = ~750 полей). Это ручной перенос с верификацией. Risk: парсинг Tooling может пропустить нюансы.

**Что НЕ меняется в этом этапе:**

- Pravila/PSR_v1 — не трогаем.
- Хуки — не трогаем (реестр пока никто не enforce'ит).
- Поведение Claude — не меняется.

**Definition of done:**

- 83 узла в `nodes.yaml`, валидация JSON Schema проходит.
- `tools/registry-render.mjs` рендерит auto-region в Tooling.md и routing-off-phase.md, diff с текущими файлами = 0 (т.е. рендер совместим).
- ≥10 unit-тестов в `registry-load.test.mjs` GREEN.
- Документация `docs/registry/README.md` — как добавлять/менять узел.
- Работа в feature-branch + PR в `main`; Pravila §15.2 pre-flight sync обязателен (Tooling §4.X — в списке 8 нормативных файлов).

**Точка остановки:** ты говоришь «продолжаем» → этап 2.

### Этап 2 — Измерения (baseline до enforcement)

**Цель:** добавить метрики разбивки дисциплины по типам задач, получить **honest «точку До»** дисциплины.

**Что УЖЕ сделано параллельной сессией 23.05 (коммиты `4665c537`, `6192d395`, `6a9df652` — spec `2026-05-23-observer-parser-skill-hook-expand-design.md`):**

- ✅ Fix `tools/observer-transcript-parser.mjs` — `candidates_considered` whitelist filter (`tools/observer-known-nodes.txt` + `observer-chain-map.json`); проза/процедуры/код отсекаются. Forward-only.
- ✅ Schema v3 — `schema_version` 2 → 3. Старые v2-эпизоды не правим (append-only).
- ✅ `primary_rationale.recommended_node` — поле есть в v3, заполняется из `observer-classification-map.json` (этап 2 переключит на реестр).
- ✅ `events[].hook_fired.scripts` — reverse-lookup имени скрипта из `.claude/settings.json` + user.
- ✅ `tools/brain-retro-analyzer.mjs` accepts schema ≥2 (v2+v3 mix); добавлена `recommended_node_for_direct` факторная ось.

**Что осталось делать в этапе 2:**

- **`tools/brain-retro-analyzer.mjs`** — новые срезы:
  - `disciplinePercentByClassification` — % эпизодов с матченным триггером, разбивка по `task_classification`.
  - `routerStepReached` — на каком шаге роутера эпизод действительно остановился (если step=1 для всех — это баг парсера, отметить).
  - `boundariesAppliedRate` — % эпизодов с непустым `boundaries_applied`.
- **`tools/missed-activations.mjs`** — переключить с `observer-classification-map.json` на реестр из этапа 1 (`docs/registry/nodes.yaml`). Маппинг classification → recommended_nodes теперь автоматический через `nodes[i].triggers[].classification`.
- **`docs/observer/STATUS.md` generator** — добавить блок «Метрики дисциплины»:

  ```
  | Тип задачи | Эпизодов | % с триггер-матчем | % через скил |
  |---|---|---|---|
  | feature | 5 | 0% | 0% |
  | planning | 3 | 0% | 0% |
  | bugfix | 6 | 33% | 33% |
  | analysis | 8 | 50% | 50% |
  ```

- **Baseline snapshot** в `docs/observer/baselines/2026-XX-XX-pre-enforcement.md` — фиксируем цифры «до» (% дисциплины, missed activations, % regulated, % границ применено), чтобы потом сравнить.

**Definition of done:**

- STATUS.md показывает разбивку дисциплины по типам задач.
- Baseline snapshot закоммичен.
- Существующие тесты `brain-retro-analyzer.test.mjs` + новые срезы (`disciplinePercentByClassification`/`routerStepReached`/`boundariesAppliedRate`) — GREEN.
- `missed-activations.mjs` читает реестр из этапа 1; результат на тех же данных не должен сильно дрейфить от текущего (sanity check). Тесты GREEN.

**Точка остановки:** ты видишь цифры «27% дисциплины, 0% на feature», говоришь «продолжаем» → этап 3.

### Этап 3 — Принуждение (где включается enforcement)

**Цель:** хук, который **физически блокирует** Edit/Write/Bash на не-micro классифицированной задаче, если skill ещё не вызван.

**Артефакты:**

- **`tools/router-classifier.mjs`** — pure module, гибрид:
  - **Layer 1 (regex pre-screen, ~10 ms):** ключевые слова RU+EN (`фича`, `feature`, `план`, `plan`, `дебаг`, `bug`, `опечатка`, `typo`, `переименуй`, `rename`, …) → классификация + micro-flag. Покрывает ~70% промптов.
  - **Layer 2 (Sonnet escalation, ~300-500 ms, $0.001-0.005/call):** если regex не уверен (multiple matches / no match / ambiguous), отправить промпт + текущую структуру задачи в Sonnet с фиксированным prompt-template, получить `{task_type, micro: bool, recommended_node: id|null, confidence: 0-1}`. Кеш per-prompt-hash в memory чтобы не повторять.
  - **Output:** `{classification, micro, recommended_node, source: 'regex'|'llm'|'cache'}`.
- **`tools/router-prehook.mjs`** — UserPromptSubmit hook:
  - Вызывает classifier.
  - Пишет результат в `~/.claude/runtime/router-state-<session>.json` (для текущего хода).
  - Если `micro: false` AND `recommended_node != null` AND `classification` ∈ §12.2 list → выставляет флаг `enforcement_required: true`.
- **`tools/router-tool-gate.mjs`** — PreToolUse hook:
  - Если `enforcement_required` AND tool ∈ {Edit, Write, Bash (kроме read-only ls/cat/git status)} AND `skill_invoked_this_turn == false` → **`decision: block`** с сообщением «Эта задача классифицирована как `feature` (например). Реестр рекомендует `superpowers:writing-plans`. Вызови skill ПЕРВЫМ, либо начни ответ с `<!-- routing: ... -->` тэга с обоснованием.»
  - Если есть routing-tag с явным `direct_justified=true` и обоснованием — пропускает (escape hatch для редких случаев когда классификатор ошибся).
- **`tools/router-stop-gate.mjs`** — Stop hook (расширение существующего routing-gate):
  - Если enforcement сработал и был обойдён через routing-tag → пишет это в эпизод как `decision_provenance: user_directed_method` с пометкой «manual override».
- **Конфигурация** `.claude/settings.json`:
  - Регистрация трёх новых хуков (UserPromptSubmit / PreToolUse / Stop расширение).
  - Permissions: запретить Edit/Write/Bash до Skill через `allow`-флаг? Нет — мы это делаем через хук, чтобы было видно решение.

**Тестирование:**

- Записать 20 ручных тестовых промптов (5 feature, 5 micro, 5 planning, 5 question) → прогнать через classifier, проверить.
- Live-тест на одной сессии — посмотреть FP/FN, итерация regex'а.
- Откат за 5 минут: убрать хуки из `settings.json` (no code change needed for rollback).

**Definition of done:**

- Classifier валидируется ≥80% точности на тестовом наборе.
- PreToolUse hook верно блокирует на feature-promt без skill (verified live).
- Routing-tag escape hatch работает (verified live с явным `direct_justified=true`).
- В STATUS.md метрика «% дисциплины» через неделю показывает рост (≥60% от baseline).
- Стоимость классификатора в Anthropic dashboard: ≤$15/мес сверх текущего.

**Точка остановки:** ты смотришь на цифру «дисциплина 27% → 75%», и либо продолжаем → этап 4, либо откатываем (если enforcement слишком жёсткий, FP > 20%).

### Этап 4 — Уборка правил (нормативка)

**Цель:** сократить Pravila/PSR_v1/Tooling до cross-refs + unique justifications. Все «routing-relevant» куски делегируются в реестр.

**Артефакты:**

- **Pravila §12-§15** — оставить декларацию hard-rule + cross-ref на реестр. Списки 14 типов задач (§12.2) перенести в реестр как trigger-теги узлов.
- **PSR_v1 R0-R15** — UI-stack apparatus оставить (это про **как** использовать Vue+Vuetify, не про роутинг). R10/R14/R15 (routing-relevant) сократить до cross-refs на реестр.
- **Tooling §4.X** — становится auto-region из `registry-render.mjs`. Прозьба для людей: «не правьте напрямую, правьте `docs/registry/nodes.yaml`».
- **`docs/routing-off-phase.md`** — становится auto-region (routing-таблица + L1-L16 chains).
- **ADR-016 (новый)** — фиксирует архитектурный сдвиг «реестр как single source», описывает alternatives rejected (зачем не Big Bang, не keyword-only, не LLM-only).
- **CLAUDE.md §0** — обновить cross-refs на новые версии Pravila/PSR_v1/Tooling.
- **`docs/router-procedure.md` v2.0** — переписать процедуру: шаг 3 теперь «classifier выдаёт recommended_node» вместо «scan Tooling §4.X manually».

**Definition of done:**

- Все cross-refs валидны (lychee 0 broken).
- `cross-ref-checker` C2 контролёр — 0 drift.
- `l1-watcher` C1 контролёр — 0 drift.
- Pravila/PSR_v1/Tooling diff vs предыдущая версия — сокращение ≥30% строк за счёт удаления дублей.
- ADR-016 committed.

**Точка остановки:** этап 4 — последний. После него — финальный brain-retro «cycle closed», measure long-term effect.

## Continuity механизм (как не забыть)

Тройная страховка:

1. **STATUS.md раздел «Активные многоэтапные проекты»** — в конце каждого этапа я обязан добавить блок:

   ```
   ## Активные проекты
   - **Router discipline overhaul** (spec 2026-05-23-router-discipline-overhaul-design.md)
     - Этап 1 ✅ закрыт 2026-XX-XX (коммиты abc..def)
     - Этап 2 ⏸ ждёт «продолжаем» от заказчика (с YYYY-MM-DD)
     - Следующий: `docs/superpowers/plans/2026-XX-XX-router-overhaul-stage-2.md`
   ```

   STATUS.md загружается в системный промпт → я вижу при старте каждой сессии.

2. **memory-файл `project_router_overhaul.md`** в `memory/` — current stage, blockers, last action timestamp, next step pointer. MEMORY.md индекс ссылается на этот файл.

3. **brain-retro еженедельный анализ** — отдельный срез «open multi-stage projects, last activity»; если >7 дней молчания на этапе — surface в C5 ⚠️.

В конце каждого этапа я обязан **спросить заказчика** напрямую: «Этап N закрыт, цифры такие-то. Запускаем N+1 или пауза?» Это часть `superpowers:finishing-a-development-branch`.

## Открытые вопросы и риски

### Открытые вопросы (для решения по ходу)

1. **OQ-1.** Threshold классификатора Layer 1 confidence для escalation в Layer 2 — будет calibrated на тестовом наборе в этапе 3.
2. **OQ-2.** Порог micro по строкам — 20 строк это первоначальный guess; возможно нужен 50 или зависит от типа файла. Определим эмпирически в этапе 3.
3. **OQ-3.** ADR-границы как переносить в YAML? Простой список `boundaries: [{adr, rule}]` или richer структура? Решим в этапе 1 после прототипа.
4. **OQ-4.** Кеш классификатора — per-session или persistent (across-session)? Влияет на стоимость и точность при повторных похожих промптах.

### Риски

- **R-1. Классификатор русскоязычный — Sonnet может ошибаться на жаргоне.** Mitigation: regex pre-screen покрывает 70% явных случаев, Sonnet получает richer context (recent history + task structure).
- **R-2. False positive enforcement блокирует тривиальную задачу.** Mitigation: routing-tag escape hatch, метрика FP rate в STATUS.md, weekly review.
- **R-3. Парсинг Tooling §4.X в YAML пропустит атрибуты.** Mitigation: render-check сравнивает rendered Markdown с originalом, diff = 0.
- **R-4. Параллельные сессии правят нормативку → collision в этапе 4.** Mitigation: Pravila §15.2 pre-flight sync обязателен на каждом коммите этапа 4.
- **R-5. Откат хука в этапе 3 не сработает на уже запущенной сессии.** Mitigation: documenting force-restart procedure, plus per-session enforcement_required flag (sessions started before flag — exempt).
- **R-6. Стоимость Sonnet escalation выйдет за бюджет.** Mitigation: hard cap в `router-classifier.mjs` (≤200 Sonnet calls/день, при превышении — fallback на regex-only, метрика в STATUS.md).
- **R-7. Я сам не вспомню запустить этап 2 через неделю.** Mitigation: тройная continuity-страховка (STATUS.md + memory + brain-retro), плюс явный prompt в конце этапа 1 «Готов запускать этап 2?».

## Что НЕ входит в scope

- **Замена observer/JSONL архитектуры** — она работает, потребляем её.
- **Замена economy mode хуков** — они остаются как есть, мы добавляем рядом.
- **Многоязычность классификатора** — RU+EN достаточно (нет других языков в журнале).
- **GUI для редактирования реестра** — правка YAML руками.
- **Поддержка Claude Code старых версий** (≤2.0) — мы на 2.x.
- **Real-time дашборд** — STATUS.md + brain-retro достаточно.

## Acceptance criteria для всего overhaul'а (после этапа 4)

- Дисциплина (% эпизодов с матченным триггером на классифицированных задачах): **≥75%** (baseline 27%).
- Missed activations: **≤5/неделю** (baseline 40/месяц = ~10/неделю с шумом, ~3/неделю без шума).
- % feature/planning без skill: **≤10%** (baseline 80%).
- Стоимость дополнительная: **≤$20/мес**.
- Откатываемость: **полный rollback ≤30 минут** (через revert коммитов + удаление хуков из settings.json).
- Документация: новичок может прочитать `docs/registry/README.md` и понять как добавить узел за 15 минут.

## Self-review (после написания, перед user review)

- ✅ Placeholders: нет «TBD», все секции заполнены.
- ✅ Внутренние противоречия: четыре этапа не пересекаются, точки остановки явные.
- ✅ Scope: подходит для 4 sub-plans (не один). Декомпозиция в этапах явная.
- ✅ Ambiguity: открытые вопросы выделены в OQ-1..4, к ним возвращаемся в соответствующих этапах. «Что считается micro» помечен как «уточняется в этапе 3».
- ⚠️ Source of truth для счётчиков узлов (83) — берётся из Tooling Прил. Н §0 (канон, finding 3 SYSTEM-аудита 18.05.2026), не из CLAUDE.md.

## Следующий шаг

После твоего ревью этого спека → `writing-plans` skill → план для этапа 1 (справочник). После закрытия этапа 1 — план этапа 2. И так далее.

---

## Amendment 2026-05-24 — расширение этапа 3 (Task 0a доменная разметка + Task 0b цепочки + chain governance)

**Триггер:** живой разговор с заказчиком 2026-05-24 после закрытия этапа 2. Заказчик задал два фундаментальных вопроса, не покрытых исходным дизайном этапа 3:

1. **«Есть скилы для биллинга, маркетинга, безопасности — но ты их не используешь. После этапа 3 эта проблема уйдёт?»** — обнажил, что классификатор смотрит только на **тип задачи** (feature/bugfix/refactor), не на **домен** (биллинг/маркетинг/безопасность). На промпт «почини списание лида» исходный дизайн рекомендовал бы Pest, но не #62 billing-audit — рядом стоящий доменно-специальный скил.
2. **«Как создаются/расширяются/меняются цепочки?»** — обнажил, что cepочки (L1-L16) сейчас описательные, но в этапе 3 становятся **исполняемыми** (сторож принуждает). Нужна явная governance.

Решение: расширить этап 3 двумя под-тасками **перед** основными (Task 1+) и зафиксировать chain governance как часть scope.

### Task 0a — Доменная разметка реестра

**Цель:** научить классификатор различать домены задач, а не только типы.

**Артефакты:**

- **Расширение `docs/registry/nodes.yaml`:** на 30-40 специализированных скилах добавить **keyword-триггеры** доменного характера. Примеры:
  - `#62 billing-audit` → keywords: `списание`, `биллинг`, `тариф`, `баланс`, `начисление лида`, `lead_charges`, `деньги`, `копейки`, `провенанс charge`, `csv reconcile`, `bcmath`, `bcadd`, `idempotency`.
  - `#71 pdn-152fz-audit` → keywords: `ПДн`, `персональные данные`, `152-ФЗ`, `согласие`, `телефон лида`, `маскирование`, `pd_subject_request`, `data subject`.
  - `#73 security-go-live` → keywords: `безопасность`, `прод`, `выход в интернет`, `go-live`, `публикация`, `attack surface`.
  - `#74 marketing` (plugin) → keywords: `email-рассылка`, `кампания`, `контент`, `лендинг`, `реклама`, `вебинар`, `лидген`.
  - `#76 brand-voice` → keywords: `тон бренда`, `voice`, `позиционирование`, `тональность`.
  - `#77 marketing-ru` → keywords: `РФ-канал`, `яндекс`, `вконтакте`, `telegram`, `152-фз маркетинг`, `unisender`.
  - `#66 laravel-backend-patterns` → keywords: `controller`, `service`, `job`, `Eloquent`, `RLS`, `partition`.
  - И т. д. по каждому из ~30-40 доменных скилов в реестре.
- **Расширение Sonnet prompt-template в `tools/router-classifier.mjs` Layer 2:** теперь Sonnet получает не только промпт + тип задачи, но и **список активных узлов реестра с их keyword/classification триггерами** (`{id, name, triggers}` для каждого active-узла). Sonnet возвращает не только `{task_type, micro}`, но и `recommended_node: id | null` напрямую — выбранный конкретный узел реестра.
- **Layer 1 (regex)** так же сканирует промпт по keyword-триггерам активных узлов: совпадение с keyword'ом узла N → recommended_node=N. Layer 2 эскалация только если Layer 1 не уверен или нашёл конфликт.

**Definition of done:**

- На 30+ доменных скилах есть как минимум 5-10 keyword-триггеров каждый.
- Sonnet prompt-template содержит сериализованный список узлов (компактный — id+name+top-5 keywords).
- Unit-тесты classifier'а покрывают 20+ доменных промптов: «почини списание» → #62, «составь email-рассылку» → #74, «выход в интернет» → #73.

### Task 0b — Цепочки в рекомендациях

**Цель:** сторож подсказывает не только первую точку, но и всю цепочку (L1-L16).

**Артефакты:**

- **Sonnet возвращает дополнительное поле:** `recommended_chain: chainId | null` (L1-L16 либо null если задача одиночная).
- **`router-prehook.mjs`** пишет в state-файл сессии не только `recommended_node`, но и `recommended_chain` + `chain_progress: []` (массив уже вызванных шагов).
- **`router-tool-gate.mjs`** при каждом вызове Edit/Write/Bash проверяет:
  - Если `chain_progress` не пустой — следующий ожидаемый шаг = `chain.sequence[chain_progress.length]`. Если этот шаг ещё не вызван, и пытаешься делать Edit — блок «не пропустить шаг цепочки X».
  - Если цепочка завершена (`chain_progress.length === chain.sequence.length`) — снимает блок, пропускает Edit/Bash.
- **`router-stop-gate.mjs`** обновляет `chain_progress`: если в текущем ходу был вызван скил из цепочки — добавляет в массив. Пишет в эпизод `chain_progress` для последующей аналитики (хватает ли цепочка от первого до последнего шага).

**Definition of done:**

- На 5+ типовых тестовых промптах, попадающих в L1-L16, сторож проводит через всю цепочку (live-тест на одной сессии).
- Brain-retro analyzer +новая ось «chain completion rate» (% начатых цепочек, доведённых до конца).
- В STATUS.md строка «Chain completion: X%».

### Chain governance — как создаются/меняются цепочки

**Базовое правило:** **никаких автоматических правок цепочек.** Claude НЕ имеет права самостоятельно (без явного согласия заказчика) добавлять, удалять или изменять цепочку в `docs/registry/nodes.yaml`. Цепочка в этапе 3+ становится исполняемой — её изменение меняет поведение мозга.

**Три источника изменений:**

1. **Явный запрос заказчика** («давай чтобы при правках биллинга всегда дёргался billing-audit потом Pest»). Claude:
   - Подтверждает формулировку.
   - Правит `nodes.yaml` (добавляет/меняет chain block + поле `chain_membership` на участвующих узлах).
   - Авто-render обновляет `docs/routing-off-phase.md`.
   - Контролёр C6 (chain-map-checker) сверяет — синхронны ли источники.
   - Коммитит одним коммитом, ~15-30 минут.

2. **Кандидат из `/brain-retro`** (раз в спринт):
   - Аналайзер ищет в журнале повторяющиеся паттерны последовательностей скилов на одной задаче.
   - Если паттерн встретился ≥3 раз и для него нет существующей цепочки в реестре — выдаёт кандидата с обоснованием в отчёте.
   - Claude **обязательно показывает кандидата заказчику** перед внесением. Без явного согласия не вносит.
   - При согласии — как в пункте 1.

3. **Сигналы пропусков** (С5 missed-activations):
   - Если на одной классификации регулярно (N>10/мес) пропускается рекомендованный узел — может быть проблема в маппинге keyword'ов (Task 0a) ИЛИ нужна цепочка с пред-условием. Решает Claude+заказчик через `/brain-retro` интерпретацию.

**Защита от ошибок при правках:**

- C6 chain-map-checker — синхрон источников (уже работает).
- Авто-render `routing-off-phase.md` — невозможен дрейф между YAML и Markdown.
- C2 cross-ref-checker — ссылки в нормативке (Pravila/CLAUDE.md/PSR_v1) на цепочки.
- C1 l1-watcher — имена узлов в `sequence`.
- **Новое в этапе 3:** перед коммитом изменения существующей цепочки — обязательно прогоняется **«what-if симуляция»** на исторических эпизодах последнего месяца: если бы новая цепочка работала, сколько было бы ложных блокировок? Порог: ≤5%. Превышение → не коммитим.

**Откатываемость изменений цепочек:**

- Каждое изменение — один коммит. `git revert <SHA>` возвращает в предыдущее состояние.
- Если правка пошла в прод и начала мешать — снимается за 10 минут (revert + push).

### Обновлённые acceptance criteria этапа 3

К исходным («дисциплина ≥75%», «FP блокировок ≤20%», «$≤15/мес») добавляются:

- **Domain hit rate** — % эпизодов с непустым `recommended_node` (не только `task_type`): **≥80%** на доменных промптах (биллинг/маркетинг/безопасность).
- **Chain completion rate** — % начатых цепочек, доведённых до последнего шага: **≥60%** к концу первой недели.
- **Доменный baseline восстановлен** — на тестовом наборе (20 заранее подготовленных доменных промптов) classifier правильно угадывает специализированный узел в **≥75% случаев**.

### Что НЕ меняется amendment'ом

- Исходный scope этапа 3 (classifier hybrid regex+LLM, 3 хука, escape hatch routing-tag).
- Бюджет $≤20/мес.
- Откатываемость ≤5 минут на отключение хуков.
- Этапы 1, 2, 4 — без изменений.
- Continuity-механизм (STATUS.md + memory + brain-retro).
- Принцип «никакого изменения нормативки в этапе 3» — нормативка трогается только этапом 4.

### Что переезжает «из головы Claude» в код

После этапа 3 с amendment'ом:

| Бывшая «процедура router-procedure.md v1.4» | Кто делает после этапа 3 |
|---|---|
| Шаг 1: Hard-floor §12/§14/§15 | Существующие хуки скилов + новый PreToolUse сторож |
| Шаг 2: Классификация задачи | `router-classifier.mjs` Layer 1 regex |
| Шаг 3: Поиск узла в реестре по триггерам | `router-classifier.mjs` Layer 2 Sonnet с доменной разметкой Task 0a |
| Шаг 4: Проверка цепочки L1-L16 | Sonnet выдаёт `recommended_chain` (Task 0b); сторож отслеживает `chain_progress` |
| Шаг 5: Выполнение | Сторож блокирует Edit/Bash до вызова нужного скила |

**Документ `docs/router-procedure.md` v1.4 → v2.0** (это этап 4): становится описанием архитектуры, а не инструкцией к действию. v1.4 удаляется как «процедура для Claude», v2.0 пишется как «как устроен мозг» для людей.

### Self-review amendment'а

- ✅ Не противоречит исходному spec'у — расширяет, не заменяет.
- ✅ Все три боли заказчика (биллинг/маркетинг/безопасность) явно адресованы Task 0a.
- ✅ Цепочки governance явно зафиксированы — Claude не имеет права автоматически менять.
- ✅ Откатываемость сохранена (хуки в settings.json + revert коммитов).
- ⚠️ Расширение scope этапа 3 ≈ +3 часа работы (Task 0a 1.5ч + Task 0b 1.5ч). Принято заказчиком 24.05.
