# ADR-026: Enforce tool-usage policy across prompts and skills ## Статус Accepted (2026-07-25) ## Контекст Research (subagent explore) выявил 3 gap'а в tool-usage инфраструктуре репо `slaid098/opencode-config`: ### Gap A: Skills предписывают raw bash, заблокированный deny-правилами 6 skill-файлов предписывали subagent'ам raw bash команды, которые глобально заблокированы в `opencode.json:311-314` (4 deny-правила: `git commit *`, `gh pr create *`, `gh pr merge *`, `gh issue create *`): - `run-pipeline/SKILL.md` Template A: `gh pr create`, `git commit` - `run-pipeline/SKILL.md` Template B/D: `git commit` - `issue/SKILL.md:83,127`: `gh issue create`; `:154`: `gh pr merge` - `spec/SKILL.md:311`: `gh issue create` - `release/SKILL.md:44`, `add-skill/SKILL.md:70`, `repo-init/SKILL.md:41`: `git commit` Симптом: subagent грузит skill → получает инструкцию нарушить permission layer → падает (deny блокирует bash) или импровизирует обход через `gh api` (см. memory `technical/gh-issue-create-blocked-workaround.md` — `gh api repos/*/issues` allow-rule существует, но использование его для создания issue обходит валидацию тулзы и ломает формат репо). ### Gap B: Tool failure handling только для 2 из 10 tools Секция "Tool failure handling" (инструкция "при сбое tool — STOP, не fallback на raw bash") была только для `post_review` (`reviewer.md:285`) и `post_docs_review` (`docs-reviewer.md:228`). Для `commit`, `create_pr`, `create_issue`, `merge_pr`, `pipeline_status`, `spec_status`, `memory_setup`, `tunnel` — отсутствует. При сбое agent без инструкции, импровизирует. ### Gap C: 4 tool'а не упомянуты нигде как tool - `create_issue`, `create_pr` — 0 упоминаний как tool в промптах/skills (только raw `gh issue create`/`gh pr create` в skills, заблокированные deny) - `tunnel` — skill удалён в PR#42 (orchestration-switch), tool остался orphan (0 упоминаний как tool; пользователь использует вручную, но агент не "видит" его в контексте) - `memory_setup` — 1 строка в `memory/SKILL.md:19`, в промптах агентов нет ### Дополнительно `gh pr comment*` остался в allow-list reviewer/docs-reviewer (ADR-019 отклонил strict-deny "для обратной совместимости"). Решено: оставить в allow (промпт уже запрещает fallback при сбое tool, противоречие minimal). ## Решение ### 1. AGENTS.md: единая Tool Usage Policy секция В `AGENTS.md` добавлена секция `## Tool Usage Policy` с таблицей всех 10 tools. Колонки: имя tool | raw-эквивалент (заблокирован deny) | когда использовать | при сбое — STOP, репорт, НЕ fallback. Правило: "Используй tool вместо raw bash. Raw bash-эквиваленты заблокированы deny (`opencode.json:311-314`). При сбое tool — STOP и репорт оркестратору, НЕ fallback на raw bash, НЕ импровизируй обход через `gh api`." Это устраняет Gap B (единая policy для всех 10 tools вместо per-tool инструкций) и Gap C (4 tool'а теперь упомянуты как tool в едином месте). Global copy `/root/.config/opencode/AGENTS.md` — bind-mount read-only из workspace `AGENTS.md` (compose: `./AGENTS.md:/root/.config/opencode/AGENTS.md:ro`). В контейнере файл не редактируется; изменения идут в workspace copy, после merge + host `git pull` + container restart подхватываются автоматически. Это symlink-эквивалент через bind-mount. ### 2. 6 skills: raw bash → tool-вызовы Все предписания raw bash в 6 skills заменены на tool-вызовы (см. handoff "Что сделано" для полного списка). `git add` и `git push` оставлены — они не заблокированы deny. Anti-instructions (упоминания "НЕ raw bash") сохранены как документация. ### 3. Skill `tunnel` восстановлен Создан `.opencode/skills/tunnel/SKILL.md` (удалён в PR#42, tool остался orphan). Frontmatter: name: tunnel, description с триггерами "подними тоннель"/"пробрось порт"/"tunnel". Тело: инструкция вызывать tool `tunnel()` (1-й вызов — start, 2-й — stop, беспараметровый). Skill станет доступен после рестарта opencode (skills загружаются при старте). ### 4. `gh pr comment*` оставлен в allow-list Не трогали — intentional (ADR-019). Промпт уже запрещает fallback при сбое tool (`post_review`/`post_docs_review`), противоречие minimal. strict-deny `gh pr comment*` отклонён (см. Альтернативы). ## Альтернативы - **Per-agent промпты: добавить tool failure handling в каждый agent.md по отдельности** — отклонено из-за дублирования. 4 agent-файла × 10 tools = 40 упоминаний, дрейф при добавлении новых tools. Единая policy в AGENTS.md (грузится всеми агентами) — canonical источник, добавление нового tool = 1 строка в таблице. - **strict-deny `gh pr comment*` (принудить к `post_review`/`post_docs_review`)** — отклонено (повторяет ADR-019). `gh pr comment*` остаётся в allow-list для обратной совместимости; tools используют spawnSync напрямую (не через bash permission layer), raw `gh pr comment*` не конфликтует. Промпт уже запрещает fallback при сбое tool — этого достаточно. - **Упомянуть 4 orphan tool'а (Gap C) в per-agent промптах** — отклонено по той же причине дублирования. Единая таблица в AGENTS.md покрывает все 10 tools одним источником правды. - **Удалить skill `tunnel` совсем (tool orphan → удалить tool)** — отклонено: tool `tunnel` используется пользователем вручную и в pipeline (tunnel.sh scaffold). Восстановление skill даёт агенту контекст для вызова tool'а по триггерам пользователя.