opencode-config/docs/decisions/026-pr-63-enforce-tool-usage-policy.md
Sergey a2666183f8
fix(agents+skills): enforce tool-usage policy across prompts and skills (#63)
* fix(agents): add tool usage policy to AGENTS.md

* fix(skills): replace raw bash with tools in 6 skills

* feat(skills): restore tunnel skill

* docs(handoff): scaffold handoff and ADR for PR

* docs(handoff): set PR number

* docs(project-map): add tunnel skill + tool usage policy note (PR#63)

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-25 18:46:16 +03:00

116 lines
No EOL
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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'а
по триггерам пользователя.