Новый предикат 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>
14 KiB
Дизайн: осмотр и инструменты под стеной — «сеанс» (объединяет пункты 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. Проблема (по факту, с цитатами)
Три независимых стопора, все про «использовать инструменты / осматривать сайт под стеной»:
- Инструменты режутся под планом. observe-only =
{Read, Grep, Glob}+ read-only Bash (enforce-supreme-gate.mjs:78-82).ToolSearch, MCP (mcp__*),WebFetch/WebSearch, браузер (browser_*) — НЕ в списке → под планом блок. Механизмаtools-json(заявить инструменты в плане) в коде нет (строка есть только в баге). - В разговорном режиме инструменты тоже режутся. Без печати плана пускаются только seed/observe/
authoring (enforce-supreme-gate.mjs:365-368) —
ToolSearch/браузер блокируются. Значит «просто посмотреть сайт без плана» невозможно. - Интерактивный проход нельзя выразить шагами (корень). Чтобы кликнуть/ввести, нужен
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_click(вtools) → 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.