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>
This commit is contained in:
Sergey 2026-07-25 18:46:16 +03:00 committed by GitHub
parent f96acaaa75
commit a2666183f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 294 additions and 27 deletions

View file

@ -67,10 +67,16 @@ Implementing agent (subagent, которому делегировано созд
```bash ```bash
cd "$(git rev-parse --show-toplevel)" cd "$(git rev-parse --show-toplevel)"
git add .opencode/skills/<skill-name>/SKILL.md git add .opencode/skills/<skill-name>/SKILL.md
git commit -m "feat(skills): add <skill-name> skill for <purpose>"
git push
``` ```
Затем через `commit` tool (НЕ raw `git commit` — заблокирован deny):
```
commit({ message: "feat(skills): add <skill-name> skill for <purpose>" })
```
Затем `git push`.
## 5. Инструкция пользователю ## 5. Инструкция пользователю
После push сообщить пользователю: После push сообщить пользователю:

View file

@ -80,10 +80,10 @@ Issue создаёт **subagent** (general type), а не основной аг
1. Загрузи навык `issue` 1. Загрузи навык `issue`
2. Собери контекст — прочитай файлы из intent summary, пойми задачу, оцени объём (правило дробления ниже) 2. Собери контекст — прочитай файлы из intent summary, пойми задачу, оцени объём (правило дробления ниже)
3. Составь self-contained body по шаблону (Контекст → Что сделать → Проверка → Acceptance criteria → Dependencies → Связанные ресурсы) 3. Составь self-contained body по шаблону (Контекст → Что сделать → Проверка → Acceptance criteria → Dependencies → Связанные ресурсы)
4. Запусти `gh issue create --title "..." --body "..."` (labels — см. guidance ниже) 4. Запусти `create_issue({ title: "...", body: "...", labels: ["..."] })` tool (НЕ raw `gh issue create` — заблокирован deny; tool валидирует conventional title format и headings `## Контекст`/`## Задача`/`## Критерии приемки`)
5. Верни URL созданного issue основному агенту 5. Верни URL созданного issue основному агенту
Main agent НЕ пишет body и НЕ запускает `gh issue create` — всё через subagent. Это согласовано с `run-pipeline` skill (Phase 0: "через subagent с `issue` skill") и `AGENTS.md` (Dev Workflow, step 2: "delegate to `task` subagent"). Main agent НЕ пишет body и НЕ запускает `create_issue` — всё через subagent. Это согласовано с `run-pipeline` skill (Phase 0: "через subagent с `issue` skill") и `AGENTS.md` (Dev Workflow, step 2: "delegate to `task` subagent").
## Пример хорошего issue ## Пример хорошего issue
@ -123,12 +123,16 @@ Zoom breathing падает при включённом geometry crop — crop
## Команда создания ## Команда создания
```bash Через tool (НЕ raw bash — `gh issue create *` заблокирован deny):
gh issue create \
--title "type(scope): description" \
--body "..." \
--label "<label>"
``` ```
create_issue({ title: "type(scope): description", body: "...", labels: ["<label>"] })
```
Tool валидирует: title соответствует conventional format (type(scope): desc,
≤80 chars, English), body содержит `## Контекст`, `## Задача`, `## Критерии
приемки` headings и на русском (Cyrillic обязательна). При ошибке валидации
tool возвращает ошибку и НЕ вызывает gh — почини формат и повтори.
Label выбирай по типу задачи (совпадает с commit `type`): Label выбирай по типу задачи (совпадает с commit `type`):
- `enhancement` — новая функциональность (`feat`) - `enhancement` — новая функциональность (`feat`)
@ -138,7 +142,9 @@ Label выбирай по типу задачи (совпадает с commit `t
- `chore` — обслуживание, зависимости, конфиг (`chore`) - `chore` — обслуживание, зависимости, конфиг (`chore`)
- `performance` — производительность (`perf`) - `performance` — производительность (`perf`)
Если label не существует в репо — `gh issue create` упадёт. Создай через `gh label create <name> --color <hex>` (один раз) или опусти `--label`. Если label не существует в репо — tool упадёт. Создай через `gh label create
<name> --color <hex>` (один раз, `gh label create` НЕ заблокирован) или
опусти labels в вызове tool.
## Пути навыков ## Пути навыков
@ -151,7 +157,9 @@ Label выбирай по типу задачи (совпадает с commit `t
1. **Subagent**`task(general)` читает issue, реализует, коммитит, push, создаёт PR. Оркестрация — через `run-pipeline` skill. 1. **Subagent**`task(general)` читает issue, реализует, коммитит, push, создаёт PR. Оркестрация — через `run-pipeline` skill.
2. **Docs review**`@docs-reviewer` subagent валидирует handoff + ADR, обновляет project map (pre-merge). 2. **Docs review**`@docs-reviewer` subagent валидирует handoff + ADR, обновляет project map (pre-merge).
3. **Code review**`@reviewer` subagent ревьюит PR (diff, skills, standards), постит `## Code Review Summary` комментарий. 3. **Code review**`@reviewer` subagent ревьюит PR (diff, skills, standards), постит `## Code Review Summary` комментарий.
4. **Merge or Repeat** — APPROVE → `gh pr merge N --squash --delete-branch` (после CI ✅); замечания → fix subagent → re-review → merge. 4. **Merge or Repeat** — APPROVE → `merge_pr({ pr_number: N })` tool (squash +
delete branch, без `--admin`; НЕ raw `gh pr merge` — заблокирован deny),
после CI ✅; замечания → fix subagent → re-review → merge.
5. **Memory-sync**`@memory-syncer` дистиллирует handoff + ADR в `app_data/opencode-memory/repos/{host}/{org}/{repo}.md`. 5. **Memory-sync**`@memory-syncer` дистиллирует handoff + ADR в `app_data/opencode-memory/repos/{host}/{org}/{repo}.md`.
См. `AGENTS.md` (Development Workflow) и `run-pipeline` skill — все три документа описывают одну и ту же full-subagent модель делегирования. См. `AGENTS.md` (Development Workflow) и `run-pipeline` skill — все три документа описывают одну и ту же full-subagent модель делегирования.

View file

@ -41,7 +41,12 @@ description: Выполняет релиз после мерджа PR — обн
```bash ```bash
git add CHANGELOG.md git add CHANGELOG.md
git commit -m "docs: add vX.Y.Z changelog entry" ```
Затем через `commit` tool (НЕ raw `git commit` — заблокирован deny):
```
commit({ message: "docs: add vX.Y.Z changelog entry" })
``` ```
### Шаг 5: Создать tag ### Шаг 5: Создать tag

View file

@ -38,7 +38,12 @@ description: Sequential checklist: create GitHub remote → configure settings/b
git init git init
echo "# <repo-name>" > README.md echo "# <repo-name>" > README.md
git add README.md git add README.md
git commit -m "chore: initial commit" ```
Затем через `commit` tool (НЕ raw `git commit` — заблокирован deny):
```
commit({ message: "chore: initial commit" })
``` ```
Если bare-repo без initial commit — `gh repo create --source=.` создаст remote, но push будет пустым, а main branch не появится → branch protection упадёт. Если bare-repo без initial commit — `gh repo create --source=.` создаст remote, но push будет пустым, а main branch не появится → branch protection упадёт.

View file

@ -47,13 +47,15 @@ description: Автономный исполнитель PR-пайплайна.
зафикь и продолжай — не додумывай. зафикь и продолжай — не додумывай.
3. Создай handoff + ADR: `bash .opencode/scripts/scaffold-handoff.sh M <slug>` 3. Создай handoff + ADR: `bash .opencode/scripts/scaffold-handoff.sh M <slug>`
(M — будет PR номер, используй placeholder `<PR-NUMBER>` в handoff (M — будет PR номер, используй placeholder `<PR-NUMBER>` в handoff
frontmatter, потом исправишь после `gh pr create`). frontmatter, потом исправишь после create_pr).
4. Коммиты в формате `type(scope): description` (≤72 chars, English, no 4. Коммиты через `commit({ message: "type(scope): description" })` tool (НЕ
period, no body unless necessary). Минимум 3-4 логических коммита. raw `git commit` — заблокирован deny). Формат: ≤72 chars, English, no
5. Push и создай PR: period, no body unless necessary. Минимум 3-4 логических коммита.
`gh pr create --title "type(scope): description" --body "## Что сделано\n...\n\n## Почему\n...\n\nCloses #N"` 5. Push ветку (`git push -u origin HEAD`), затем создай PR через tool:
`create_pr({ title: "type(scope): description", body: "## Что сделано\n...\n\n## Почему\n...\n\nCloses #N", issue_number: N })`.
6. После получения PR номера — исправь placeholder `<PR-NUMBER>` в handoff 6. После получения PR номера — исправь placeholder `<PR-NUMBER>` в handoff
frontmatter, отдельный коммит `docs(handoff): set PR number`, push. frontmatter, отдельный коммит `docs(handoff): set PR number` через
`commit` tool, push.
7. Верни PR номер M. 7. Верни PR номер M.
``` ```
@ -69,8 +71,9 @@ Review PR#M в текущем репо (pre-merge, режим docs).
5. Валидируй ADR `docs/decisions/*-pr-M-*.md`: 4 секции (Статус, Контекст, 5. Валидируй ADR `docs/decisions/*-pr-M-*.md`: 4 секции (Статус, Контекст,
Решение, Альтернативы). Решение, Альтернативы).
6. Если криво — почини (edit: allow). 6. Если криво — почини (edit: allow).
7. `git add docs/project-map/ docs/handoff/ docs/decisions/ && git commit -m 7. `git add docs/project-map/ docs/handoff/ docs/decisions/` (add — НЕ
"docs: update project map + handoff + ADR" && git push`. заблокирован), затем `commit({ message: "docs: update project map + handoff + ADR" })`
tool, и `git push`.
8. **ВСЕГДА** оставь PR comment (даже если structural changes нет) — это 8. **ВСЕГДА** оставь PR comment (даже если structural changes нет) — это
детерминированный marker для `check_docs` в pipeline-status.py. Без comment детерминированный marker для `check_docs` в pipeline-status.py. Без comment
pipeline блокируется на DOCS phase. Используй tool `post_docs_review` (НЕ pipeline блокируется на DOCS phase. Используй tool `post_docs_review` (НЕ
@ -113,7 +116,8 @@ Log: `gh run view <run-id> --log-failed` output:
1. `gh pr checkout M`. 1. `gh pr checkout M`.
2. Проанализируй log, найди причину. 2. Проанализируй log, найди причину.
3. Исправь (минимальные изменения, whitespace/formatting/logic fix). 3. Исправь (минимальные изменения, whitespace/formatting/logic fix).
4. Коммит `fix(ci): <description>`, push. 4. Коммит через `commit({ message: "fix(ci): <description>" })` tool (НЕ raw
`git commit`), push.
5. Не трогай логику unrelated файлов. 5. Не трогай логику unrelated файлов.
``` ```

View file

@ -308,8 +308,8 @@ Default stack для типа (хардкод, добавить всегда):
## Связанные ресурсы (Part of spec, ref к docs/spec/roadmap.md) ## Связанные ресурсы (Part of spec, ref к docs/spec/roadmap.md)
- Issue #1 (scaffolding) body ДОЛЖЕН включать: - Issue #1 (scaffolding) body ДОЛЖЕН включать:
"Используй repo-init skill для: pyproject.toml, CI, .gitignore, LICENSE, dependabot, pre-commit. Структура — из ## Структура в docs/spec/modules.md." "Используй repo-init skill для: pyproject.toml, CI, .gitignore, LICENSE, dependabot, pre-commit. Структура — из ## Структура в docs/spec/modules.md."
- gh issue create --title "type(scope): description" --body "<body>" --label "enhancement,from-spec" - `create_issue({ title: "type(scope): description", body: "<body>", labels: ["enhancement", "from-spec"] })` tool (НЕ raw `gh issue create` — заблокирован deny)
4. Собери реальные номера issues из вывода gh. 4. Собери реальные номера issues из вывода tool.
5. Update docs/spec/roadmap.md: добавь реальные #N номера. Update docs/spec/meta.md: executed=true, phase=8. 5. Update docs/spec/roadmap.md: добавь реальные #N номера. Update docs/spec/meta.md: executed=true, phase=8.
6. Верни: [{number, url, title}, ...] для всех issues. 6. Верни: [{number, url, title}, ...] для всех issues.
``` ```

View file

@ -0,0 +1,13 @@
---
name: tunnel
description: Подними cloudflare туннель когда пользователь просит "подними тоннель", "пробрось порт", "tunnel"
---
# Tunnel
Когда пользователь просит поднять или опустить cloudflare тоннель — вызови
tool `tunnel()`. Первый вызов — старт, второй — стоп. Tool беспараметровый.
Не используй raw bash `bash .opencode/scripts/tunnel.sh` — заблокирован deny
(см. `opencode.json:311-314`). При сбое tool — STOP и репорт оркестратору, НЕ
fallback на raw bash.

View file

@ -27,6 +27,29 @@ All PR work runs through `/run-pipeline`. Pipeline phases orchestrated by `pipel
- No comments unless explicitly requested - No comments unless explicitly requested
- Match surrounding code style (imports, naming, patterns) - Match surrounding code style (imports, naming, patterns)
## Tool Usage Policy
Используй tool вместо raw bash. Raw bash-эквиваленты заблокированы deny
(`opencode.json:311-314`). При сбое tool — STOP и репорт оркестратору, НЕ
fallback на raw bash, НЕ импровизируй обход через `gh api`.
| Tool | Raw bash (заблокирован deny) | Когда использовать | При сбое — STOP, репорт, НЕ fallback |
|---|---|---|---|
| `commit({ message })` | `git commit *` | Коммит staged файлов (conventional format валидируется tool'ом) | Сообщи оркестратору, не `git commit` |
| `create_pr({ title, body, issue_number })` | `gh pr create *` | Создание PR после push ветки | Сообщи оркестратору, не `gh pr create`, не `gh api repos/*/pulls` |
| `create_issue({ title, body, labels })` | `gh issue create *` | Создание GitHub issue (валидация формата) | Сообщи оркестратору, не `gh issue create`, не `gh api repos/*/issues` |
| `merge_pr({ pr_number })` | `gh pr merge *` | Merge PR (squash + delete branch, без `--admin`) | Сообщи оркестратору, не `gh pr merge` |
| `post_review({ pr_number, verdict, body })` | `gh pr comment` для verdict | Code review verdict (heading `## Code Review Summary` + verdict) | Сообщи оркестратору, не `gh pr comment` |
| `post_docs_review({ pr_number, verdict, body })` | `gh pr comment` для verdict | Docs review verdict (heading `## Docs Review Summary` + verdict) | Сообщи оркестратору, не `gh pr comment` |
| `pipeline_status({ pr_number })` | `python3 .opencode/scripts/pipeline-status.py` | Read-only oracle: статус фаз PR + NEXT action | Сообщи оркестратору, не bash-запуск скрипта |
| `spec_status({})` | `python3 .opencode/scripts/spec-status.py` | Read-only oracle: текущая фаза spec + NEXT action | Сообщи оркестратору, не bash-запуск скрипта |
| `memory_setup()` | `bash .opencode/scripts/setup-memory.sh` | Инициализация/синхронизация opencode-memory (clone + hook + reindex) | Сообщи оркестратору, не bash-запуск скрипта |
| `tunnel()` | `bash .opencode/scripts/tunnel.sh` | Cloudflare tunnel toggle (1-й вызов — start, 2-й — stop) | Сообщи оркестратору, не bash-запуск скрипта |
`gh pr comment*` остаётся в allow-list reviewer/docs-reviewer для обратной
совместимости (ADR-019 отклонил strict-deny). Промпт уже запрещает fallback при
сбое tool — противоречие minimal.
## Language ## Language
- Always respond to the user in Russian. - Always respond to the user in Russian.

View file

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

View file

@ -0,0 +1,86 @@
---
pr: 63
title: fix(agents+skills): enforce tool-usage policy across prompts and skills
---
## Что сделано
- **AGENTS.md** — добавлена секция `## Tool Usage Policy` с таблицей 10 tools
(commit, create_pr, create_issue, merge_pr, post_review, post_docs_review,
pipeline_status, spec_status, memory_setup, tunnel). Колонки: имя tool |
raw-эквивалент (заблокирован deny) | когда использовать | при сбое — STOP,
репорт, НЕ fallback. Правило: "Используй tool вместо raw bash. Raw bash
заблокирован deny (`opencode.json:311-314`). При сбое tool — STOP и репорт
оркестратору, НЕ fallback на raw bash, НЕ импровизируй обход через `gh api`."
Global copy `/root/.config/opencode/AGENTS.md` — bind-mount read-only из
workspace `AGENTS.md`, обновится после merge + хост `git pull` + рестарт
контейнера (см. ADR-026 Решение).
- **6 skills** — raw bash заменён на tool-вызовы:
- `run-pipeline/SKILL.md` Template A: `git commit``commit({ message })`,
`gh pr create``create_pr({ title, body, issue_number })`
- `run-pipeline/SKILL.md` Template B: `git commit``commit({ message })`
(`git add` и `git push` оставлены — не заблокированы)
- `run-pipeline/SKILL.md` Template D: `git commit``commit({ message: "fix(ci): ..." })`
- `issue/SKILL.md`: `gh issue create``create_issue({ title, body, labels })`
(3 места), `gh pr merge``merge_pr({ pr_number })`
- `spec/SKILL.md:311`: `gh issue create``create_issue({ title, body, labels })`
- `release/SKILL.md:44`, `add-skill/SKILL.md:70`, `repo-init/SKILL.md:41`:
`git commit``commit({ message })`
- **Skill `tunnel` восстановлен**`.opencode/skills/tunnel/SKILL.md` (удалён
в PR#42 как orphan; tool `tunnel` остался без skill-описания, пользователь
вызывал вручную, но агент не "видел" его в контексте). Frontmatter: name:
tunnel, description: "Подними cloudflare туннель когда пользователь просит
'подними тоннель', 'пробрось порт', 'tunnel'". Тело: инструкция вызывать
tool `tunnel()` (1-й вызов — start, 2-й — stop, беспараметровый).
## Почему
Research (subagent explore) выявил 3 gap'а в tool-usage инфраструктуре:
- **Gap A**: 6 skills предписывали raw bash (`git commit`, `gh pr create`,
`gh issue create`, `gh pr merge`), заблокированный deny в
`opencode.json:311-314`. Симптом: subagent грузит skill → получает
инструкцию нарушить permission layer → падает или импровизирует обход через
`gh api` (см. memory `technical/gh-issue-create-blocked-workaround.md`).
- **Gap B**: Tool failure handling ("при сбое tool — STOP, не fallback на raw
bash") был только для 2 из 10 tools — `post_review` (reviewer.md:285) и
`post_docs_review` (docs-reviewer.md:228). Для остальных 8 tools при сбое
agent без инструкции, импровизирует.
- **Gap C**: 4 tool'а не упомянуты нигде как tool — `create_issue`,
`create_pr` (0 упоминаний), `tunnel` (skill удалён в PR#42, tool orphan),
`memory_setup` (1 строка в `memory/SKILL.md:19`).
Дополнительно: `gh pr comment*` остался в allow-list reviewer/docs-reviewer
(ADR-019 отклонил strict-deny "для обратной совместимости"). Решено оставить
как есть — промпт уже запрещает fallback при сбое tool, противоречие minimal.
## Pending
- После merge: на хосте `git pull` + рестарт opencode-контейнера чтобы global
config подхватил обновлённый `AGENTS.md` (bind-mount read-only) и
восстановленный skill `tunnel` (авто-дискаверится при старте).
- Зависимости: supersedes partial PR#42 (orchestration-switch) — Watch out в
handoff pr-42 явно пишет "Третий PR серии: обновить промпты
reviewer.md/memory-syncer.md/skills"; related PR#61 (issue #60) добавил
"Tool failure handling" в reviewer.md/docs-reviewer.md для
post_review/post_docs_review — этот PR расширяет на все 10 tools через
единую policy в AGENTS.md.
## Watch out
- `/root/.config/opencode/AGENTS.md` — bind-mount read-only из
`/dev/vda1[/root/dockers/opencode-config/AGENTS.md]` (compose:
`./AGENTS.md:/root/.config/opencode/AGENTS.md:ro`). В контейнере файл НЕ
редактируется — изменения идут в workspace copy, после merge +
host git pull + container restart подхватятся автоматически. Это
symlink-эквивалент через bind-mount (issue предусматривал оба варианта).
- Skill `tunnel` станет доступен только после рестарта opencode (skills
загружаются при старте, см. add-skill/SKILL.md секция 3).
- `gh pr comment*` в allow-list reviewer/docs-reviewer НЕ трогали — это
intentional (ADR-019), не regression.
- 7 оставшихся упоминаний `gh pr create|git commit -m|gh issue create|gh pr merge`
в skills — это anti-instructions (упоминания в контексте "НЕ raw bash") и
examples в permission-rules docs (`configure-opencode/SKILL.md:89`), не
предписания. Grep на raw patterns нужно фильтровать по контексту.
- Ветка создана от `main` (не `master` — master не существует в этом репо).
Issue говорил "от master" — это соглашение naming, фактически main.

View file

@ -2,7 +2,7 @@
opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). Runs in Docker via `docker-compose.yml` (dind + opencode services). opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). Runs in Docker via `docker-compose.yml` (dind + opencode services).
Root `AGENTS.md` — orchestrator directive (chat = plan only, all via subagents) + global rules (pipeline, code style, language RU). Formats enforced by tools (commit/create_pr/create_issue). Auto-loaded for project + bind-mounted globally in container — PR#31. Root `AGENTS.md` — orchestrator directive (chat = plan only, all via subagents) + global rules (pipeline, code style, language RU) + `## Tool Usage Policy` (таблица 10 tools: commit/create_pr/create_issue/merge_pr/post_review/post_docs_review/pipeline_status/spec_status/memory_setup/tunnel; raw bash заблокирован deny, при сбое tool — STOP, НЕ fallback). Auto-loaded for project + bind-mounted globally in container — PR#31, PR#63.
## Structure ## Structure
@ -35,6 +35,7 @@ opencode-config/
│ │ ├── python-development/SKILL.md # Python dev patterns │ │ ├── python-development/SKILL.md # Python dev patterns
│ │ ├── release/SKILL.md # Tag + GitHub Release │ │ ├── release/SKILL.md # Tag + GitHub Release
│ │ ├── repo-init/SKILL.md # New repository bootstrap │ │ ├── repo-init/SKILL.md # New repository bootstrap
│ │ ├── tunnel/SKILL.md # Cloudflare tunnel toggle (tool `tunnel()`: 1-й вызов start, 2-й stop) — PR#63 (восстановлен, удалён в PR#42)
│ │ ├── run-tests/SKILL.md # Test runner guide │ │ ├── run-tests/SKILL.md # Test runner guide
│ │ └── spec/SKILL.md # 9-phase spec generation │ │ └── spec/SKILL.md # 9-phase spec generation
│ ├── tools/ │ ├── tools/