* 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>
116 lines
No EOL
7.1 KiB
Markdown
116 lines
No EOL
7.1 KiB
Markdown
# 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'а
|
||
по триггерам пользователя. |