Files
brain/docs/superpowers/specs/2026-06-18-wall-interactive-session-design.md
T
Дмитрий e9ba6fb9a2 feat: B+C part1 - смотрящие инструменты свободны под/без плана (isQueryOnly)
Новый предикат isQueryOnly (ToolSearch/WebFetch/WebSearch/read-only браузер:
navigate/snapshot/wait/screenshot) проведён во все ветки стены: смотрящие и
спрашивающие инструменты проходят и в разговорном режиме (осмотр чужого сайта
без плана), и под опечатанным планом, не двигая указатель шагов. Действующие
инструменты (клик/ввод) сюда не входят - они пойдут через сеанс осмотра
(часть 2 B+C). Свод зелёный: 4229 passed, 2 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:52:47 +03:00

14 KiB
Raw Blame History

Дизайн: осмотр и инструменты под стеной — «сеанс» (объединяет пункты B + C)

Дата: 2026-06-18 · Репозиторий: claude-brain (управляющий слой) · Кодовая фраза: «роутер-наставник». Источник: баги bags/2026-06-17-wall-interactive-walk-blockers-bug.md (Баги 1/2/3). Пункты B (tools-json) и C (интерактивный осмотр) допила эталона — слиты в один механизм. Поглощает: 2026-06-18-wall-impl-tool-allowlist-design.md (B как отдельная спека — отменена этой). Брат: 2026-06-18-wall-impl-read-freedom-design.md (A — чтение). Тот же принцип «не-шаг сбоку от очереди». Тип: дизайн-доказательство (brainstorming → writing-plans). Правки tools/*.mjs — далее по церемонии TDD.


1. Проблема (по факту, с цитатами)

Три независимых стопора, все про «использовать инструменты / осматривать сайт под стеной»:

  1. Инструменты режутся под планом. observe-only = {Read, Grep, Glob} + read-only Bash (enforce-supreme-gate.mjs:78-82). ToolSearch, MCP (mcp__*), WebFetch/WebSearch, браузер (browser_*) — НЕ в списке → под планом блок. Механизма tools-json (заявить инструменты в плане) в коде нет (строка есть только в баге).
  2. В разговорном режиме инструменты тоже режутся. Без печати плана пускаются только seed/observe/ authoring (enforce-supreme-gate.mjs:365-368) — ToolSearch/браузер блокируются. Значит «просто посмотреть сайт без плана» невозможно.
  3. Интерактивный проход нельзя выразить шагами (корень). Чтобы кликнуть/ввести, нужен ref элемента из ЖИВОГО снимка страницы — на момент печати плана его не существует. Шаги матчатся по op+object (plan-lock.mjs:89-104) — клик по динамическому ref так не задать. Плюс op:"Skill" опечатывается, но указатель не двигает (enforce-supreme-gate.mjs:265) → дедлок.

Вывод владельца (2026-06-18): «заранее расписать, что и сколько смотреть, нельзя — ни на нашем сайте, ни на чужом». Значит «план просмотра по кликам» — тупик. План должен фиксировать намерение и результат, а не клики.

2. Принцип

  • Не-мутирующее действие не расширяет набор разрешённого → безопасно (как чтение в A). Смотреть/ спрашивать инструментом — это «чтение», только внешнее.
  • Клик/ввод — действие, но НЕ над нашим репозиторием. Несущая защита стены — мутация репозитория через шаг. Внешний клик репозиторий не меняет; деттерминированный якорь интерактива — то, что агент в итоге запишет к нам (отчёт/наблюдения).
  • Указатель двухтактный (F-J) — не-шаг очередь не двигает (enforce-supreme-gate.mjs:446-448).

3. Решение — три части

3.1 Смотрящие инструменты — свободны в ОБОИХ режимах

Именованный набор заведомо read-only пускается и в разговорном режиме, и под планом, без объявления:

  • ToolSearch, WebFetch, WebSearch;
  • read-only браузер: browser_navigate, browser_snapshot, browser_wait_for, browser_take_screenshot.

Механика: новый предикат isQueryOnly(toolUse) рядом с isObserveOnly (:78-99); decideMode пускает его И в ветке «нет frozenPlan» (разговорный, :365-368), И под планом (:282). Указатель не двигают, пишутся в тихий лог (A). Это закрывает «чужой сайт без плана»: смотрю и рассказываю, никакой печати не нужно.

3.2 «Сеанс осмотра» — новый тип шага для интерактива

Когда надо реально пройтись и пощёлкать (логин, формы, сценарий клиента) — план объявляет НЕ список кликов, а один шаг-сеанс:

{ "op": "session", "goal": "<что осмотреть>", "tools": [<действующие инструменты>], "produces": "<путь итогового файла>" }
  • tools — действующие инструменты сеанса (browser_click, browser_type, browser_fill_form, browser_select_option, конкретные MCP). Это и есть бывший tools-json, теперь привязанный к сеансу.
  • produces — файл(ы), которые сеанс ОБЯЗАН произвести (наблюдения/отчёт). ≥1.
  • Пока указатель на шаге-сеансе: разрешены tools сеанса + смотрящие (3.1), сколько угодно, по живым ref — стена клики с шагами НЕ сверяет (динамический ref — не проблема). Указатель НЕ двигается.
  • Сеанс закрывается записью produces-файла: это обычный Write-шаг, он матчится и двигает указатель дальше. Деттерминированный якорь = итоговый файл, а не клики.

Этим снимается и дедлок op:"Skill": навык не делается шагом — он объявляется в skills-json и вызывается свободно (как сейчас, :265). op:"Skill" в steps-json запрещаем с явным сообщением «Skill не может быть шагом — объяви в skills-json». «Проверка» осмотра — это produces-отчёт, а не шаг-навык (убирает требование наставника «дай шаг verify»).

3.3 Предохранитель (ядро безопасности)

tools сеанса НИКОГДА не содержат:

  • Write/Edit/MultiEdit/мутирующий Bash — правки репозитория остаются ШАГАМИ плана (вкл. сам produces-файл сеанса — это обычный шаг записи);
  • floor-опасное (install/cloud-CLI/ssh/redirect/destructive, matchBashHardBlacklist, PowerShell-записи, запись в ~/.claude/runtime/секреты) — остаётся за полом/escape.

Запрещённое имя в tools отбрасывается с видимым предупреждением (решение владельца Q2 = вариант «а»: не валить план целиком из-за описки). Сеанс/tools входят в хеш плана — подмена после печати ломает печать; наставник и судья видят goal/tools/produces при разборе.

3.4 Логирование

Все вызовы инструментов и кликов под планом/в сеансе пишутся в тот же тихий лог, что impl-чтения (A): только для ретро, без баннера, без порога-warn (зеркало решений A; решения владельца Q1/Q2 спеки A).

3.5 Точки изменения в коде

  • enforce-supreme-gate.mjs: isQueryOnly (новый, рядом :78-99); decideMode — пускать query-only в обоих режимах (:282, :365-368); decide — ветка «указатель на шаге-сеансе»: вызов из frozenPlan сеанса tools → allow без advance; produces-Write → обычный матч-шаг с advance.
  • plan-lock.mjs: распознать op:"session" (goal/tools/produces) в дереве плана; produces — матч-якорь шага; применить предохранитель §3.3 (отбросить чёрный набор с предупреждением); запретить op:"Skill" как шаг; сеанс+tools в хеш плана.
  • наставник/судья (mentor-verdict.mjs/judge-*): показать goal/tools/produces сеанса в разборе плана (чтобы ревью видело набор инструментов и обещанный результат).
  • read-LOG (общий с A): принять записи tool-use/клик под планом.

4. Что НЕ трогаем

  • Шаг-гейт для мутаций репозитория (Write/Edit/Bash-write, вкл. produces-файл сеанса) — несущая защита, без изменений.
  • Пол M5 / escape — необратимое и floor-опасное по-прежнему через них.
  • Чтение A — отдельная спека; общий тихий лог переиспользуем.
  • Машинерия указателя (F-J) — не касаемся; инструмент/клик-не-шаг очередь не двигает.

5. Критерий «починено»

  • «Чужой сайт без плана»: в разговорном режиме работают ToolSearch/WebFetch/read-only браузер.
  • Под планом-сеансом: browser_click/type/fill работают по живым ref, без escape на каждый клик, сколько нужно; указатель не двигается.
  • Сеанс закрывается записью produces-файла (обычный шаг двигает указатель).
  • op:"Skill" как шаг — отбит с явным сообщением; навык из skills-json вызывается свободно.
  • tools сеанса с Write/rm -rf/ssh → отброшены с предупреждением, под планом не проходят.
  • Сеанс+tools в хеше плана: подмена после печати ломает печать.
  • Ни клик, ни tool-вызов не двигают указатель (регресс F-J держит).
  • Лог фиксирует tool-use/клики (видно в ретро).

6. Тесты (TDD-набросок)

  • isQueryOnly: ToolSearch/WebFetch/read-only браузер → true; browser_click/Write → false.
  • decideMode без frozenPlan: query-only → allow (разговорный осмотр чужого сайта).
  • plan-lock парсит op:"session": goal/tools/produces извлечены; produces пуст → план невалиден.
  • Предохранитель: Write/ssh в tools сеанса → отброшены + warn.
  • op:"Skill" в steps-json → план отвергает шаг с сообщением.
  • decide указатель на сеансе: browser_clicktools) → allow, advance=false.
  • decide указатель на сеансе: запись produces-файла → allow, advance=true (сеанс закрыт).
  • decide указатель на сеансе: инструмент НЕ в tools и не query-only → block (default-deny держит).
  • Хеш: план с другим tools/produces → другой plan_id.
  • Регресс F-J: клик/tool-use не двигает указатель.

7. Риски / открытые вопросы

  • Q-A (для владельца): ограничивать ли длительность/объём сеанса (тайм-аут, максимум кликов)? Предлагаю нет (зеркало «без порога» из A) — сеанс и так обязан кончиться записью produces, иначе план не сдвинется. Незавершённый сеанс просто висит, как любой недоделанный план.
  • Q-B (для владельца): один сеанс = один produces-файл, или можно несколько (наблюдения + отчёт)? Предлагаю разрешить несколько — гибче; последний из списка закрывает сеанс.
  • Наставник/судья должны понимать новый тип шага — иначе завернут как «непонятный шаг». Часть §3.5.
  • Связанный хвост (баг read-block Баг-2 Дефект 1): наставник видит не все поля шага. Пересекается с «показать goal/tools/produces наставнику» — закрывается тем же изменением §3.5.