# Аудит готовности М7 Фазы 8 «Растворение зоопарка + переезд» — находки

**Дата:** 2026-06-09 · **Ветка:** `worktree-brainrepo` · **HEAD на старте:** `4ea4806f`
**Метод:** read-only, инлайн (субагенты запрещены). Скилы: `audit-context-building` (+`sharp-edges` по швам).
**Источники:** spec `docs/superpowers/specs/2026-06-08-router-mentor-machine-7-design.md` (§5/§9/§10/§12) + handoff `2026-06-09-phase8-migration-handoff.md` + loose-ends `2026-06-08-router-mentor-loose-ends-registry.md` + код `tools/*.mjs`.
**Принцип:** память `*_done.md` НЕ источник истины — всё верифицируется ПО КОДУ.
**Кодовая фраза:** «роутер-наставник».

---

## Вердикт

Оборонный контур М1–М6 собран на **~95%**. Блокёр №1 (content-floor port, §10 V1/V1-PS) **закрыт в коде**.
Остаток несущего кода Claude **мал**: один настоящий gap (**G1** — verify→Гейт-3 не проведён живьём) + одно
решение с мелким кодом (**G2** — манифест 9→11). Всё прочее — owner-активация (A-блок) или осознанно отложено.

---

## Confirmed-by-code (собрано)

| ID | Шов / пункт §12 | Статус | Доказательство |
|---|---|---|---|
| **C-1** | Seam #1 content-floor port (БЛОКЁР §10 V1/V1-PS, правило 8) | ✅ ПОЛНЫЙ | `shell-content-rules.matchBashHardBlacklist` — весь P-1 набор (rm/mv/cp/chmod/chown/chgrp + node -e/-r/--import/fs.* #4 + python/bash/sh -c + eval + composer/npm/yarn/pnpm install + npx claude-* + curl -X + env-prefix #21 + --watch #22 + wget G7 + nc/ncat/netcat/socat G8 + echo-inj #34 + stderr-redirect C16). `matchPsHardBlacklist` — весь V1-PS (Remove/Move/Copy/Set/Add-Content/Out-File + redirect + iex + egress IWR/IRM/curl/wget + Start-Process + [IO.File]/[IO.Directory] + Stop-Process + service + ExecutionPolicy + ItemProperty + Get-Credential + Restart/Stop-Computer + ScheduledTask + Set-Acl/icacls + New-Item + G10 env/cloud). `floor-decide.floorDecide`: Bash (`bashIsContentBlock` whole+per-segment P-4 + `detectSubshell`) и PS (`psContentBlock` + `psProtectedWrite` P-3 forge-страж) рубят **независимо от плана**, escapable. tool-agnostic write-guard (MCP-writer) + fail-CLOSED на бросок normalize. |
| **C-2** | `canonicalAction` PS-ветка (P-2) | ✅ | `escape-grant.canonicalAction`: `if (name==='PowerShell') return powershell:${normalizeCommand(input.command)}` — escape специфичен per PS-команда (не схлопывается в `write:`). |
| **C-3** | Проводка пола (enforce-floor) | ✅ | `enforce-floor.decide` зовёт `floorDecide` ПЕРВЫМ, fail-CLOSED (любая ошибка → block), panic-ветка (правило 7б): если `floorDecide` бросит ДО своего escape-чека — `escapeAllowsEvent` всё равно оценивается (не кирпич мимо escape). |
| **C-4** | Seam #2 escape-survivability (правило 7/L6) | ✅ ПОДТВЕРЖДЁН | `enforce-read-path-deny` (Read+escapeGrantOpen), `enforce-mcp-classification` (mcp+egress ветки + escaped()), `enforce-normative-content-rules` (escapeAction + escaped law-edit §6) — все читают грант. Пол тоже. `canonicalAction` тотальна. |
| **C-5** | Seam #3 транспорт судьи М4 | ✅ ЕСТЬ (owner-активация остаётся) | `enforce-judge-gate.callJudgeModel` → реальный `callAnthropicAPI` (Sonnet за `ROUTER_LLM_KEY`); `runJudgeGate` async + degraded-allow на unavailable (VA-FIX-1, не фабрикует NO-GO); `runJudgeTurn` 3 режима inert/shadow/live-block; `decide`→`finalGate` (пол перевешивает). Рубильник `judge-gate-config.judgeActive` = флаг `ROUTER_MENTOR_JUDGE_ENABLED=1` + `resolveJudgeKey`. `judgeGateMode` = inert/shadow/live-block (`ROUTER_MENTOR_JUDGE_MODE=block`). |
| **C-6** | Seam #4 skill-журналер (SE-K) | ✅ | `enforce-skill-journaler` (PostToolUse Skill) пишет каждый Skill в action-journal (op:'Skill') + дедуп vs wall-pre-write; мост `extractSkillCalls`→K2. |
| **C-7** | Seam #4 доска «кто на посту» | ✅ (live-источник событий — follow-up) | `status-md-generator.computeGuardBoardBlock` рендерит манифест (`checkManifest`→registered/missing→⚠️ ПОСТ ПУСТОЙ) + режим судьи (`judgeGateMode()` inert/shadow/floor-only/live-block). recentEscapes/recentBlocks = `[]` (детализация follow-up, см. D-3). |
| **C-8** | Seam #4 normative-канал КАРТА/ЗАКОН (SE-D) | ✅ | `enforce-normative-content-rules`: `DISCIPLINE_SOURCE_RE` (tools/enforce-/judge-/floor-/escape-grant/action-journal/receipt-/shell-content-rules/plan-lock/classify-destructive/path-normalization) + `sealedPlanCoversEdit` (build-loop: LAW под планом → CARD) + escaped owner law-edit (§6) снимает гейт. |
| **C-9** | Манифест полный (SE-B) | ✅ | `floor-manifest-check.DEFAULT_REQUIRED_HOOKS` = все 9 хуков М1–М6 (floor/supreme-gate/normative-content-rules/read-path-deny/mcp-classification/judge-gate/snapshot/floor-escape-consume/skill-journaler). WARN-only (Δ8). |
| **C-10** | Поглощённые coverage/todowrite → журнал-K2 (Фаза 4a) | ✅ переписаны in-place, fail-CLOSE | `enforce-coverage-verify.decide` судит `skill:`-канал по `skillTakenByJournal` (K2) ∩ turn-scope (`turnToolUses`), НЕ по строке coverage (Класс 1 закрыт). `direct/node/chain/hook/agent` приняты на этом слое (судит живой М4, §1 П3). `enforce-todowrite-skill-verifier` тоже через `extractSkillCalls`. `domain-skill-discipline` (K2). |
| **C-11** | Стена М2 + волны R-08 | ✅ | `enforce-supreme-gate`: `verifyFrozenPlan`/`actionMatchesStep`/`treeLeafAt`/`validatePlanTree` (tree-aware волны), подписанные расписки (`receipt-sign`), журналирование op:Skill на plan-step (substrate K2). |

