Files
brain/docs/superpowers/router-mentor-wall-GUIDE.md
T

29 KiB
Raw Blame History

Как работать под стеной «роутер-наставник» (шпаргалка сессии)

Читать ПЕРЕД работой под стеной. Каждое правило сверено с кодом нашей стены (tools/*.mjs, .claude/settings.json) на 2026-06-19. Якоря файл:строка — рядом с правилом.

Суть. Под стеной реальная работа (Edit/Write/Bash по коду) проходит ТОЛЬКО как шаг опечатанного плана. Поток: спека → (печать) → план → (печать) → шаги по порядку → авто-завершение. Печать встаёт за один заход (наставник→судья, оба GO).

Оглавление

  1. Поток и режимы
  2. Рецепт церемонии (форматы спеки и плана)
  3. Что свободно vs что гейтится
  4. Аварийный выход (escape) — лёгкое vs тяжёлое
  5. Коммит и пуш
  6. Субагенты под стеной — только разведка
  7. Судьи-машины и печать
  8. Частые ошибки и тупики
  9. Только владелец (штатный режим, снятие печати)
  10. Что под капотом (хуки + env)

1. Поток и режимы

Стена = enforce-supreme-gate.mjs (PreToolUse на всё, settings.json:75). Три режима — функция decideMode (enforce-supreme-gate.mjs):

  • разговорный — нет опечатанного плана (или judge_mode ≠ 'live-block'). Мутаторы (Edit/Write/мут-Bash) блокируются; чтение/спрашивание свободно; пишется только новый .md в specs//plans/.
  • исполнение — опечатаны И спека, И план, оба judge_mode='live-block'. Идут шаги плана по одному; каждый Edit/Write/Bash обязан совпасть с текущим шагом (actionMatchesStep, plan-lock.mjs).
  • escape — открыт разовый грант владельца: действие пропускается без сдвига указателя (§4).

Проба состояния — безобидный не-readonly Bash, напр. node --version (echo режет как не-whitelist):

  • блок «разговорный режим» → стена включена, печати нет;
  • блок «ожидался шаг N: …» → ты в исполнении на шаге N (возможно, застрял — см. §8);
  • команда выполнилась → стены нет (штатный) или открыт escape.

Авто-завершение лениво (§3.4). После последнего шага печать снимается НЕ синхронно, а на следующем действии — чтобы criterion-gate на код-пуше успел увидеть живой план. Старый обход «сделать push не-терминальным» больше не нужен.


2. Рецепт церемонии (точные форматы — иначе печать не встанет)

Спека — Write НОВОГО файла docs/superpowers/specs/<уникальное-имя>.md:

  • якорные секции ## Заголовок {#D1}с непустым телом (контракт, edge-cases, конвенция, критерий);
  • блок ```verified-context-json``` — массив с ≥1 EXTRACTED: {"id":"…","kind":"EXTRACTED","ref":"<существующий файл>","anchor":"<реальная подстрока ≥4 симв>"}. Готовый якорь: ref tools/cost-pricing.mjs, anchor export const PRICING = Object.freeze(;
  • секция ## Цель;
  • никакой мета-болтовни (печать/хеши/оркестратор/обход стены) — судья судит вслепую, завернёт fatal.

План — Write НОВОГО файла docs/superpowers/plans/<уникальное-имя>.md:

  • ## Цель;
  • ```skills-json``` — навыки без плагин-префикса: ["test-driven-development"];
  • ```steps-json``` — массив {op, object, ref}: op ∈ Write|Edit|Bash|MultiEdit|PowerShell|session (Task/Skill шагом запрещены); object = путь (файловые op) или точная команда (Bash); ref = якорь спеки (D1..Dn), непустой, должен резолвиться в опечатанном артефакте;
  • ```verified-context-json``` — план тоже требует ≥1 EXTRACTED;
  • раздел ## Переговоры → ### Круг N — сюда дословные доводы на возражения (память кругов подаёт их судье/наставнику, §7).

Реализация — шаги по порядку; каждый Edit/Write/Bash совпадает с текущим шагом (op+object). Указатель двигается по одному.

Осмотр сайта/живые проверки — op:"session". Один шаг-сеанс {op:"session", goal, tools:[…], produces:"<файл>"}: внутри смотришь/кликаешь живые инструменты сколько нужно (указатель не двигается), сеанс закрывает запись последнего produces. В tools сеанса нельзя Write/Edit/мут-Bash/floor — правки репозитория остаются шагами (sanitizeSessionTools, enforce-supreme-gate.mjs).

Под капотом: planId = sha256 от шагов (plan-lock.mjs) — байт-в-байт тот же план = тот же id; каждый шаг получает criterion_id (sha256 содержимого) для критерий-судьи; judge_mode — строгий whitelist (live-block/shadow; опечатка → fail-closed в разговорный).


3. Что свободно vs что гейтится

  • Чтение кода. В исполнении чтение вне путей текущего шага блокирует ДР-1 (reading-discipline, вызывается из enforce-supreme-gate.mjs). Свободно — только в разговорном режиме. Весь контекст собирай ДО печати плана (под планом ни исходники, ни вывод сканера не прочитать; запасной канал — владелец вставляет содержимое в чат).
  • Смотрящие/спрашивающие свободны везде: Read/Grep/Glob, ToolSearch/WebFetch/WebSearch, read-only браузер (browser_navigate|snapshot|wait_for|take_screenshot) — и под планом, и без (OBSERVE_ONLY_TOOLS/QUERY_ONLY_TOOLS, enforce-supreme-gate.mjs).
  • Авторская запись. Свободно пишется только новый .md в specs//plans/ (isAuthoringWrite, enforce-supreme-gate.mjs). Существующий файл и код — только шагом плана (или escape).
  • Пол (enforce-floor.mjs settings.json:55, логика floor-decide.mjs) рубит по содержанию, escapable:
    • инлайн-eval (node -e/python -c/bash -c), curl/install, egress, redirect >/>>;
    • цепочки ;/&&/||/| с мутирующей частью (посегментно, bash-tokenizer.mjs);
    • subshell как класс$()/backtick/<()/heredoc рубятся целиком, независимо от parse (detectSubshell);
    • ядерное (отдельный класс, classify-destructive.mjs): rm -rf/git push --force/migrate:fresh/db:wipe — даже под D1-грантом остаётся per-command escape;
    • PowerShell-записи в ~/.claude/runtime/секреты (psProtectedWrite).
    • ⚠️ Любая мутация ФС шагом-Bash тоже режется полом: mv/rm/cp -f — даже одиночные. Файловые операции делай инструментами Write/Edit (пол их не гейтит), а не Bash; удаление/переименование файла — через owner escape или терминал владельца.
    • Запуск скрипта-файла пол ПУСКАЕТ: python tools/x.py, node tools/x.mjs, npx vitest …, установленный CLI (graphify …). Режется только инлайн -e/-c. → нужен скрипт под планом: пиши .py/.mjs шагом Write, запускай node/python <файл> шагом Bash.

4. Аварийный выход (escape) — лёгкое vs тяжёлое

Старый рецепт «контроллер кладёт метку FLOOR-ESCAPE: в КНОПКУ» НЕ работает (фикс подделки согласия). Среда могла вернуть кнопку как «ответ» без действия владельца, поэтому ответ, дословно совпавший с ярлыком опции, согласием не считается (anti-button, enforce-askuser-answer-parser.mjs:64-65; answerMatchesOption askuser-answer-parser.mjs:38-43). Диктовать метку тоже нельзя (anti-dictation detectOtherSocialEng askuser-answer-parser.mjs:161-167; гасит грант в enforce-askuser-answer-parser.mjs:70).

ЛЁГКОЕwrite:<путь>, безопасный bash:, skill:<имя>. Владелец сам набирает FLOOR-ESCAPE: <action> в поле «Другое» ответа AskUserQuestion (toFloorEscapeRecord askuser-answer-parser.mjs:211-225). Контроллер кнопку-с-меткой не делает и метку не диктует. Грант одноразовый, окно 5 мин (escape-grant.mjs:17), точное совпадение canonicalAction. Как дать через окошко: контроллер показывает строку FLOOR-ESCAPE: <action> в чате (без императива рядом — иначе anti-dictation), владелец копирует её в поле «Другое» окошка (из обычного чата грант не пишется). Escape-окошки освобождены от anti-cosmetic-стража (isEscapeAuthQuestion).

ТЯЖЁЛОЕowner-seal:<hash>, ops-runbook:<hash>, commit:<hash>, plan-done, gate3-arb:*, разрушительный bash:/powershell:. Только из терминала владельца:

node tools/owner-consent.mjs <sessionId> <action>

Скрипт подписывает грант ключом keychain (origin:owner-terminal, owner-consent.mjs:16-27). Контроллер подделать не может: нет ключа → читатель отвергает (fail-closed, escape-grant.mjs:120-129); ~/.claude/runtime ему писать нельзя (пол). Тяжёлые гранты окно 5 мин не режут — окно = существование опечатанного плана (loadPlanScopedGrants, escape-grant.mjs:131-157).

Каноны <action> (canonicalAction escape-grant.mjs:30-52): bash:<норм.команда> · powershell:<норм.команда> · skill:<имя lowercase> · write:<путь lowercase, прямые /, NFC> · mcp:<имя>:<args-json>. Путь для write: — абсолютный, нижний регистр, прямые слэши.

Что escape чтит: стена / пол / судья / гейт памяти (enforce-normative-content-rules). НЕ чтит: normative §6 для discipline-source (код tools/enforce-* правь в терминале — write-escape его не снимает).

Активация (обязательна в settings.json): PostToolUse AskUserQuestionenforce-askuser-answer-parser (writer, settings.json:205) + Edit|Write|MultiEdit|Bash|PowerShellenforce-floor-escape-consume (one-shot гашение, settings.json:215). Нет регистрации — клик уходит «в никуда».

Упавший инструмент грант НЕ гасит (можно повторить). Грант одноразовый — все правки файла одним Edit (широкий old_string) или отдельный грант на каждый.


5. Коммит и пуш

Гейт присутствия (enforce-router-gate) у нас НЕ подключён — его нет в settings.json (файл существует, но мёртв по дизайну). Значит approve_git_operation не нужен; контроль коммита — на гейтах качества и согласии:

  • enforce-verify-gate (settings.json:125) — на git commit/push требует свежую подписанную расписку ~/.claude/runtime/verify-receipt.json. Производит node tools/produce-verify-receipt.mjs (гонит tools-сюиту, подписывает по отпечатку staged-diff). docs-only .md — короткозамкнут (isDocsOnlyChange).
  • enforce-criterion-gate (settings.json:135) — на код-коммит требует по-критерийный GREEN (тест прошёл И мутация убита) + валидный frozen-plan. docs-only — тоже короткозамкнут.

Коммит силами агента (D2): одно тяжёлое согласие FLOOR-ESCAPE: commit:<plan-hash> (терминал владельца) снимает гейт присутствия; гейты качества остаются; force-push/--no-verify блокируются всегда. Деплой (D1): FLOOR-ESCAPE: ops-runbook:<plan-hash>, окно = до конца плана; ядерное — всё равно per-command.

Коммит/пуш шагами опечатанного плана: {op:Write,object:".git/CB_MSG.txt"}git add <путь>git commit -F .git/CB_MSG.txtgit push gitea main. Проходит наставника/судью, ЕСЛИ в плане есть обоснование, что docs-only git-команды входят в объявленный claude-md-management (отдельного git-скила в реестре нет). Сообщение через файл (пол режет <email>/цепочки в -m); paren-free; трейлер Co-Authored-By: Claude Opus 4.8.

Чище — терминал владельца (минует все гейты): $env:LEFTHOOK="0"; git commit … ; $env:LEFTHOOK=$null. Нужен полный LEFTHOOK=0 (PostToolUse markdownlint --fix правит .md и рвёт git-stash, settings.json:167). На pre-existing находках pre-push — LEFTHOOK_EXCLUDE=<hook> git push (не глухой LEFTHOOK=0 — судья флагует [heavy]).

Доступно под стеной: git status/diff/log/add/commit. НЕ доступно: restore/stash/reset/checkout.


6. Субагенты под стеной — только разведка

op:"Task" шагом запрещён, субагент-мутатор под стеной не живёт: ломает три опоры подотчётности (в коде enforce-supreme-gate.mjs/plan-lock.mjs):

  1. печать кроет конкретные op+object-шаги (actionMatchesStep) — не «пошли работника»;
  2. пред-запись намерения контроллера в журнал перед каждой правкой (Δ3, «нет записи — нет действия»);
  3. один указатель по одному дереву. Плюс субагенту нельзя доверять мутацию (память subagent-falsified-test-results: фальсифицировал «5 pass»). Под стеной его Read режет ДР-1, его Write не совпадёт с шагом → блок. Доп-страж — enforce-subagent-return-scanner ловит narrative-claim «N тестов прошли» без прогона.

Как помогают: субагент только разведывает (read-only, возврат текстом), гоняй в разговорном режиме до печати; контроллер берёт текст и сам пишет шагами плана. Так проходит даже agent-as-LLM сборка (напр. graphify-экстракторы возвращают JSON текстом → контроллер пишет чанки + запускает скрипт шагами).


7. Судьи-машины и печать

Оркестратор enforce-mentor-then-judge (PostToolUse Write, settings.json:185, timeout 300с) гонит наставника → судью строго по очереди; при ОБОИХ GO артефакт печатается сам. Печать не проси у владельца — это не ручное действие.

  • Тупой судья навыков (enforce-domain-skill-discipline settings.json:65): навыки из skills-json опечатанного плана обязаны быть реально вызваны (журнал) до первого мутирующего шага, иначе блок. Объявленный навык стена пускает к вызову (isPlanDeclaredSkill, матч по суффиксу).
  • Критерий-судья (enforce-criterion-gate): на git commit/push через Claude — по-критерийный GREEN. Коммиты в терминале владельца минуют.
  • Видимость вердиктов (enforce-verdict-surface, UserPromptSubmit settings.json:302): в начале следующего хода всплывают подписанные строки 🧭 РОУТЕР / 🧑‍🏫 НАСТАВНИК / ⚖️ СУДЬЯ. Вердикт у владельца спрашивать больше не надо. Best-effort/fail-open.
  • Память кругов (round-memory-*): круг 1 слеп; круг 2+ судья/наставник видят свои прошлые претензии дословно + доводы контроллера из ## Переговоры → не противоречат себе.
  • Арбитраж (enforce-gate3-loop, Stop settings.json:262): на 3-м NO-GO подряд — карточка: «держусь контроллера» (owner-seal:<hash>, перевешивает NO-GO) / «согласен» (переделать) / «своё решение». owner-seal — тяжёлое (терминал).

Правила судьи к плану (DR-1): каждый мутирующий шаг проверяем (после Edit/Write — Bash-проверка); два Edit одного файла подряд без Bash между — запрещены (MultiEdit недоступен → один аккуратный old_string); дублирующие шаги убрать.

Правки памяти/CLAUDE.md под стеной: объяви claude-md-management в skills-json И реально вызови до записи — иначе гейт памяти/судья навыков заблокируют. (Тяжёлая нормативка — чище в штатном.)


8. Частые ошибки и тупики

  • Печать ≠ escape. escape только снимает блок (block:false, указатель не двигает); печать встаёт ТОЛЬКО при wired===true && decision==='GO' (или owner-seal). ⚠️ Два разных канала владельца: chat-escape (loadFloorEscapes, лёгкий) разблокирует ОТДЕЛЬНОЕ действие, но режим исполнения НЕ откроет — для печати на арбитраже нужен терминальный owner-seal:<hash>. Не путать.
  • Ловушка устаревшего указателя. План байт-в-байт = тот же planId → старая позиция не сбрасывается. Лечение: другой план (другое имя/шаги) ИЛИ FLOOR-ESCAPE: plan-done. Удаление файлов не помогает (позиция в runtime).
  • Десинк указателя (F-J). Ранний хук (supreme-gate) сдвинул указатель, поздний PreToolUse-блокер (skill-discipline) уронил действие → шаг потерян. Профилактика: объявленные навыки вызови ПЕРВЫМ делом после печати, до первого мутирующего/Bash-шага. Сброс — новый план / plan-done.
  • Молчаливый срыв наставника (№7). main() в enforce-mentor-on-plan-write.mjs обёрнут в catch {}; throw между роутером и LLM-вердиктом (напр. в renderSkillContext, on-plan-write.mjs вне try) глотается — нет вердикта, нет записи, нет ошибки, «будто ещё считает». При «застряло»: сначала читай код (в разговорном свободно), не опрашивай снимок по кругу; лечение — перезапуск плана новым именем (свежий planId; чуть варьируй шаг, напр. --reporter dot).
  • H4 — наставник видит только steps-json. Тело плана/код/шаблоны в его промпт не идут (mentor-verdict.mjs), поэтому требует «покажи шаблон»/«создай заглушки» даже когда модули есть. Судья же видит тело — асимметрия, слепой наставник бьёт первым. Сверяй с реальностью; опасное (заглушки поверх рабочих) не выполнять; доводы — в ## Переговоры.
  • Недетерминизм судьи. Запрос к LLM без temperature:0/seed (router-classifier.mjs) → байт-идентичный текст может дать GO/NO-GO по-разному; невалидный JSON fail-closed → [fatal]/NO-GO без текста. «retry новым именем» работает потому что это новый сэмпл, а не из-за имени.
  • wired:false = три причины (degraded / стёртый mentor-GO / транспорт) с разными лечениями — различай по seal-attempts.jsonl (Grep, не Read).
  • CRLF ломает vitest@4 (№8). SyntaxError без позиции, а node --check/esbuild чисты → подозревай CRLF (autocrlf при checkout/merge), не синтаксис. Лечение: .gitattributes * text=auto eol=lf. Harness-collapse: полный npx vitest через Claude-Bash рушит воркеры (Cannot read properties of undefined (reading 'config')) — авторитетный свод гонит владелец в своём терминале.
  • Наставник контаминирует критику между артефактами (требует убрать блок, которого нет) — сверь с содержимым, ложняк не плодит версии.

9. Только владелец (штатный режим, снятие печати)

Штатный режим (standby-mode-control.mjs, UserPromptSubmit settings.json:311) — двухтактный хендшейк:

  1. фраза «штатный режим» → хук просит подтверждение;
  2. владелец подтверждает дословно «да, штатный» → стена off;
  3. вернуть стену — «выключи штатный».

Claude обязан запросить подтверждение, а не считать штатный включённым по просьбе. В штатном остаются: пол + точки возврата (enforce-snapshot) + проверка-перед-пушем (verify/criterion); снимается только стена (standbyActive проверяется в enforce-supreme-gate.mjs). Сброс штатного — на SessionStart (standby-session-reset.mjs), поэтому после перезапуска сессии его надо включать заново.

Снятие застрявшей печати (владелец): новый план с другим именем; ИЛИ FLOOR-ESCAPE: plan-done; ИЛИ maintenance — заменить весь блок "hooks": {…} на "hooks": {} + перезапуск Claude Code. Перезапуск ≠ сброс печати (план/указатель живут в runtime отдельно от settings).

Чтение ~/.claude/runtime контроллеру закрыто (enforce-read-path-deny); вердикты — через enforce-verdict-surface или Grep по runtime.


10. Что под капотом (хуки + env)

PreToolUse (порядок в settings.json): enforce-floor (пол) → enforce-domain-skill-discipline (судья навыков) → enforce-supreme-gate (стена) → enforce-snapshot (точки возврата) → enforce-normative-content-rules (§6 память/нормативка) → enforce-read-path-denyenforce-mcp-classificationenforce-verify-gateenforce-criterion-gateenforce-todowrite-skill-verifieraskuser-cosmetic-detector. PostToolUse: enforce-mentor-then-judge (Write) · enforce-skill-journaler (Skill) · enforce-askuser-answer-parser + enforce-floor-escape-consume (escape) · markdownlint-fix .md. Stop: observer / cost / enforce-coverage-verify / enforce-verdict-ack / enforce-gate3-loop (арбитраж). SessionStart: floor-manifest-check (сигнал, не блок) / warmup / standby-session-reset. UserPromptSubmit: enforce-verdict-surface / standby-mode-control.

Живой шов требует env: ROUTER_MENTOR_SEAM_ENABLED=1, ROUTER_MENTOR_JUDGE_ENABLED=1, ROUTER_MENTOR_JUDGE_MODE=block + ключи наставника/судьи/роутера. Нет ключа → degraded (wired:false).

Не править машинерию стены под стеной. tools/enforce-*, judge-*, mentor-*, floor-*, escape-grant, plan-lock — discipline-source: enforce-normative-content-rules §6 блокирует их правку, и write-escape это НЕ снимает. Правь в терминале (string-replace по якорям + бэкап + verify) и сразу коммить.

↑ наверх