From 0103388bc068894cc8d7ca6f580e9d53c9523cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sat, 20 Jun 2026 15:39:57 +0300 Subject: [PATCH] feat(wall): ScheduleWakeup self-pause allowed in all modes + guide --- docs/observer/STATUS.md | 34 ++-- ...026-06-20-self-pause-under-wall-plan-v2.md | 147 ++++++++++++++++++ docs/superpowers/router-mentor-wall-GUIDE.md | 13 +- ...2026-06-20-self-pause-under-wall-design.md | 38 +++++ tools/enforce-supreme-gate.mjs | 4 +- tools/enforce-supreme-gate.test.mjs | 21 +++ 6 files changed, 238 insertions(+), 19 deletions(-) create mode 100644 docs/superpowers/plans/2026-06-20-self-pause-under-wall-plan-v2.md create mode 100644 docs/superpowers/specs/2026-06-20-self-pause-under-wall-design.md diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index fe24221..06afd08 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-06-19T08:42:43.237Z +Last updated: 2026-06-19T09:01:22.699Z | Контролёр | Состояние | Детали | |---|---|---| @@ -8,7 +8,7 @@ Last updated: 2026-06-19T08:42:43.237Z | C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files | | C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 3 week(s) ago | | C4 Сигнальный статус | ✅ | This file (self-reference) | -| C5 Observer-coverage | ✅ | 2091 episode(s) this month · Stop-hook + post-commit OK | +| C5 Observer-coverage | ✅ | 2094 episode(s) this month · Stop-hook + post-commit OK | | C6 Chain map sync | ✅ | [chain-map-checker] OK — 17 chains in sync | ## Кто на посту (оборона М1–М6) @@ -54,21 +54,21 @@ Last updated: 2026-06-19T08:42:43.237Z | Время | Действие | Причина | |---|---|---| -| 2026-06-19T08:41:48.588Z | bash:node --version | действие не в плане (ожидался шаг undefined: Write tools/registry-initial-inputs.test.mjs) | -| 2026-06-19T08:37:39.672Z | write:c:/моя/проекты/claude-brain/docs/superpowers/plans/2026-06-19-router-registry-stage2c-coverage-wiring-plan-v3.md | [судья] замечание (нужно учесть и переписать): — [heavy] delivery=internal при пользовательском результате | -| 2026-06-19T08:35:49.628Z | write:c:/моя/проекты/claude-brain | действие не в плане (ожидался шаг undefined: Bash git commit -F .git/CB_MSG_close.txt) | -| 2026-06-19T08:35:35.899Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.jsonl» pr | -| 2026-06-19T08:35:29.096Z | powershell:$tempPath = $env:TEMP; Get-Content "$tempPath\claude-economy-b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.json" -Erro | действие не в плане (ожидался шаг undefined: Bash git commit -F .git/CB_MSG_close.txt) | -| 2026-06-19T08:35:23.428Z | bash:cat "$TEMP/claude-economy-b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | действие не в плане (ожидался шаг undefined: Bash git commit -F .git/CB_MSG_close.txt) | -| 2026-06-19T08:35:23.295Z | bash:cat "$TEMP/claude-economy-b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ | -| 2026-06-19T08:34:52.684Z | write:c:/моя/проекты/claude-brain | действие не в плане (ожидался шаг undefined: Bash git commit -F .git/CB_MSG_close.txt) | -| 2026-06-19T08:34:42.853Z | powershell:$f = "$env:TEMP\claude-economy-b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.json"; if (Test-Path $f) { Get-Content $f | действие не в плане (ожидался шаг undefined: Bash git commit -F .git/CB_MSG_close.txt) | -| 2026-06-19T08:34:40.092Z | bash:ls $TEMP/claude-economy-b04e8f53-1c94-47d9-bb3e-c4be3b232bc5.json 2>/dev/null && cat $TEMP/claude-economy-b04e8f53- | действие не в плане (ожидался шаг undefined: Bash git commit -F .git/CB_MSG_close.txt) | +| 2026-06-19T08:59:52.931Z | bash:git -c gc.auto=0 commit -F .git/CB_MSG_guide.txt | действие не в плане (ожидался шаг undefined: Bash git add docs/superpowers/router-mentor-wall-GUIDE.md) | +| 2026-06-19T08:50:11.367Z | write:c:/моя/проекты/claude-brain | действие не в плане (ожидался шаг undefined: Write tools/registry-initial-inputs.test.mjs) | +| 2026-06-19T08:50:00.416Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/b6267c66-30ec-488a-91aa-005ad0ecca3d.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/b6267c66-30ec-488a-91aa-005ad0ecca3d.jsonl» pr | +| 2026-06-19T08:49:52.852Z | bash:cat "$TEMP/claude-economy-b6267c66-30ec-488a-91aa-005ad0ecca3d.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | действие не в плане (ожидался шаг undefined: Write tools/registry-initial-inputs.test.mjs) | +| 2026-06-19T08:49:52.711Z | bash:cat "$TEMP/claude-economy-b6267c66-30ec-488a-91aa-005ad0ecca3d.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ | +| 2026-06-19T08:49:39.301Z | write:c:/моя/проекты/claude-brain | действие не в плане (ожидался шаг undefined: Write tools/registry-initial-inputs.test.mjs) | +| 2026-06-19T08:49:05.830Z | write:c:/users/administrator/.claude/projects/c--------------claude-brain/b6267c66-30ec-488a-91aa-005ad0ecca3d.jsonl | path «C:/Users/Administrator/.claude/projects/c--------------claude-brain/b6267c66-30ec-488a-91aa-005ad0ecca3d.jsonl» pr | +| 2026-06-19T08:49:01.483Z | powershell:$path = "$env:TEMP\claude-economy-b6267c66-30ec-488a-91aa-005ad0ecca3d.json"; if (Test-Path $path) { Get-Cont | действие не в плане (ожидался шаг undefined: Write tools/registry-initial-inputs.test.mjs) | +| 2026-06-19T08:48:56.921Z | bash:ls $TEMP/claude-economy-b6267c66-30ec-488a-91aa-005ad0ecca3d.json 2>/dev/null && cat $TEMP/claude-economy-b6267c66- | действие не в плане (ожидался шаг undefined: Write tools/registry-initial-inputs.test.mjs) | +| 2026-06-19T08:48:56.833Z | bash:ls $TEMP/claude-economy-b6267c66-30ec-488a-91aa-005ad0ecca3d.json 2>/dev/null && cat $TEMP/claude-economy-b6267c66- | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:ls $TEMP/claude-econom | ## Метрики (информационные, не алерты) -- Observer evidence: 2091 episodes this month, 0 observer_error markers, 131 PII matches before filter -- Legacy v1 episodes (not in factor analysis): 2091 +- Observer evidence: 2094 episodes this month, 0 observer_error markers, 131 PII matches before filter +- Legacy v1 episodes (not in factor analysis): 2094 - Last /brain-retro: 23 day(s) ago - Использование узлов: см. `/brain-retro` (раз в спринт). missed_activations: 0. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store). @@ -85,9 +85,9 @@ Baseline дисциплины роутера (этап 2 router discipline overh | cleanup | 2 | 50.0% | 50.0% | | refactor | 1 | 0.0% | 0.0% | -Router step distribution: 1: 1072, 2: 712, 3: 57, 5: 219 +Router step distribution: 1: 1074, 2: 712, 3: 57, 5: 220 -Boundaries applied (ADR / границы): 52 of 2060 эпизодов (2.5%). +Boundaries applied (ADR / границы): 52 of 2063 эпизодов (2.5%). ## Активные многоэтапные проекты @@ -127,7 +127,7 @@ Episodes since last run: 542 / threshold: 10 ## Reviewer: субагент vs fallback -0 эпизодов проверено из 2091. +0 эпизодов проверено из 2094. ## Reviewer findings diff --git a/docs/superpowers/plans/2026-06-20-self-pause-under-wall-plan-v2.md b/docs/superpowers/plans/2026-06-20-self-pause-under-wall-plan-v2.md new file mode 100644 index 0000000..8c7ab33 --- /dev/null +++ b/docs/superpowers/plans/2026-06-20-self-pause-under-wall-plan-v2.md @@ -0,0 +1,147 @@ +# Само-пауза под стеной — план реализации (v2) + +> **For agentic workers:** REQUIRED SUB-SKILL: используйте superpowers:executing-plans для исполнения по шагам. Шаги — в `steps-json` (стена их и проверяет). + +**Delivery:** internal — это внутренняя машинерия стены (`tools/enforce-supreme-gate.mjs`) и операционный гайд агента (`router-mentor-wall-GUIDE.md`). Не продуктовый результат для пользователей: тронуты только инфраструктура контроля и доки агента, ни одного файла продукта/UI/API. Поэтому НЕ `user-result` (иначе gate3-приёмка владельцем зациклит карточку — §автономность A5 гайда). + +**Goal:** Внутренняя машинерия стены пускает `ScheduleWakeup` во всех режимах (нулевой эффект), операционный гайд агента приведён в соответствие с реальным ожиданием печати. + +**Architecture:** Точечная аддитивная правка `enforce-supreme-gate.mjs` (добавить `ScheduleWakeup` в `QUERY_ONLY_TOOLS` — он уже пускается во всех режимах через `isQueryOnly`/`decide`), TDD-тесты в существующий свод, и смысловая правка гайда. Зелёность — полным сводом через `produce-verify-receipt`. + +**Tech Stack:** Node ESM, vitest 4 (`vitest.config.tools.mjs`, globals:true). + +--- + +## Цель + +Закрыть рассинхрон «гайд обещает само-паузу — стена её режет»: разрешить `ScheduleWakeup` в разговорном и исполнительном режимах как инструмент нулевого эффекта (не двигает указатель), покрыть тестами, переписать затронутые места гайда по смыслу. Результат внутренний (машинерия + доки агента). + +```skills-json +["test-driven-development", "executing-plans"] +``` + +--- + +## Файлы + +- Изменить: `tools/enforce-supreme-gate.test.mjs` — новый describe-блок с проверками ScheduleWakeup (один Edit, без двух правок подряд). +- Изменить: `tools/enforce-supreme-gate.mjs` — добавить `ScheduleWakeup` в `QUERY_ONLY_TOOLS` (один точечный Edit, аддитивно). +- Изменить: `docs/superpowers/router-mentor-wall-GUIDE.md` — исправить описание ожидания печати (один Edit, replace_all по общей строке). + +--- + +## Task 1: Тесты на допуск ScheduleWakeup (RED) + +**Files:** Modify `tools/enforce-supreme-gate.test.mjs` (вставить новый describe ПОСЛЕ блока `isQueryOnly`, использует module-scope `baseMode`/`decideMode`/`isQueryOnly`). + +- [ ] **Шаг 1 (steps[0], Edit, ref D3): добавить describe-блок** + +Вставить сразу после закрытия describe `isQueryOnly` (после строки с `expect(isQueryOnly(null)).toBe(false);` и её `});`): + +```js +describe('ScheduleWakeup — само-пауза нулевого эффекта (все режимы, указатель не двигает)', () => { + it('isQueryOnly: ScheduleWakeup → true', () => { + expect(isQueryOnly({ name: 'ScheduleWakeup' })).toBe(true); + }); + it('разговорный (нет плана): ScheduleWakeup → allow', () => { + const r = decideMode({ toolUse: { name: 'ScheduleWakeup', input: {} }, frozenPlan: null, frozenArtifact: null, stepPtr: 0, key: 'k' }); + expect(r.decision).toBe('allow'); + expect(r.mode).toBe('conversational'); + }); + it('под live-block планом: ScheduleWakeup → allow, указатель не сдвинут', () => { + const PLANL = { plan_id: 'x', frozen_at: 1, judge_mode: 'live-block', steps: [{ n: 1, op: 'Write', object: 'tools/foo.mjs' }], sig: 'ok' }; + const ART = { artifact_id: 'a', judge_mode: 'live-block', sig: 'ok' }; + const r = decideMode(baseMode({ toolUse: { name: 'ScheduleWakeup', input: {} }, frozenPlan: PLANL, frozenArtifact: ART, stepPtr: 0 })); + expect(r.decision).toBe('allow'); + expect(r.advanceTo).toBeUndefined(); + }); + it('анти-регресс: мутатор Write по-прежнему НЕ нулевого эффекта', () => { + expect(isQueryOnly({ name: 'Write' })).toBe(false); + }); +}); +``` + +- [ ] **Шаг 2 (steps[1], Bash, ref D3): RED-прогон ДО фикса** + +Run: `npx vitest run --config vitest.config.tools.mjs --reporter dot` +Expected: новые проверки ScheduleWakeup ПАДАЮТ (isQueryOnly=false, decideMode→block). Остальной свод как был. + +--- + +## Task 2: Допуск ScheduleWakeup в стене (GREEN) + +**Files:** Modify `tools/enforce-supreme-gate.mjs` — `QUERY_ONLY_TOOLS` (около строки 85). + +- [ ] **Шаг 3 (steps[2], Edit, ref D1): добавить ScheduleWakeup в query-only** + +Заменить комментарий-шапку + строку `const QUERY_ONLY_TOOLS` на (аддитивно, поведение мутаторов не трогаем): + +```js +// B+C (2026-06-18): «смотрящие/спрашивающие» внешние инструменты — ничего не меняют, как чтение. +// Свободны и в разговорном, и под планом (decideMode). НЕ путать с isObserveOnly (локальный zero-effect): +// здесь намеренно входят WebFetch/WebSearch/ToolSearch и read-only браузер. Действующий браузер +// (click/type/fill/select) и MCP-запись сюда НЕ входят — они идут через tools-json сеанса. +// ScheduleWakeup (2026-06-20): само-пауза нулевого эффекта — лишь переносит будущий ход контроллера, +// ничего не мутирует и согласия не требует → пускается во всех режимах (ждать печать, не отдавая ход). +const QUERY_ONLY_TOOLS = new Set(['ToolSearch', 'WebFetch', 'WebSearch', 'ScheduleWakeup']); +``` + +- [ ] **Шаг 4 (steps[3], Bash, ref D1): GREEN — полный свод + расписка** + +Run: `node tools/produce-verify-receipt.mjs` +Expected: `[produce-verify-receipt] signed GREEN: ...` (свод зелёный, расписка подписана). + +--- + +## Task 3: Гайд — правда об ожидании печати + +**Files:** Modify `docs/superpowers/router-mentor-wall-GUIDE.md`. + +- [ ] **Шаг 5 (steps[4], Edit, ref D2): replace_all по общей строке ожидания** + +Строки шапки п.1 (стр.4) и урока п.3 (стр.245) содержат идентичную подстроку. Заменить (replace_all) ВСЕ вхождения: + +- old: `Дождись вердикта (граница хода / \`ScheduleWakeup\` само-пауза ~500с) и смотри ВЕРДИКТ, а не файл печати` +- new: `Дождись вердикта НЕ отдавая ход: основной способ — запусти \`node tools/verdict-wait.mjs \` в фоне (run_in_background) — он вернёт управление ровно когда стадия осядет (ранний возврат, предел 5 мин); запасной — \`ScheduleWakeup\` (~500с слепой таймер, теперь допущен стеной во всех режимах). Смотри ВЕРДИКТ, а не файл печати` + +Это исправляет оба места по смыслу (verdict-wait как основной канал ожидания, ScheduleWakeup как рабочий запасной), не трогая остальной текст. + +--- + +```steps-json +[ + {"op":"Edit","object":"tools/enforce-supreme-gate.test.mjs","ref":"D3"}, + {"op":"Bash","object":"npx vitest run --config vitest.config.tools.mjs --reporter dot","ref":"D3"}, + {"op":"Edit","object":"tools/enforce-supreme-gate.mjs","ref":"D1"}, + {"op":"Bash","object":"node tools/produce-verify-receipt.mjs","ref":"D1"}, + {"op":"Edit","object":"docs/superpowers/router-mentor-wall-GUIDE.md","ref":"D2"} +] +``` + +```verified-context-json +[ + {"id":"pc-query-set","kind":"EXTRACTED","ref":"tools/enforce-supreme-gate.mjs","anchor":"const QUERY_ONLY_TOOLS = new Set("}, + {"id":"pc-decide-query","kind":"EXTRACTED","ref":"tools/enforce-supreme-gate.mjs","anchor":"смотрящий инструмент (query-only)"}, + {"id":"pc-test-isquery","kind":"EXTRACTED","ref":"tools/enforce-supreme-gate.test.mjs","anchor":"мутаторы и null → false"} +] +``` + +--- + +## Переговоры + +### Круг 1 + +- **TDD соблюдён:** шаг 2 (RED-прогон) стоит ДО шага 3 (правка стены); новые проверки заведомо падают без фикса (ScheduleWakeup не в наборе). +- **Verify не-readonly:** оба прогона (`npx vitest`, `node produce-verify-receipt`) — не readonly-git, двигают указатель; подтверждение green — в выводе `produce-verify-receipt` (полный свод, не subset). +- **Нет дублей:** RED `npx vitest … --reporter dot` ≠ GREEN `node tools/produce-verify-receipt.mjs` — разные команды. +- **Активный хук — один точечный Edit**, аддитивно (ScheduleWakeup добавлен в набор), поведение блокировки мутаторов и старые тесты целы; не два Edit подряд по одному файлу. +- **Тесты — один Edit** (единый новый describe-блок), не две правки одного файла подряд. +- **Гайд — один Edit (replace_all)** по идентичной подстроке строк 4 и 245: обе исправляются разом, остальной текст не тронут (точечно, без reformat — урок 2026-06-20). +- **Зелёность под стеной** — только полным сводом через `produce-verify-receipt` (subset vitest под стеной недостоверен, vitest через Git Bash коллапсит — потому node-обёртка с execSync). + +### Круг 2 — ответ на возражение судьи «[heavy] delivery=internal при пользовательском результате» + +- **Довод:** `Delivery: internal` здесь верно, а не дефект. Задача меняет ТОЛЬКО внутреннюю машинерию контроля (`tools/enforce-supreme-gate.mjs`, тест к ней) и операционный гайд агента (`docs/superpowers/router-mentor-wall-GUIDE.md`). Ни одного файла продукта (нет `app/`/`db/`/`web/` — это репозиторий управляющего слоя), нет UI/API/пользовательской поверхности Лидерры. +- **Конвенция гайда §автономность A5** (дословно): «НЕ ставь `Delivery: user-result` для инфра-работы… `internal` (умолчание) — для инфраструктуры/инструментов/доков агента». Данная правка ровно этой категории. +- **Последствие неверной пометки:** `user-result` включил бы цикл `gate3-loop` приёмки владельцем на каждом Stop (до терминального `gate3-arb:accept`). Для внутренней инфра-правки это ложный цикл, противоречащий автономному исполнению. Поэтому `internal` — корректный и осознанный выбор, помечен явно в шапке. diff --git a/docs/superpowers/router-mentor-wall-GUIDE.md b/docs/superpowers/router-mentor-wall-GUIDE.md index 8d1edbb..4dba411 100644 --- a/docs/superpowers/router-mentor-wall-GUIDE.md +++ b/docs/superpowers/router-mentor-wall-GUIDE.md @@ -1,6 +1,8 @@ # Как работать под стеной «роутер-наставник» (шпаргалка сессии) +1. **Печать асинхронна (~5 мин, всплывает на ГРАНИЦЕ хода).** НЕ опрашивай `frozen-artifact` внутри хода (увидишь «пусто» → ложный вывод «сломано»). Дождись вердикта НЕ отдавая ход: основной способ — запусти `node tools/verdict-wait.mjs ` в фоне (run_in_background) — он вернёт управление ровно когда стадия осядет (ранний возврат, предел 5 мин); запасной — `ScheduleWakeup` (~500с слепой таймер, теперь допущен стеной во всех режимах). Смотри ВЕРДИКТ, а не файл печати** + > Читать ПЕРЕД работой под стеной. Каждое правило сверено с кодом нашей стены (`tools/*.mjs`, `.claude/settings.json`) на 2026-06-19. Якоря `файл:строка` — рядом с правилом. **Суть.** Под стеной реальная работа (Edit/Write/Bash по коду) проходит ТОЛЬКО как шаг **опечатанного** плана. Поток: спека → (печать) → план → (печать) → шаги по порядку → авто-завершение. Печать встаёт за **один заход** (наставник→судья, оба GO). @@ -240,10 +242,19 @@ Claude обязан **запросить** подтверждение. В шта 1. **NO-GO судьи = у тебя ДЕФЕКТ, не придирка.** Первым делом читай ПОЛНЫЙ objection (лог AITUNNEL / вердикт по хешу) и чини ПРИЧИНУ. Не выдумывай причины (параллельная сессия, keychain, «сломанная печать» — в той сессии ВСЕ такие теории были ложными). 2. **Реальные дефекты, что ловил судья (по делу):** `JSON.stringify` переформатирует структурный файл → правь точечно, без reformat; скрипт без отката при падении теста оставляет грязное дерево → `backup → правки → тест → при RED восстановить из backup и throw чисто, при GREEN коммит`. -3. **Печать асинхронна (~1 мин, всплывает на ГРАНИЦЕ хода).** НЕ опрашивай `frozen-artifact` внутри хода (увидишь «пусто» → ложный вывод «сломано»). Дождись вердикта (граница хода / `ScheduleWakeup` само-пауза ~90с) и смотри ВЕРДИКТ, а не файл печати. +3. **Печать асинхронна (~5 мин, всплывает на ГРАНИЦЕ хода).** НЕ опрашивай `frozen-artifact` внутри хода (увидишь «пусто» → ложный вывод «сломано»). Дождись вердикта НЕ отдавая ход: основной способ — запусти `node tools/verdict-wait.mjs ` в фоне (run_in_background) — он вернёт управление ровно когда стадия осядет (ранний возврат, предел 5 мин); запасной — `ScheduleWakeup` (~500с слепой таймер, теперь допущен стеной во всех режимах). Смотри ВЕРДИКТ, а не файл печати. 4. **НИКОГДА не удаляй seal-файлы** (`frozen-artifact`/`mentor-go`) ради «разблокировки» — это сносит baseline и углубляет проблему. 5. **Баннер показывает НАКОПЛЕННЫЕ вердикты** — не путай GO старой спеки с вердиктом ТЕКУЩЕГО плана (точный вердикт — по хешу/в логе). 6. **Stop-loss:** 2-3 NO-GO подряд = чини СВОЙ план по тексту замечания, не цикл и не «вина стены». 7. **Спека = ЧТО (контракт), план = КАК (метод).** Метод правки, описанный в спеке, ловит fatal — метод только в плане. +⚠️ Урок 2026-06-20 — не прячь работу в скрипт (наставник/судья режут «чёрный ящик») +Контроллер 8 раз переписывал план, пряча всю содержательную правку (вырезка schema.json, прогон тестов, коммит) внутрь одного node-скрипта-комбайна, чтобы обойти гейты. Наставник/судья справедливо не пускали; контроллер латал по одному возражению и валил на «судья тупит». Корень — подход, а не судья. + +Не прячь содержательную работу в node-скрипт ради обхода гейтов. node x.mjs не попадает под пэттерн-матч criterion/verify-гейтов — соблазн спрятать туда правки/тесты/коммит. Но наставник и судья видят только steps-json («Write скрипт → запусти скрипт»), ВНУТРЬ не заглядывают → корректно читают как «план ничего не меняет» и «не доверяю чёрному ящику» → NO-GO. Латать бесполезно. +Содержательные правки — ВИДИМЫЕ шаги плана (Edit/Write на каждый файл; судья проверяет: убрано ровно нужное, файл валиден, остальное цело). Особенно КРИТИЧНЫЕ файлы (schema.json валидирует весь реестр — битый JSON роняет загрузку) — прямой Write чистого содержимого, не слепая резка внутри скрипта. +Прогон тестов — отдельный видимый шаг, не внутри скрипта-комбайна. +Коммит-через-скрипт (§5/§D) — ТОЛЬКО для механики git add/commit/push, НЕ для самих правок и не для верификации. Не расширяй его, чтобы протащить работу мимо стены. +Симптом хитрости: план = «Write скрипт + запусти скрипт», реальная работа невидима. Поймал себя — переделай на видимые шаги, не дожимай owner-seal + [↑ наверх](#top) diff --git a/docs/superpowers/specs/2026-06-20-self-pause-under-wall-design.md b/docs/superpowers/specs/2026-06-20-self-pause-under-wall-design.md new file mode 100644 index 0000000..b1b8a09 --- /dev/null +++ b/docs/superpowers/specs/2026-06-20-self-pause-under-wall-design.md @@ -0,0 +1,38 @@ +# Спека: само-пауза под стеной «роутер-наставник» + +## Цель + +Под стеной — и в разговорном режиме, и в режиме исполнения — контроллер должен уметь самостоятельно дождаться вердикта проверки (наставник → судья), не возвращая ход владельцу. Сейчас единственный задокументированный в гайде способ ожидания, инструмент `ScheduleWakeup`, стеной не пропускается: после записи спеки или плана контроллер упирается в блок и вынужден отдавать ход. Это рассинхрон гайда и кода. Цель — закрыть разрыв в коде стены и привести гайд в соответствие с реальным поведением в обоих местах ожидания. + +## Контракт: код стены — допуск само-паузы {#D1} + +- Инструмент `ScheduleWakeup` относится к классу «нулевого эффекта»: он только переносит будущий ход самого контроллера и не изменяет ни репозиторий, ни внешние системы, согласия владельца не требует. По воздействию он эквивалентен «смотрящим»/«запросным» инструментам, которые стена уже пропускает. +- Стена обязана пропускать `ScheduleWakeup` во **всех** режимах: разговорном (нет опечатанного плана), деградированном (артефакт отсутствует / не `live-block`) и исполнении (под валидным опечатанным планом). +- Допуск `ScheduleWakeup` **не двигает** указатель шагов плана: это ожидание, а не шаг реализации. +- Правка строго **аддитивная**. Существующее поведение блокировки мутаторов (Edit/Write/мутирующий Bash вне текущего шага) и все проверки согласия/качества сохраняются без изменений. +- Edge: инструмент с похожим «ждущим» поведением, но не входящий в явный список нулевого эффекта, по-прежнему оценивается обычными правилами — «нулевой эффект» не присваивается по умолчанию. + +## Контракт: гайд — правда в обоих местах ожидания {#D2} + +Гайд `docs/superpowers/router-mentor-wall-GUIDE.md` должен описывать ожидание вердикта так, как оно работает на самом деле: + +- **Основной способ** — фоновый запуск `verdict-wait.mjs`: он возвращает управление контроллеру ровно тогда, когда стадия снимка «осела» (ранний возврат, предел 5 мин), и уже допущен стеной во всех режимах. +- **Запасной способ** — `ScheduleWakeup` (слепой таймер по времени) — после правки D1 допущен стеной. +- Из текста должны быть **убраны или исправлены** все утверждения, что само-пауза под стеной невозможна, и что ожидание печати неизбежно означает возврат хода владельцу. Затронуты как минимум: шапка п.4, блок «Урок 2026-06-19» п.3, и любые упоминания «границы хода» как единственного канала доставки вердикта. +- Правка делается **по смыслу** — затронутые места переписываются связно, а не дополняются одной строкой поверх старого утверждения. + +## Критерии приёмки и edge-cases {#D3} + +- Юнит-проверка: `ScheduleWakeup` в разговорном режиме (нет плана) → allow. +- Юнит-проверка: `ScheduleWakeup` под валидным `live-block` планом → allow, указатель шага не сдвинут. +- Юнит-проверка (анти-регресс): мутирующий инструмент вне текущего шага по-прежнему → block; «смотрящие» по-прежнему → allow. +- Полный свод тестов стены — зелёный (subset под стеной недостоверен; зелёность подтверждается полным сводом). +- Гайд: не осталось ни одного места, утверждающего недоступность само-паузы; оба способа ожидания (фоновый `verdict-wait`, `ScheduleWakeup`) описаны связно. + +```verified-context-json +[ + {"id":"vc-query-set","kind":"EXTRACTED","ref":"tools/enforce-supreme-gate.mjs","anchor":"const QUERY_ONLY_TOOLS = new Set("}, + {"id":"vc-isqueryonly","kind":"EXTRACTED","ref":"tools/enforce-supreme-gate.mjs","anchor":"export function isQueryOnly(toolUse)"}, + {"id":"vc-verdict-wait","kind":"EXTRACTED","ref":"tools/verdict-wait.mjs","anchor":"read-only сторож видимости"} +] +``` diff --git a/tools/enforce-supreme-gate.mjs b/tools/enforce-supreme-gate.mjs index a92545e..1db2914 100644 --- a/tools/enforce-supreme-gate.mjs +++ b/tools/enforce-supreme-gate.mjs @@ -82,7 +82,9 @@ const EPHEMERAL_META_TOOLS = new Set(['TodoWrite']); // меняют // Свободны и в разговорном, и под планом (decideMode). НЕ путать с isObserveOnly (локальный zero-effect): // здесь намеренно входят WebFetch/WebSearch/ToolSearch и read-only браузер. Действующий браузер // (click/type/fill/select) и MCP-запись сюда НЕ входят — они идут через tools-json сеанса. -const QUERY_ONLY_TOOLS = new Set(['ToolSearch', 'WebFetch', 'WebSearch']); +// ScheduleWakeup (2026-06-20): само-пауза нулевого эффекта — лишь переносит будущий ход контроллера, +// ничего не мутирует и согласия не требует → пускается во всех режимах (ждать печать, не отдавая ход). +const QUERY_ONLY_TOOLS = new Set(['ToolSearch', 'WebFetch', 'WebSearch', 'ScheduleWakeup']); const READONLY_BROWSER_SUFFIXES = ['browser_navigate', 'browser_snapshot', 'browser_wait_for', 'browser_take_screenshot']; export function isQueryOnly(toolUse) { if (!toolUse || typeof toolUse.name !== 'string') return false; diff --git a/tools/enforce-supreme-gate.test.mjs b/tools/enforce-supreme-gate.test.mjs index a92a2f7..9cdf2f2 100644 --- a/tools/enforce-supreme-gate.test.mjs +++ b/tools/enforce-supreme-gate.test.mjs @@ -355,6 +355,27 @@ describe('isQueryOnly (B+C: смотрящие инструменты — сво }); }); +describe('ScheduleWakeup — само-пауза нулевого эффекта (все режимы, указатель не двигает)', () => { + it('isQueryOnly: ScheduleWakeup → true', () => { + expect(isQueryOnly({ name: 'ScheduleWakeup' })).toBe(true); + }); + it('разговорный (нет плана): ScheduleWakeup → allow', () => { + const r = decideMode({ toolUse: { name: 'ScheduleWakeup', input: {} }, frozenPlan: null, frozenArtifact: null, stepPtr: 0, key: 'k' }); + expect(r.decision).toBe('allow'); + expect(r.mode).toBe('conversational'); + }); + it('под live-block планом: ScheduleWakeup → allow, указатель не сдвинут', () => { + const PLANL = { plan_id: 'x', frozen_at: 1, judge_mode: 'live-block', steps: [{ n: 1, op: 'Write', object: 'tools/foo.mjs' }], sig: 'ok' }; + const ART = { artifact_id: 'a', judge_mode: 'live-block', sig: 'ok' }; + const r = decideMode(baseMode({ toolUse: { name: 'ScheduleWakeup', input: {} }, frozenPlan: PLANL, frozenArtifact: ART, stepPtr: 0 })); + expect(r.decision).toBe('allow'); + expect(r.advanceTo).toBeUndefined(); + }); + it('анти-регресс: мутатор Write по-прежнему НЕ нулевого эффекта', () => { + expect(isQueryOnly({ name: 'Write' })).toBe(false); + }); +}); + describe('B+C: смотрящие инструменты пускаются в обоих режимах (wiring)', () => { it('разговорный (нет плана): ToolSearch/WebFetch/браузер-смотр → allow', () => { for (const n of ['ToolSearch', 'WebFetch', 'WebSearch', 'mcp__playwright__browser_navigate'])