---

## 🔴 Остаток несущего кода (Claude)

### G1 — НЕСУЩИЙ: verify→Гейт-3 не проведён живьём (блокирует увольнение `verify-before-push`)
`enforce-verify-before-push` — **старый** zoo-хук (sentinel-артефакт + override-фраза + `RULE_KEY_PUSH`), §10 его увольняет.
Замена по §5 = «М5 подписанный GREEN + М4 Гейт-3». `judge-engine` **имеет** линзы `gate3: [goal_achieved, premortem_whole, behavior_vs_goal]` (`requiredLensesFor`),
**но хук судьи (`enforce-judge-gate`) бьёт только Гейт-2** (`extractGate2Product` = Write плана `docs/superpowers/plans/*.md`).
Гейт-3 ни на каком живом триггере не висит; нет PreToolUse-хука, требующего подписанный GREEN перед `git commit/push`.
→ **Увольнять `verify-before-push` без G1 = потерять pre-push verify-дисциплину.** NO-GO на это увольнение до G1.
**Достроить:** живой Гейт-3 / подписанный-GREEN PreToolUse на `git commit/push` (М4 Гейт-3 + М5 signer-расписка).

### G2 — решение + мелкий код: журнал-K2 coverage/todowrite не в манифесте
`enforce-coverage-verify` + `enforce-todowrite-skill-verifier` переписаны в журнал-K2 fail-CLOSE (Фаза 4a),
но **отсутствуют в `DEFAULT_REQUIRED_HOOKS` (9)** и §10 числит `coverage-verify` к увольнению.
**Решение:** оставить+зарегистрировать журнал-K2-версии (расширить манифест 9→11) **vs** уволить (судья coverage не судит — Гейт-2 only).
Рекомендация: **оставить+зарегистрировать** (это и есть миграция дисциплины; иначе coverage-дисциплина теряется).
Код: расширить `DEFAULT_REQUIRED_HOOKS` + runbook-регистрация.

