Files
dotfiles/docs/research/2026-10-11-opencode-permission-reviewer-autoapprove.md
ddadmin 16e85139b9 chore(opencode): эксперимент с permission-reviewer — итоги и отключение
Плагин настроен и проверен (jev через Zen + прокси, enforce+deny),
но признан не стоящим использования: хрупкая цепочка, нервные
эскалации, запертая сессия. Запись в opencode.json отключена
(закомментирована), выводы — в docs/research/.
2026-10-11 12:15:19 +03:00

5.9 KiB

Эксперимент: ИИ-ревьюер разрешений в OpenCode (autoapprove)

Дата: 2026-10-11 Статус: эксперимент закрыт, плагин снят. Документ сохранён как описание системы и причин отказа — если идея вернётся, начинать отсюда.

Итог одним абзацем

Плагин opencode-permission-reviewer (LLM-ревьюер каждого ask) настроен и работоспособен, но признан не стоящим использования: хрупкая цепочка (ключ в окружении сервиса → региональный 451 на endpoint jev → прокси), плюс ревьюер нервно эскалует всё подряд в сессиях с «опасным» транскриптом. Боевой режим: permission: autoaccept + deny-паттерны на секреты + snapshots/git как обратимость. AFK для системного администрирования не достигнут: узкое место — не разрешения, а компетентность модели-исполнителя (glm flash провалил простые админские задачи: «делал странное с умным видом»).

Как была устроена система

Цепочка решений для каждого ask из opencode.json:

  1. Deny-паттерны (permission.bash) — мгновенный отказ по строке команды, до моделей. Только секреты: *.env*, *.secrets*, ~/.ssh/* и т.п. Паттерны слепы к содержимому скриптов — защита секретов, не рубеж.
  2. Jev (opencode/jev-1.13, System One) — типизированный ревьюер: команда, свидетельства из транскрипта, содержимое локальных скриптов (read-only enrichment). Отвечает allow / deny / escalate.
  3. Второе мнение (opencode-go/glm-5.3-flash) — трудные случаи, где jev неуверен.
  4. Финал — escalationMode: manual (спросить человека; при permission=autoaccept на него отвечает auto) или deny (отказ с причиной). enforcementMode: observe (только аудит, дефолт) или enforce.

Инфраструктурные грабли (главная ценность эксперимента)

  • Jev ходит на отдельный endpoint Zen /zen/v1/systemone. Zen закрывает его по региону (HTTP 451) — нужен прокси в окружении сервиса (HTTPS_PROXY в ~/.env.secrets). Чат-модели Zen (/zen/v1/chat/completions) доступны без прокси; блокирован только systemone. Jev нет в общем каталоге моделей — это намеренно (не чат-модель), на плагин не влияет.
  • OPENCODE_API_KEY должен быть в окружении процесса сервиса. Сервис наследует окружение шелла, из которого запущен; после правки ~/.env.secrets нужны source + рестарт из свежего шелла. Рестарт из старого шелла ключ не подхватывает.
  • Ревьюер зовётся из серверного процесса: рестарт сервиса из сессии агента рвёт саму сессию.
  • Emergency brake: команды, пахнущие выносом креденшелов (curl -H "Authorization: ... $KEY"), режутся детерминированно.
  • Конфиг плагина читается с O_NOFOLLOW: симлинк молча игнорируется, деплой только копией (см. .stow-local-ignore).

Известные свойства поведения

  • Сессия, полная опасных команд в транскрипте, выглядит для ревьюера подозрительно: jev эскалует всё подряд, и с escalationMode: deny такая сессия становится неработоспособной — fail-closed по замыслу.
  • В этой сессии это проявилось полностью: заперт даже git status, отменить отказы изнутри сессии нельзя (явное разрешение в чате не помогло).
  • Пороги тонкой настройки (не крутились): systemOneConfidenceThreshold (дефолт 0.4), systemOneReasoningThreshold (дефолт 0.38), confidenceThreshold (чат-ревьюер, 0.7).
  • Установлено про авто-подтверждение без плагина: ручное approve при серии запросов вырождается в слепое «да» — честный auto + обратимость лучше декоративного ручного.

Ссылки