refactor(pipeline): make pipeline-status sole orchestrator + slim docs (#42)
* refactor(pipeline): add subagent_type and template to NEXT actions
NEXT_ACTIONS now explicitly specifies subagent_type + template per phase (except MERGE which calls merge_pr tool). ISSUE/IMPLEMENT -> general/A, DOCS -> docs-reviewer/B, REVIEW -> reviewer/C, MEMORY -> memory-syncer/E. Existing tests updated to new format, 25 new tests added.
* refactor(docs): slim AGENTS.md and run-pipeline skill
AGENTS.md 71 -> 31 lines: remove Commits/Pull Requests sections (formats in tools), shorten Development Workflow/Pipeline to 1-2 lines. run-pipeline SKILL.md 164 -> 155 lines: remove Phase 0 Bootstrap (duplicated phases, now in pipeline-status NEXT_ACTIONS). Keep protocol, templates, restrictions.
* chore(skills): remove commit skill (format in tool validator)
commit/SKILL.md deleted (format validated by commit.ts tool, PR#38). References updated: configure-opencode SKILL.md (2 places), add-skill SKILL.md (1 place + tree structure). project-map README: remove commit/SKILL.md from tree, add configure-opencode.
* docs(handoff): add ADR-017 and handoff for orchestration switch
PR number in handoff frontmatter is <PR-NUMBER> placeholder, will be set after gh pr create.
* docs(handoff): set PR number
Set PR number 42 in handoff frontmatter, rename handoff/ADR files from pr-41 to pr-42 (pipeline-status matches pr-{pr_number}-*.md in diff).
* docs: update project map + handoff + ADR
---------
Co-authored-by: opencode-agent <agent@slaid098.dev>
This commit is contained in:
parent
f9a9e0f854
commit
67174b4879
11 changed files with 245 additions and 133 deletions
|
|
@ -608,17 +608,17 @@ def get_pr_title(pr_number: int) -> str:
|
|||
|
||||
|
||||
NEXT_ACTIONS: dict[str, str] = {
|
||||
"ISSUE": "создать issue и связать через Closes #N в body PR",
|
||||
"IMPLEMENT": "добавить handoff docs/handoff/pr-N-slug.md в diff",
|
||||
"DOCS": "запустить docs-reviewer (режим pre-merge)",
|
||||
"ISSUE": "dispatch subagent (subagent_type=general, template=A) for PR #N",
|
||||
"IMPLEMENT": "dispatch subagent (subagent_type=general, template=A) for PR #N",
|
||||
"DOCS": "dispatch subagent (subagent_type=docs-reviewer, template=B) for PR #N",
|
||||
"CI": "проверь статус CI вручную (gh run view)",
|
||||
"REVIEW": "запустить reviewer (task subagent_type=reviewer)",
|
||||
"MERGE": "вызвать merge_pr tool ({pr_number: N})",
|
||||
"MEMORY": "запустить memory-syncer",
|
||||
"REVIEW": "dispatch subagent (subagent_type=reviewer, template=C) for PR #N",
|
||||
"MERGE": "call merge_pr tool with pr_number=N",
|
||||
"MEMORY": "dispatch subagent (subagent_type=memory-syncer, template=E) for PR #N",
|
||||
}
|
||||
|
||||
REVIEW_NEXT_REQUEST_CHANGES = (
|
||||
"запусти fix subagent (general) с prompt 'fix reviewer comments: <list>', "
|
||||
"dispatch subagent (subagent_type=general) with prompt 'fix reviewer comments: <list>', "
|
||||
"commit, push → re-loop (pipeline_status проверит CI автоматически)"
|
||||
)
|
||||
REVIEW_NEXT_NEEDS_DISCUSSION = "уточни вопросы с автором PR (verdict: NEEDS_DISCUSSION)"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ description: <когда загружать. Триггеры на русско
|
|||
├── add-skill/SKILL.md ← этот скилл
|
||||
├── branch/SKILL.md
|
||||
├── code-standards/SKILL.md
|
||||
├── commit/SKILL.md
|
||||
├── configure-opencode/SKILL.md
|
||||
├── get-project-map/SKILL.md
|
||||
├── issue/SKILL.md
|
||||
├── memory/SKILL.md
|
||||
|
|
@ -62,7 +62,7 @@ Opencode сканирует все под-директории `.opencode/skills
|
|||
|
||||
## 4. Commit и Push (для implementing agent)
|
||||
|
||||
Implementing agent (subagent, которому делегировано создание) после создания файла — сразу коммит и пуш по правилам скилла `commit`:
|
||||
Implementing agent (subagent, которому делегировано создание) после создания файла — сразу коммит и пуш по правилам `commit` tool (формат валидируется tool'ом):
|
||||
|
||||
```bash
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
---
|
||||
name: commit
|
||||
description: Analyse git history of any project and suggest commit messages that match existing conventions.
|
||||
---
|
||||
|
||||
## Процесс
|
||||
|
||||
1. Запустить `git log --oneline -20`
|
||||
2. Если коммиты есть:
|
||||
- Извлечь все уникальные `type(scope):` паттерны
|
||||
- Составить список реальных scopes проекта
|
||||
- Следовать найденному стилю
|
||||
3. Если коммитов нет (новый проект):
|
||||
- Базовый формат: `type(scope): description`
|
||||
- Типы: `feat | fix | chore | docs | refactor | test | style | perf`
|
||||
- Scope по умолчанию: спросить пользователя
|
||||
- Description — что сделано (кратко), на английском
|
||||
- Без точки в конце
|
||||
- Max ≤72 символа
|
||||
- Язык: English only — type, scope, description всё на английском
|
||||
|
||||
## Type
|
||||
|
||||
| Type | When |
|
||||
|---|---|
|
||||
| `feat` | New feature |
|
||||
| `fix` | Bug fix |
|
||||
| `chore` | Maintenance, cleanup, dependencies, config |
|
||||
| `refactor` | Code restructuring, no behavior change |
|
||||
| `docs` | Documentation files (AGENTS.md, SKILL.md, README, handoff, ADR, project-map, docs/) |
|
||||
| `test` | Adding or fixing tests |
|
||||
| `style` | Formatting, linting, whitespace only |
|
||||
| `perf` | Performance improvements |
|
||||
|
||||
## Branch naming
|
||||
|
||||
`type/scope/description` — kebab-case, из тех же scopes.
|
||||
|
||||
## Пример вывода для агента
|
||||
|
||||
Если `git log` показывает:
|
||||
```
|
||||
chore(config): add docker proxy
|
||||
fix(docker): resolve no-sandbox
|
||||
feat(agents.md): add guidelines
|
||||
```
|
||||
|
||||
То scopes: `config`, `docker`, `agents.md`. Новый коммит пишется в том же стиле.
|
||||
|
|
@ -18,7 +18,7 @@ description: Use when adding, changing, or removing MCP servers, providers, perm
|
|||
|
||||
## 2. Применение изменений
|
||||
|
||||
- `commit` + `push` в репо `slaid098/opencode-config` (через `commit` skill).
|
||||
- `commit` + `push` в репо `slaid098/opencode-config` (через `commit` tool).
|
||||
- В клонах: `git pull` + рестарт opencode (MCP-серверы, skills, agents грузятся при старте — см. `add-skill/SKILL.md`). До рестарта правки не видны.
|
||||
- Для Docker-сетапа: `git pull` на хосте + `docker compose restart opencode` (или эквивалент) — MCP/skills/agents грузятся при старте контейнера.
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ description: Use when adding, changing, or removing MCP servers, providers, perm
|
|||
## 8. Commit message
|
||||
|
||||
- Формат: `feat(config): ...` / `chore(config): ...` / `fix(config): ...` (conventional commits, English, ≤72 chars).
|
||||
- Перед commit — загрузить `commit` skill, проверить `git log --oneline -20`, match existing style.
|
||||
- Перед commit — использовать `commit` tool (валидация формата встроена), проверить `git log --oneline -20`, match existing style.
|
||||
- Примеры: `feat(config): add integrations MCP server`, `fix(config): correct timeout for integrations discover tool`.
|
||||
|
||||
## 9. Не дублировать блоки между репо
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ description: Автономный исполнитель PR-пайплайна.
|
|||
# Run Pipeline
|
||||
|
||||
Автономная процедура-loop для проведения PR через 7 фаз. Source of truth для
|
||||
порядка и действий — `pipeline_status` tool.
|
||||
порядка и действий — `pipeline_status` tool. NEXT action из tool явно
|
||||
указывает `subagent_type` + `template` (кроме MERGE — `merge_pr` tool).
|
||||
|
||||
## ПРОТОКОЛ (ЖЁСТКО)
|
||||
|
||||
|
|
@ -35,15 +36,6 @@ description: Автономный исполнитель PR-пайплайна.
|
|||
- `AMBIGUOUS` в выводе `pipeline_status` → STOP + report.
|
||||
- 5 итераций подряд без прогресса (та же фаза ❌) → STOP + report.
|
||||
|
||||
## Phase 0: Bootstrap
|
||||
|
||||
1. Если задача описана в чате, а не issue → dispatch subagent (general type)
|
||||
с инструкцией load `issue` skill и создать GitHub issue N. Main agent НЕ
|
||||
пишет body issue и НЕ запускает `gh issue create` (чистый orchestrator).
|
||||
2. Запусти subagent (general type) с prompt template A → PR M с `Closes #N` в
|
||||
body. Subagent вернёт PR номер M.
|
||||
3. Войди в loop ПРОТОКОЛ выше.
|
||||
|
||||
## Prompt templates
|
||||
|
||||
### Template A (implement_issue)
|
||||
|
|
@ -161,4 +153,4 @@ successfully ...` → 1 строка прогресса и re-loop (`pipeline_st
|
|||
- Если subagent error → 1 retry, потом STOP + report пользователю.
|
||||
- `merge_pr({ pr_number: M })` tool — единственный способ мержить PR (без
|
||||
`--admin`, без raw bash). См. Template F.
|
||||
- Если reviewer вердикт `REQUEST_CHANGES` → запусти subagent (general) с prompt "fix reviewer comments: <list>", commit, push → re-loop (`pipeline_status` проверит CI автоматически).
|
||||
- Если reviewer вердикт `REQUEST_CHANGES` → dispatch subagent (general) с prompt "fix reviewer comments: <list>", commit, push → re-loop (`pipeline_status` проверит CI автоматически).
|
||||
43
AGENTS.md
43
AGENTS.md
|
|
@ -8,52 +8,13 @@
|
|||
- Pipeline: каждую фазу (ISSUE → IMPLEMENT → DOCS → CI → REVIEW → MERGE → MEMORY) делегировать subagent'у.
|
||||
- Subagent error → 1 retry, потом STOP + report.
|
||||
|
||||
## Commits
|
||||
|
||||
- Language: English only — type, scope, and description all in English
|
||||
- Format: `type(scope): description` — ≤72 chars
|
||||
- Types: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `style`, `perf`
|
||||
- Before ANY commit: load `commit` skill (`skill("commit")`), run `git log --oneline -20`, match existing style
|
||||
- Break large changes into multiple short commits by logical parts
|
||||
- No period at end, no body unless necessary
|
||||
|
||||
## Pull Requests
|
||||
|
||||
- Title: на английском, формат `type(scope): what changed` — same principle as commits
|
||||
- Body: на русском, в Markdown — сначала **что** сделано, затем **почему** (мотивация, контекст)
|
||||
- Reference issues if applicable
|
||||
- Before creating PR: load `commit` skill, inspect `git diff` from base branch
|
||||
|
||||
## Development Workflow
|
||||
|
||||
All PR work runs through `/run-pipeline` (7 phases: ISSUE → IMPLEMENT → DOCS → CI → REVIEW → MERGE → MEMORY). Load the `run-pipeline` skill for the protocol.
|
||||
|
||||
1. **Plan** — discuss requirements in chat, understand scope
|
||||
2. **Issue** — create self-contained GitHub issue (full context, file list, exact content, acceptance criteria, dependencies). Load `issue` skill. Via subagent.
|
||||
3. **Subagent** — delegate to `task` subagent (general type):
|
||||
- Reads issue via `gh issue view N`
|
||||
- Branches off default branch, implements per spec, commits, pushes, creates PR with `Closes #N`
|
||||
- Creates handoff `docs/handoff/pr-<N>-<slug>.md` and ADR if architectural decision
|
||||
- Follows issue spec exactly — if spec has errors, report them, don't deviate
|
||||
4. **Review** — run docs-reviewer (`@docs-reviewer`, pre-merge) then reviewer (`@reviewer`):
|
||||
- docs-reviewer: updates project map + validates/fixes handoff + ADR, commits to PR branch
|
||||
- reviewer: posts `## Code Review Summary` comment with verdict APPROVE|REQUEST_CHANGES (does NOT merge)
|
||||
5. **Merge or Repeat**:
|
||||
- APPROVE → `merge_pr` tool (orchestrator-safe, via `run-pipeline` skill, after CI ✅)
|
||||
- Issues found → fix subagent (same branch, new commit) → re-loop → merge
|
||||
- REQUEST_CHANGES → fix subagent → re-review → merge
|
||||
6. **Memory-sync** — run memory-syncer (`@memory-syncer`):
|
||||
- Distills gotchas + ADR pointers from merged handoff into `app_data/opencode-memory/repos/{host}/{org}/{repo}.md`
|
||||
- Format: `- [YYYY-MM-DD, PR#N] <summary>`, receipt always (even if empty)
|
||||
- Calls `memory_save`, then guards against accidental commits to main repo
|
||||
All PR work runs through `/run-pipeline`. Pipeline phases orchestrated by `pipeline_status` tool (NEXT action per phase). Load `run-pipeline` skill.
|
||||
|
||||
## Pipeline
|
||||
|
||||
Основной pipeline для любой задачи — `/run-pipeline` в UI opencode. Skill выполняет 7 фаз (ISSUE → IMPLEMENT → DOCS → CI → REVIEW → MERGE → MEMORY) автономно, не импровизируя порядок. `merge_pr` tool — после CI ✅ (transitive guard в `pipeline-status.py`).
|
||||
|
||||
- Tool `pipeline_status` — read-only oracle, возвращает статус + NEXT action.
|
||||
- Tool `merge_pr` — orchestrator-safe merge wrapper (replaces raw `gh pr merge`).
|
||||
- Execution — через `/run-pipeline` (command `.opencode/commands/run-pipeline.md`).
|
||||
`pipeline_status` = read-only oracle (returns NEXT action). `merge_pr` = orchestrator-safe merge wrapper. Execution via `/run-pipeline` skill.
|
||||
|
||||
## Read Path
|
||||
|
||||
|
|
|
|||
24
docs/decisions/017-pr-42-orchestration-switch.md
Normal file
24
docs/decisions/017-pr-42-orchestration-switch.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# ADR-017: pipeline-status sole orchestrator + slim docs
|
||||
|
||||
## Статус
|
||||
Accepted (2026-07-24)
|
||||
|
||||
## Контекст
|
||||
PR #38 (build) добавил 3 детерминированных tool'а (`commit`, `create_pr`, `create_issue`) с валидацией форматов. PR #40 (lock) заблокировал прямые bash-вызовы мутаций через global deny rules + role-based tool access. Но промпты и оркестрация всё ещё ссылались на старые паттерны: `pipeline-status.py` NEXT_ACTIONS описывал действие текстом без указания `subagent_type`/`template`, run-pipeline skill дублировал 7 фаз в `Phase 0: Bootstrap`, AGENTS.md дублировал правила форматов commits/PRs (уже в tools), commit skill дублировал правила коммитов (уже в `commit.ts`).
|
||||
|
||||
Нужно: сделать `pipeline-status.py` единственным source of truth для оркестрации (NEXT action явно указывает `subagent_type` + `template`), убрать дублирование в AGENTS.md/run-pipeline skill, удалить commit skill (формат в `commit.ts`).
|
||||
|
||||
## Решение
|
||||
1. **`pipeline-status.py` NEXT_ACTIONS** — каждая фаза явно указывает `subagent_type` + `template` (кроме MERGE — `merge_pr` tool): ISSUE/IMPLEMENT → `subagent_type=general, template=A`; DOCS → `subagent_type=docs-reviewer, template=B`; REVIEW → `subagent_type=reviewer, template=C`; MERGE → `call merge_pr tool with pr_number=N`; MEMORY → `subagent_type=memory-syncer, template=E`. `REVIEW_NEXT_REQUEST_CHANGES` → `dispatch subagent (subagent_type=general)`. Формат `NEXT:` строки сохранён (pipeline-driver парсит без изменений).
|
||||
|
||||
2. **AGENTS.md slim** (71 → 31 строк) — убраны `## Commits` и `## Pull Requests` (форматы в tools). `## Development Workflow` и `## Pipeline` сокращены до 1-2 строк (ссылки на `/run-pipeline` + `pipeline_status`/`merge_pr` tools). Оставлены: Orchestrator Model, Read Path, Code Style, Language.
|
||||
|
||||
3. **run-pipeline SKILL.md slim** (164 → 155 строк) — убран `Phase 0: Bootstrap` (дублировал фазы). Оставлены: ПРОТОКОЛ, ЗАПРЕЩЕНО, Остановы, Prompt templates A-F, API Restrictions, Rules.
|
||||
|
||||
4. **commit skill удалён** — формат зашит в `commit.ts` валидаторе (PR#38). Ссылки в `configure-opencode/SKILL.md` и `add-skill/SKILL.md` обновлены на `commit` tool.
|
||||
|
||||
## Альтернативы
|
||||
- **Оставить NEXT_ACTIONS описательным текстом** — отклонено: run-pipeline skill должен парсить NEXT action и диспетчизировать subagent'ов. Без явного `subagent_type`/`template` skill должен был гадать или хардкодить маппинг фаза→subagent. Явное указание = single source of truth, deterministic.
|
||||
- **Не сокращать AGENTS.md/run-pipeline** — отклонено: дублирование правил форматов (AGENTS.md commits/PRs vs tools) и фаз (run-pipeline Phase 0 vs pipeline-status NEXT_ACTIONS) нарушает DRY и pure-orchestrator model. Tools уже валидируют форматы — текстовые правила в AGENTS.md не enforced.
|
||||
- **Оставить commit skill как pointer на tool** — отклонено: `commit.ts` уже содержит все правила валидации (regex, English, ≤72, staged check). Skill с текстом правил = дублирование. Если правила изменятся — нужно править 2 места. Удаление skill устраняет дублирование.
|
||||
- **Добавить отдельный tool для dispatch subagent** — отклонено: opencode уже имеет `task` subagent dispatch mechanism. NEXT action строка достаточно для run-pipeline skill чтобы вызвать `task` с правильным `subagent_type` и template. Новый tool = избыточность.
|
||||
37
docs/handoff/pr-42-orchestration-switch.md
Normal file
37
docs/handoff/pr-42-orchestration-switch.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
pr: 42
|
||||
title: pipeline-status sole orchestrator + slim docs
|
||||
---
|
||||
|
||||
# PR: pipeline-status sole orchestrator + slim docs
|
||||
|
||||
## Что сделано
|
||||
- `.opencode/scripts/pipeline-status.py` → `NEXT_ACTIONS`: каждая фаза теперь явно указывает `subagent_type` + `template` (кроме MERGE — `merge_pr` tool). ISSUE/IMPLEMENT → `subagent_type=general, template=A`; DOCS → `subagent_type=docs-reviewer, template=B`; REVIEW → `subagent_type=reviewer, template=C`; MERGE → `call merge_pr tool with pr_number=N` (не subagent); MEMORY → `subagent_type=memory-syncer, template=E`. `REVIEW_NEXT_REQUEST_CHANGES` → `dispatch subagent (subagent_type=general)`. Формат `NEXT:` строки сохранён (pipeline-driver парсит без изменений) — изменился только текст action.
|
||||
- `AGENTS.md` сокращён с 71 → 31 строк: убраны `## Commits` и `## Pull Requests` (форматы валидируются `commit`/`create_pr` tools, PR#38). `## Development Workflow` и `## Pipeline` сокращены до 1-2 строк (ссылки на `/run-pipeline` skill + `pipeline_status`/`merge_pr` tools). Оставлены без изменений: `## Orchestrator Model`, `## Read Path`, `## Code Style`, `## Language`.
|
||||
- `.opencode/skills/run-pipeline/SKILL.md` сокращён с 164 → 155 строк: убран `## Phase 0: Bootstrap` (8 строк, дублировал фазы — теперь порядок в `pipeline-status.py` NEXT_ACTIONS). Оставлены: ПРОТОКОЛ, ЗАПРЕЩЕНО, Остановы, Prompt templates A-F, API Restrictions, Rules. `## Rules` последняя строка обновлена: `запусти fix subagent` → `dispatch subagent (general)`.
|
||||
- `.opencode/skills/commit/SKILL.md` удалён (директория `.opencode/skills/commit/` удалена). Формат коммитов зашит в `commit.ts` валидаторе (PR#38).
|
||||
- Ссылки на commit skill обновлены: `configure-opencode/SKILL.md` (2 места: "через `commit` skill" → "через `commit` tool", "загрузить `commit` skill" → "использовать `commit` tool"), `add-skill/SKILL.md` (1 место: "по правилам скилла `commit`" → "по правилам `commit` tool" + убран `commit/SKILL.md` из дерева структуры skills, добавлен `configure-opencode/SKILL.md`).
|
||||
- `docs/project-map/README.md`: убран `commit/SKILL.md` из дерева skills, добавлен `configure-opencode/SKILL.md` (был вне алфавитного порядка), обновлено описание `AGENTS.md` ("commits, PRs" → "pipeline, formats enforced by tools").
|
||||
- `tests/test_pipeline_status.py`: обновлены 7 существующих тестов под новый формат NEXT_ACTIONS — `test_get_next_action` (parametrize: 7 фаз), `test_get_next_action_review` (5 cases), `test_format_single_pr_review_*` (3 теста), `test_format_single_pr_memory_not_done`, `test_format_table_with_prs`, `test_format_pr_row_review_request_changes`. Строки assertions изменены: `запустить reviewer` → `dispatch subagent (subagent_type=reviewer`, `запустить memory-syncer` → `dispatch subagent (subagent_type=memory-syncer`, и т.д.
|
||||
- `tests/test_pipeline_status_next_actions.py` (новый, 25 тестов): для каждой фазы NEXT action содержит `subagent_type` + `template` (кроме MERGE — `merge_pr`); ISSUE/IMPLEMENT → general/A, DOCS → docs-reviewer/B, REVIEW → reviewer/C, MERGE → merge_pr (не subagent), MEMORY → memory-syncer/E; pr_number substitution; REVIEW verdict branching (REQUEST_CHANGES → general, default → reviewer, NEEDS_DISCUSSION → no subagent).
|
||||
- ADR-017 + этот handoff
|
||||
|
||||
## Почему
|
||||
Третий PR из серии из 3 (build → lock → **switch**). PR #38 (build) добавил 3 детерминированных tool'а (`commit`, `create_pr`, `create_issue`) с валидацией форматов. PR #40 (lock) заблокировал прямые bash-вызовы `git commit`/`gh pr create`/`gh pr merge`/`gh issue create` через global deny rules + role-based tool access. Этот PR (switch) обновляет промпты и оркестрацию на использование tools — `pipeline-status.py` становится единственным source of truth для оркестрации (NEXT action явно указывает `subagent_type` + `template`), а AGENTS.md/run-pipeline skill сокращаются убирая дублирование.
|
||||
|
||||
`pipeline-status.py` NEXT_ACTIONS был описательным текстом ("создать issue", "запустить docs-reviewer") без указания какой subagent и какой template использовать. run-pipeline skill дублировал 7 фаз в `Phase 0: Bootstrap`. AGENTS.md дублировал правила форматов commits/PRs (уже зашиты в tools, PR#38). commit skill дублировал правила коммитов (уже в `commit.ts`, PR#38). Цель: single source of truth + убрать дублирование.
|
||||
|
||||
Спека issue #41 не содержала ошибок. Все acceptance criteria выполнены.
|
||||
|
||||
## Pending
|
||||
- `run-pipeline/SKILL.md` сокращён с 164 → 155 строк (спека просила ~90). Дальнейшее сокращение невозможно без удаления обязательных секций: спека явно требует оставить ПРОТОКОЛ + ЗАПРЕЩЕНО + Остановы + Prompt templates A-F + API Restrictions + Rules. Одни templates A-F = ~100 строк. Единственное дублирование фаз (`Phase 0: Bootstrap`) удалено.
|
||||
- `AGENTS.md` = 31 строк (спека просила ~25). 4 оставленные секции (Orchestrator Model 6 строк, Read Path 2, Code Style 5, Language 2) + 2 сокращённые (Development Workflow 2, Pipeline 2) + заголовки = 31. Дальнейшее сокращение потребует удаления обязательных секций.
|
||||
- `add-skill/SKILL.md` дерево skills теперь не содержит `commit/SKILL.md` и содержит `configure-opencode/SKILL.md` (раньше был вне алфавитного порядка в дереве)
|
||||
|
||||
## Watch out
|
||||
- **NEXT_ACTIONS format совместим с pipeline-driver**: `get_next_action` заменяет `N` на pr_number через `.replace("N", str(pr_number))`. Новые строки содержат `PR #N` и `pr_number=N` — оба корректно заменяются. `REVIEW_NEXT_DEFAULT = NEXT_ACTIONS["REVIEW"]` — default branch `get_next_action_review` теперь возвращает `dispatch subagent (subagent_type=reviewer, template=C) for PR #N`. pipeline-driver парсит `NEXT:` строку целиком — изменений в формате строки нет, изменился только текст action.
|
||||
- **7 существующих тестов обновлены**: `test_get_next_action`, `test_get_next_action_review`, `test_format_single_pr_review_not_done`, `test_format_single_pr_review_request_changes`, `test_format_single_pr_review_needs_discussion`, `test_format_single_pr_memory_not_done`, `test_format_table_with_prs`, `test_format_pr_row_review_request_changes`. Не удалены, а обновлены под новый формат — это часть реализации (формат изменился).
|
||||
- **commit skill удалён полностью** (директория `.opencode/skills/commit/`). `commit.ts` tool (PR#38) валидирует формат независимо. Skills auto-discovered при старте opencode — после merge нужен `git pull` + рестарт контейнера (config bind-mount, skills грузятся при старте).
|
||||
- **Ссылки на commit skill обновлены в 3 файлах**: `configure-opencode/SKILL.md` (2 места), `add-skill/SKILL.md` (1 место + дерево). `opencode.json` `commit: true/false` — это tool access (не skill), без изменений. В handoff/ADR других PR упоминания commit skill оставлены как исторические (не актуальны).
|
||||
- ADR number = sequential (017), НЕ PR number. Проверить ADR naming в handoff до push (эволюция паттерна PR#26 docs-reviewer typo).
|
||||
- PR number = 42 (установлен после `gh pr create`, отдельный коммит `docs(handoff): set PR number`). Handoff/ADR файлы переименованы с `pr-41-*` → `pr-42-*` (pipeline-status ищет `pr-{pr_number}-*.md` в diff, pr_number=42).
|
||||
|
|
@ -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).
|
||||
|
||||
Root `AGENTS.md` — orchestrator directive (chat = plan only, all via subagents) + global rules (commits, PRs, code style, language RU). 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). Formats enforced by tools (commit/create_pr/create_issue). Auto-loaded for project + bind-mounted globally in container — PR#31.
|
||||
|
||||
## Structure
|
||||
|
||||
|
|
@ -27,11 +27,10 @@ opencode-config/
|
|||
│ │ ├── add-skill/SKILL.md # Create new opencode skill
|
||||
│ │ ├── branch/SKILL.md # Branch naming conventions
|
||||
│ │ ├── code-standards/SKILL.md # Universal code style rules
|
||||
│ │ ├── commit/SKILL.md # Commit message conventions
|
||||
│ │ ├── configure-opencode/SKILL.md # Canonical rule: write to .opencode/
|
||||
│ │ ├── get-project-map/SKILL.md # Maintain docs/project-map/
|
||||
│ │ ├── issue/SKILL.md # GitHub issue creation
|
||||
│ │ ├── memory/SKILL.md # opencode-memory usage guide
|
||||
│ │ ├── configure-opencode/SKILL.md # Canonical rule: write to .opencode/
|
||||
│ │ ├── run-pipeline/SKILL.md # 7-phase pipeline orchestration
|
||||
│ │ ├── python-development/SKILL.md # Python dev patterns
|
||||
│ │ ├── release/SKILL.md # Tag + GitHub Release
|
||||
|
|
@ -51,7 +50,7 @@ opencode-config/
|
|||
│ │ ├── check-adr-refs.py # ADR cross-reference validator (adr-check.yml)
|
||||
│ │ ├── check-permissions.py # Permissions validator (permissions-check.yml)
|
||||
│ │ ├── observability.py # OTel spans for tools
|
||||
│ │ ├── pipeline-status.py # 7-phase oracle (gh PR + CI polling)
|
||||
│ │ ├── pipeline-status.py # 7-phase oracle (gh PR + CI polling, NEXT_ACTIONS with subagent_type+template) — PR#42
|
||||
│ │ ├── scaffold-handoff.sh # Scaffold handoff + ADR stubs
|
||||
│ │ ├── setup-memory.sh # opencode-memory bootstrap (deterministic 6-step flow, idempotent) — PR#36
|
||||
│ │ ├── spec-status.py # 9-phase spec oracle
|
||||
|
|
@ -91,6 +90,7 @@ opencode-config/
|
|||
│ ├── test_pipeline_status.py # .opencode/scripts/pipeline-status.py (REVIEW verdict branching)
|
||||
│ ├── test_pipeline_status_adr.py
|
||||
│ ├── test_pipeline_status_ci.py
|
||||
│ ├── test_pipeline_status_next_actions.py # NEXT_ACTIONS subagent_type+template per phase (25 tests) — PR#42
|
||||
│ ├── test_pipeline_status_tool.py
|
||||
│ ├── test_pipeline_status_tool.ts # TS wrapper test (mjs loader)
|
||||
│ ├── test_search.py # src/memory/search.py
|
||||
|
|
|
|||
|
|
@ -851,7 +851,7 @@ def test_format_single_pr_review_not_done(monkeypatch):
|
|||
ps, "run_cmd", mock_run_cmd({("gh", "pr", "view"): (0, '{"title": "test"}', "")})
|
||||
)
|
||||
output = ps.format_single_pr(46, results)
|
||||
assert "NEXT: запустить reviewer" in output
|
||||
assert "NEXT: dispatch subagent (subagent_type=reviewer" in output
|
||||
assert "❌" in output
|
||||
|
||||
|
||||
|
|
@ -870,9 +870,9 @@ def test_format_single_pr_review_request_changes(monkeypatch):
|
|||
ps, "run_cmd", mock_run_cmd({("gh", "pr", "view"): (0, '{"title": "test"}', "")})
|
||||
)
|
||||
output = ps.format_single_pr(46, results)
|
||||
assert "NEXT: запусти fix subagent" in output
|
||||
assert "NEXT: dispatch subagent (subagent_type=general)" in output
|
||||
assert "re-loop" in output
|
||||
assert "запустить reviewer" not in output.split("NEXT:")[1]
|
||||
assert "dispatch subagent (subagent_type=reviewer" not in output.split("NEXT:")[1]
|
||||
|
||||
|
||||
def test_format_single_pr_review_needs_discussion(monkeypatch):
|
||||
|
|
@ -891,7 +891,7 @@ def test_format_single_pr_review_needs_discussion(monkeypatch):
|
|||
)
|
||||
output = ps.format_single_pr(46, results)
|
||||
assert "NEXT: уточни вопросы" in output
|
||||
assert "запустить reviewer" not in output.split("NEXT:")[1]
|
||||
assert "dispatch subagent (subagent_type=reviewer" not in output.split("NEXT:")[1]
|
||||
|
||||
|
||||
def test_format_single_pr_memory_not_done(monkeypatch):
|
||||
|
|
@ -908,7 +908,7 @@ def test_format_single_pr_memory_not_done(monkeypatch):
|
|||
ps, "run_cmd", mock_run_cmd({("gh", "pr", "view"): (0, '{"title": "test"}', "")})
|
||||
)
|
||||
output = ps.format_single_pr(46, results)
|
||||
assert "NEXT: запустить memory-syncer" in output
|
||||
assert "NEXT: dispatch subagent (subagent_type=memory-syncer" in output
|
||||
|
||||
|
||||
def test_format_single_pr_ambiguous(monkeypatch):
|
||||
|
|
@ -945,7 +945,7 @@ def test_format_table_with_prs(monkeypatch):
|
|||
monkeypatch.setattr(ps, "get_pr_title", lambda n: "test PR title")
|
||||
output = ps.format_table([47])
|
||||
assert "PR#47" in output
|
||||
assert "NEXT: запустить docs-reviewer (режим pre-merge)" in output
|
||||
assert "NEXT: dispatch subagent (subagent_type=docs-reviewer" in output
|
||||
|
||||
|
||||
def test_format_pr_row_review_request_changes(monkeypatch):
|
||||
|
|
@ -962,8 +962,8 @@ def test_format_pr_row_review_request_changes(monkeypatch):
|
|||
monkeypatch.setattr(ps, "run_all_checks", lambda n: results)
|
||||
monkeypatch.setattr(ps, "get_pr_title", lambda n: "test")
|
||||
output = ps.format_pr_row(47)
|
||||
assert "NEXT: запусти fix subagent" in output
|
||||
assert "запустить reviewer" not in output.split("NEXT:")[1]
|
||||
assert "NEXT: dispatch subagent (subagent_type=general)" in output
|
||||
assert "dispatch subagent (subagent_type=reviewer" not in output.split("NEXT:")[1]
|
||||
|
||||
|
||||
# ── get_next_action ──────────────────────────────────────────────────────────
|
||||
|
|
@ -972,13 +972,13 @@ def test_format_pr_row_review_request_changes(monkeypatch):
|
|||
@pytest.mark.parametrize(
|
||||
("phase", "expected"),
|
||||
[
|
||||
("ISSUE", "создать issue и связать через Closes #46 в body PR"),
|
||||
("IMPLEMENT", "добавить handoff docs/handoff/pr-46-slug.md в diff"),
|
||||
("DOCS", "запустить docs-reviewer (режим pre-merge)"),
|
||||
("ISSUE", "dispatch subagent (subagent_type=general, template=A) for PR #46"),
|
||||
("IMPLEMENT", "dispatch subagent (subagent_type=general, template=A) for PR #46"),
|
||||
("DOCS", "dispatch subagent (subagent_type=docs-reviewer, template=B) for PR #46"),
|
||||
("CI", "проверь статус CI вручную (gh run view)"),
|
||||
("REVIEW", "запустить reviewer (task subagent_type=reviewer)"),
|
||||
("MERGE", "вызвать merge_pr tool ({pr_number: 46})"),
|
||||
("MEMORY", "запустить memory-syncer"),
|
||||
("REVIEW", "dispatch subagent (subagent_type=reviewer, template=C) for PR #46"),
|
||||
("MERGE", "call merge_pr tool with pr_number=46"),
|
||||
("MEMORY", "dispatch subagent (subagent_type=memory-syncer, template=E) for PR #46"),
|
||||
],
|
||||
)
|
||||
def test_get_next_action(phase, expected):
|
||||
|
|
@ -988,11 +988,17 @@ def test_get_next_action(phase, expected):
|
|||
@pytest.mark.parametrize(
|
||||
("detail", "expected_substring"),
|
||||
[
|
||||
("последний verdict reviewer'а: REQUEST_CHANGES", "запусти fix subagent"),
|
||||
(
|
||||
"последний verdict reviewer'а: REQUEST_CHANGES",
|
||||
"dispatch subagent (subagent_type=general)",
|
||||
),
|
||||
("последний verdict reviewer'а: NEEDS_DISCUSSION", "уточни вопросы с автором"),
|
||||
("Code Review Summary не найден в комментариях", "запустить reviewer"),
|
||||
("APPROVE не найден", "запустить reviewer"),
|
||||
("нет комментариев PR", "запустить reviewer"),
|
||||
(
|
||||
"Code Review Summary не найден в комментариях",
|
||||
"dispatch subagent (subagent_type=reviewer",
|
||||
),
|
||||
("APPROVE не найден", "dispatch subagent (subagent_type=reviewer"),
|
||||
("нет комментариев PR", "dispatch subagent (subagent_type=reviewer"),
|
||||
],
|
||||
)
|
||||
def test_get_next_action_review(detail, expected_substring):
|
||||
|
|
|
|||
140
tests/test_pipeline_status_next_actions.py
Normal file
140
tests/test_pipeline_status_next_actions.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
"""Tests for .opencode/scripts/pipeline-status.py — NEXT_ACTIONS orchestration.
|
||||
|
||||
Verifies each phase's NEXT action explicitly specifies ``subagent_type`` and
|
||||
``template`` (except MERGE which calls ``merge_pr`` tool directly). This makes
|
||||
``pipeline_status`` the single source of truth for orchestration — the
|
||||
``run-pipeline`` skill parses the ``NEXT:`` line and dispatches accordingly.
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPT_PATH = (
|
||||
Path(__file__).resolve().parent.parent / ".opencode" / "scripts" / "pipeline-status.py"
|
||||
)
|
||||
spec = importlib.util.spec_from_file_location("pipeline_status_next_actions", SCRIPT_PATH)
|
||||
ps = importlib.util.module_from_spec(spec)
|
||||
sys.modules["pipeline_status_next_actions"] = ps
|
||||
spec.loader.exec_module(ps)
|
||||
|
||||
|
||||
# ── NEXT_ACTIONS: subagent_type + template per phase ─────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"phase",
|
||||
["ISSUE", "IMPLEMENT"],
|
||||
)
|
||||
def test_next_action_general_subagent_template_a(phase):
|
||||
"""ISSUE/IMPLEMENT → subagent_type=general, template=A."""
|
||||
action = ps.get_next_action(phase, 41)
|
||||
assert "subagent_type=general" in action
|
||||
assert "template=A" in action
|
||||
assert "PR #41" in action
|
||||
|
||||
|
||||
def test_next_action_docs_subagent_template_b():
|
||||
"""DOCS → subagent_type=docs-reviewer, template=B."""
|
||||
action = ps.get_next_action("DOCS", 41)
|
||||
assert "subagent_type=docs-reviewer" in action
|
||||
assert "template=B" in action
|
||||
assert "PR #41" in action
|
||||
|
||||
|
||||
def test_next_action_review_subagent_template_c():
|
||||
"""REVIEW → subagent_type=reviewer, template=C."""
|
||||
action = ps.get_next_action("REVIEW", 41)
|
||||
assert "subagent_type=reviewer" in action
|
||||
assert "template=C" in action
|
||||
assert "PR #41" in action
|
||||
|
||||
|
||||
def test_next_action_merge_pr_tool_not_subagent():
|
||||
"""MERGE → merge_pr tool (NOT a subagent dispatch)."""
|
||||
action = ps.get_next_action("MERGE", 41)
|
||||
assert "merge_pr" in action
|
||||
assert "pr_number=41" in action
|
||||
assert "subagent_type" not in action
|
||||
assert "template=" not in action
|
||||
|
||||
|
||||
def test_next_action_memory_subagent_template_e():
|
||||
"""MEMORY → subagent_type=memory-syncer, template=E."""
|
||||
action = ps.get_next_action("MEMORY", 41)
|
||||
assert "subagent_type=memory-syncer" in action
|
||||
assert "template=E" in action
|
||||
assert "PR #41" in action
|
||||
|
||||
|
||||
# ── NEXT_ACTIONS: every dispatchable phase has subagent_type ──────────────────
|
||||
|
||||
|
||||
DISPATCH_PHASES = ["ISSUE", "IMPLEMENT", "DOCS", "REVIEW", "MEMORY"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("phase", DISPATCH_PHASES)
|
||||
def test_dispatch_phases_have_subagent_type(phase):
|
||||
"""All dispatch phases (except MERGE) include subagent_type in NEXT action."""
|
||||
action = ps.get_next_action(phase, 41)
|
||||
assert "subagent_type=" in action, f"{phase} NEXT action missing subagent_type: {action}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("phase", DISPATCH_PHASES)
|
||||
def test_dispatch_phases_have_template(phase):
|
||||
"""All dispatch phases (except MERGE) include template=X in NEXT action."""
|
||||
action = ps.get_next_action(phase, 41)
|
||||
assert "template=" in action, f"{phase} NEXT action missing template: {action}"
|
||||
|
||||
|
||||
def test_merge_phase_does_not_dispatch_subagent():
|
||||
"""MERGE is a tool call, not a subagent dispatch — no subagent_type/template."""
|
||||
action = ps.get_next_action("MERGE", 41)
|
||||
assert "subagent_type" not in action
|
||||
assert "template=" not in action
|
||||
assert "merge_pr" in action
|
||||
|
||||
|
||||
# ── NEXT_ACTIONS: pr_number substitution ──────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("phase", "pr_number"),
|
||||
[("ISSUE", 1), ("DOCS", 999), ("REVIEW", 42), ("MERGE", 7), ("MEMORY", 100)],
|
||||
)
|
||||
def test_next_action_pr_number_substitution(phase, pr_number):
|
||||
"""``N`` placeholder in NEXT_ACTIONS is replaced with the actual PR number."""
|
||||
action = ps.get_next_action(phase, pr_number)
|
||||
assert str(pr_number) in action
|
||||
# The literal "N" placeholder must not survive substitution.
|
||||
assert " PR #N" not in action
|
||||
assert "pr_number=N" not in action
|
||||
|
||||
|
||||
# ── REVIEW verdict branching still uses subagent_type ─────────────────────────
|
||||
|
||||
|
||||
def test_review_request_changes_dispatches_general_subagent():
|
||||
"""REVIEW REQUEST_CHANGES → fix subagent (general), not re-run reviewer."""
|
||||
result = ps.PhaseResult(ps.PhaseStatus.NOT_DONE, "verdict: REQUEST_CHANGES")
|
||||
action = ps.get_next_action_review(result)
|
||||
assert "subagent_type=general" in action
|
||||
assert "fix reviewer comments" in action
|
||||
|
||||
|
||||
def test_review_default_dispatches_reviewer_subagent():
|
||||
"""REVIEW default (no verdict) → dispatch reviewer subagent (template C)."""
|
||||
result = ps.PhaseResult(ps.PhaseStatus.NOT_DONE, "нет комментариев PR")
|
||||
action = ps.get_next_action_review(result)
|
||||
assert "subagent_type=reviewer" in action
|
||||
assert "template=C" in action
|
||||
|
||||
|
||||
def test_review_needs_discussion_no_subagent():
|
||||
"""REVIEW NEEDS_DISCUSSION → clarification, not a subagent dispatch."""
|
||||
result = ps.PhaseResult(ps.PhaseStatus.NOT_DONE, "verdict: NEEDS_DISCUSSION")
|
||||
action = ps.get_next_action_review(result)
|
||||
assert "уточни вопросы" in action
|
||||
assert "subagent_type" not in action
|
||||
Loading…
Add table
Reference in a new issue