---

## 🟥 Owner-активация (кода Claude нет — A-блок loose-ends)
A1-half (флаг `ROUTER_MENTOR_JUDGE_ENABLED=1` + ключ судьи keychain + регистрация хука + shadow→block) · A2 Фаза 8
(атомарно 9 хуков + увольнение зоопарка + `sealedPlanCoversEdit` live) · A3 пол · A4 escape/снимок · A5 стена matcher `*` + HMAC ·
A6 M1 matcher · A7 M3 живой Sonnet · A8 правила судьи в protected-файл · A9 push.

## ⚪ Отложено осознанно (owner-scoped / контракты)
R-10 G6-адаптер (НЕ построен) · M4 think-layer 4 пункта · M6 FIX-5 подпись floor_escape-гранта (spec §6 «не обязательно») ·
доска recentEscapes/Blocks live-источник (сейчас `[]`) · observer Фаза 2 (авто-правка нормативки — owner-scope).

## 🟨 Doc-drift (косметика, не блок)
**D-1:** шапка `enforce-judge-gate` стр.12-14 устарела — пишет «реальный llmCall-транспорт подключит владелец», но `callJudgeModel` уже использует `callAnthropicAPI`. Транспорт проведён A1; остаётся лишь флаг/ключ/режим.
**D-3:** доска recentEscapes/recentBlocks = `[]` (live-источник — follow-up B-блока R-09/R-30, не E).

---

## §10 GO/NO-GO на увольнение

| Зоопарк-хук | Вердикт | Условие |
|---|---|---|
| `router-gate` / `powershell-gate` | ✅ **GO** | content-floor port C-1 закрыт (блокёр снят) |
| `runtime-write-deny` | ✅ GO | пол RUNTIME_RE + escape |
| `verify-before-push` / `verify-record` | 🔴 **NO-GO до G1** | Гейт-3 не проведён живьём |
| `coverage-verify` / `todowrite-skill-verifier` | 🟡 GO при G2 | оставить журнал-K2 + манифест 9→11 |
| `branch-switch` | ✅ GO | М2 plan-step + M6 escape (маркеры удалены — подтвердить в Pass B) |
| `llm-judge-per-tool` / `-response-scan` (L4) | ✅ GO по A1 | после активации судьи М4 |
| `router-prehook` / `router-tool-gate` / `router-stop-gate` / `router-embedding-warmup` | ✅ GO по A7 | после живого М3 |
| `memory-coverage` / `tdd-gate` / `tdd-real-test-verifier` / `self-debrief-detector` / `rationalization-audit` / `prompt-injection`(reminder) | ✅ GO | поглощены §4.2 (см. Pass B детально) |
| `decomposition-detector` / `safe-baseline-metering` / `parallel-session-lock` | ✅ GO | no-op (ничего не теряют) |
| `subagent-return-scanner` / `subagent-prompt-prefix` | ✅ GO | субагенты запрещены |
| dead-vocab `findOverride`/`findOverrideAttempt` + врущие сообщения | ✅ GO | чистка Класса 6 |

---

## Pass B — построчный аудит ядер M1/M3/M6 (добор «ничего не пропускай»)

Швы M2/M4/M5 покрыты выше (C-1..C-11). Здесь — построчный добор ядер M1/M3/M6.

### M1 — Фундамент (журнал + ключ). Вердикт: ✅ **здоров, дефектов нет**
- `escape-grant.canonicalAction` — **тотальна** (try/catch → `'unknown:'` на любой throw; вход всегда доходит до escape-чека, SE-I/L6). Ветки Bash/**PowerShell** (P-2)/mcp/write. `findOpenGrant` — **единый** предикат свежести (action-match + `ts∈[0,5мин]` + не-погашен) для open-проверки И consume → гасится ИМЕННО открывший грант, без дрейфа. Гранты читаются read-only из `askuser-decisions-<sess>.jsonl` (type `floor_escape`), пишет среда (`enforce-askuser-answer-parser`), контроллер канал не пишет (runtime protected).
- `action-journal` — хеш-цепочка `sha256(prev_hash + canonicalJson({seq,ts,payload}))` (**seq+ts в хеше**, B2 — подмена метаданных задним числом ломает цепь); голова HMAC-подписана (`JOURNAL_HEAD`); `verifyChain` пересчитывает каждый хеш + linkage + подпись головы, структурно-битая запись → `{ok:false}` **значением**, не throw (N2, fail-closed). `assertSafeSessionId` — path-injection guard (N3, единый для всех строителей пути). **Неатомарность** дозаписи (append jsonl + write head — 2 fs-вызова) остаётся по конструкции, но **запинена** характеризационным тестом (`5782ede3`): обрыв → `verifyChain {ok:false, brokenAt}` (fail-closed); полная починка = иной дизайн персиста, owner-scope.
- `receipt-sign` — `canonicalJson` (сорт ключей, стабильно); `signPayload` HMAC-SHA256(`domain + ' ' + canonicalJson`), без ключа → null; `verifyReceipt` — формат sig (64 hex) + пересчёт + `timingSafeEqual` (constant-time), без ключа/sig → false (fail-closed). **Domain-separation (R-31)** заморожен: JOURNAL_HEAD/FROZEN_PLAN/FROZEN_ARTIFACT/APPROVAL/STEP_PTR/**M5_GREEN** — подпись одного типа не принимается за другой. **NB:** домен `M5_GREEN` существует → примитив подписанного GREEN для G1 **готов**; не хватает лишь хук-триггера (см. G1).

### M6 — Ключ+снимок (escape + откат). Вердикт: ✅ **здоров, дефектов нет**
- `enforce-snapshot` (PreToolUse после пола): перед разрушительным — `git stash create` + `update-ref refs/floor-snapshots/<id>` + `restore-points.jsonl`. Чистое дерево → ref=HEAD; **git-ошибка → fail-CLOSE (block)**; уникальный id (FIX-4: ts+pid+монотонный счётчик — два разрушительных в одну мс не клоббернут точку возврата). Инфра-ошибка самого хука (parse/stdin) → fail-open allow, но это не дыра: пол (`enforce-floor`) рубит разрушительное отдельно и fail-CLOSE; снимок = страховка отката, не гейт.
- `floor-escape-consume` + `enforce-floor-escape-consume` (PostToolUse): one-shot погашение через тот же `findOpenGrant`. **Порядок верен:** снимок-блок (PreToolUse) → действие не исполнилось → PostToolUse не сработал → грант НЕ сгорел (escape переживает снимок-блок). fail-open (PostToolUse не бросает) — приемлемо (не-погашение = мелкий over-permit в 5-мин окне, не дыра; блокировать PostToolUse не может).
- **FIX-5 (подпись floor_escape-гранта) осознанно отложена** (spec §6 «не обязательно»): целостность гранта держит **защита записи в runtime** (пол RUNTIME_RE + read-path-deny + content-floor #4 node-inline-fs рубят forge), а не подпись. Forge через `node -e fs.*` / PS-запись закрыт content-floor (C-1). Остаточный риск нулевой пока content-floor стоит; owner-noted.

### M3 — Роутер (каким навыком, по журналу). Вердикт: ✅ **классификатор жив; «не тот навык» по K2 — модуль готов; живой Sonnet = A7 owner**
- `router-classifier` **жив** (выдаёт `task_type`/`recommended_chain` — виден в §17-хук-контексте каждый ход). Аудит ядра + 5 TDD-фиксов — память `project_m3_audit_and_fixes` (chosen∈candidates / managed-терминатор / token-cover / neutrality).
- «Не тот навык по журналу» — `domain-skill-discipline` (`uncalledDomainSkills`/`domainCallDiscipline`, K2: рекомендованный доменный навык не вызван по журналу → not ok). Чистый модуль; **хук-проводка enforce — часть переезда/A7** (не отдельный enforce-хук сейчас). routing-tag-обход (`router-tool-gate`) увольняется §10.
- Живой Sonnet вместо мока для роутинг-решений = **A7 (owner)**.

### M2/M4/M5 — сводка (детали C-1..C-11)
- **M2 стена** `enforce-supreme-gate`: действие=шаг запечатанного плана, tree-aware волны (R-08), подписанные расписки, журналирование op:Skill (substrate K2). ✅
- **M4 судья** — механический пол `judge-gate-floor` (K2/K5) + думающий `judge-engine` (линзы gate1/gate2/**gate3**) + транспорт `callAnthropicAPI`; хук бьёт **только Гейт-2** (G1). ✅ транспорт / 🔴 Гейт-3 wiring.
- **M5 пол** `floor-decide`: content-floor (Bash+PS, P-1/V1-PS) + write-guard + критерий/мутация (прежние аудиты). ✅
- `branch-switch` — увольняется целиком §10 (замена = M2 plan-step + M6 escape); проверять marker-removal не нужно (хук уходит).

---

## Итог аудита (Шаг 1 завершён)

**Контур М1–М6 собран и аудирован по коду. Криптоядра M1/M6 здоровы, швы M2/M4/M5 закрыты, блокёр №1 (content-floor port) полон.**
**Несущий остаток Claude = G1 (Гейт-3/подписанный-GREEN wiring) + G2 (манифест 9→11, решение).** Примитив подписи для G1 (`M5_GREEN`) готов.
Всё прочее — owner-активация (A-блок) и осознанно отложенное. §10-увольнение безопасно при G1 (для verify-before-push) и G2 (для coverage/todowrite); router-gate/powershell-gate — GO уже сейчас.

---

## G1 (Level A) — ПОСТРОЕН (2026-06-09, commit-not-push)

Цепочка скилов отработана полностью: `audit-context-building` → `sharp-edges` → `variant-analysis` → `writing-plans` → `executing-plans` (инлайн TDD, наблюдаемый RED) → `verification-before-completion`. План: `docs/superpowers/plans/2026-06-09-g1-gate3-verify-signed-green.md`.

**7 task-коммитов `1857773f..b92afad9`** на `worktree-brainrepo` (HEAD `b92afad9`), origin НЕ тронут:
- `1857773f` task1 — `RECEIPT_DOMAINS.VERIFY_PASS` (receipt-sign).
- `10885990` task2 — `verify-receipt.mjs` (`signVerifyReceipt`/`verifyReceiptSig`/`acceptVerifyReceipt`: sig + occurrence>0 + fingerprint-match).
- `0f20c944` task3 — `verify-gate-config.mjs` рубильник (`ROUTER_MENTOR_VERIFY_ENABLED`: OFF=inert$0 / ON+key=enforce / ON+no-key=fail-CLOSE).
- `93e516e6` task4 — producer pure `buildVerifyReceipt`.
- `9ce6041b` task5 — producer staged-fingerprint + I/O main (сам прогоняет сюиту, SE-G1-5).
- `2bd7efbe` task6 — consumer `enforce-verify-gate.mjs` (PreToolUse, fail-CLOSE + escape M6 + docs-only, fingerprint из реального `git diff --staged`).
- `b92afad9` task7 — `enforce-verify-gate` в `DEFAULT_REQUIRED_HOOKS` (9→10).

**Верификация:** vitest tools regression **3350 passed / 2 skipped / 0 failed** (167 файлов; свежий прогон 10:25:55), выше планки §9.2 ≥2843+2skip. +27 G1-тестов к baseline 3323.

**Решение реализации (deviation, обосновано):** verify-receipt пишется в ЕДИНЫЙ `~/.claude/runtime/verify-receipt.json` (НЕ session-scoped) — producer-CLI не имеет session_id из stdin как consumer-хук; свежесть держит fingerprint. Escape-гранты остаются session-scoped.

**§10 разблокировано:** `verify-before-push`/`verify-record` теперь увольняемы (замена = `enforce-verify-gate`). **GO/NO-GO таблица выше:** verify-before-push 🔴NO-GO→✅GO.

**Остаётся (owner / follow-on):** G2 (манифест coverage/todowrite 10→12 — строкой в A2); owner-активация A-блок (ключ подписанта + `ROUTER_MENTOR_VERIFY_ENABLED=1` + регистрация хуков + увольнение зоопарка §10 + shadow→block + «пуш»); Level B (per-criterion + мутация P18 через готовый `runCriterionGate`).

