auto-sync: 2026-06-03 08:50:01
This commit is contained in:
63
tasks/orchestrator/DEV_TASK_ORCH4_STAGE_ENGINE.md
Normal file
63
tasks/orchestrator/DEV_TASK_ORCH4_STAGE_ENGINE.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# DEV TASK — ORCH-4: единый stage-engine (M-3)
|
||||
|
||||
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500)
|
||||
**Ветка:** `feature/ORCH-4-stage-engine` из свежего main (main содержит ORCH-1/1b/2/6/7).
|
||||
|
||||
⚠️ **ВАЖНО про push (грабля ORCH-7):** после коммитов ОБЯЗАТЕЛЬНО проверь что ветка реально указывает на твои коммиты и они на remote:
|
||||
`git log origin/main..HEAD` (должны быть твои коммиты) и после push `git ls-remote origin feature/ORCH-4-stage-engine`. Не отчитывайся «готово» пока `git log origin/main..origin/feature/ORCH-4-stage-engine` не покажет твои коммиты.
|
||||
|
||||
## Проблема (M-3)
|
||||
Логика перехода стадий ДУБЛИРУЕТСЯ в двух местах и **семантически разошлась**:
|
||||
- `src/agents/launcher.py` → `def _try_advance_stage(self, run_id, agent, repo, branch)` (sync, ~174 строки) — вызывается после завершения агента (строка ~362).
|
||||
- `src/webhooks/plane.py` → `async def _try_advance_stage(task_id, current_stage, repo, work_item_id, branch)` (async) — вызывается из plane-webhook (строка ~257).
|
||||
|
||||
**Это НЕ просто дубль — версии делают РАЗНОЕ.** Нельзя «оставить одну и удалить другую». Надо СЛИТЬ обе в один движок, сохранив ВСЮ логику и устранив расхождения.
|
||||
|
||||
## Расхождения, которые ОБЯЗАТЕЛЬНО сохранить/исправить
|
||||
1. 🔴 **БАГ выбора агента (исправить):** launcher запускает `get_agent_for_stage(next_stage)`, plane — `get_agent_for_stage(current_stage)`. Семантика `stages.py`: `get_agent_for_stage(stage)` = "agent to launch when advancing FROM this stage". Правильно по `STAGE_TRANSITIONS`: при переходе current→next запускается агент, прописанный в transition[current]["agent"] (т.е. `get_agent_for_stage(current_stage)`). **СВЕРЬ по stages.py какой вариант корректен и приведи к одному.** Обоснуй в отчёте. (Похоже правильный = current_stage, но launcher-логика построена на next — РАЗБЕРИСЬ и не сломай реальный пайплайн.)
|
||||
2. 🔴 **Бизнес-логика rollback/retry — ТОЛЬКО в launcher, сохранить целиком:**
|
||||
- analyst approved-флоу: artifacts готовы → `set_issue_in_review` + plane-коммент с просьбой :approved:; иначе questions-файл → `set_issue_needs_input`; иначе предупреждение.
|
||||
- reviewer REQUEST_CHANGES → rollback в development, retry-счётчик по `agent_runs WHERE agent='developer'`, макс 3, иначе telegram-алерт.
|
||||
- tester check_tests_passed FAIL → rollback в development + retry (макс 3 → `set_issue_blocked`).
|
||||
- architect conflict (10-conflict.md существует) → rollback в analysis + enqueue analyst.
|
||||
3. 🟡 **check_review_approved (поиск PR по ветке через Gitea) — ТОЛЬКО в plane, сохранить.** launcher этого не умеет.
|
||||
4. Разные QG-сигнатуры обработаны в обоих (`check_ci_green/check_tests_local`→(repo,branch); `check_tests_passed`→(repo,wi,branch); артефактные→(repo,wi,branch)). Свести в одну диспетчеризацию.
|
||||
|
||||
## Что сделать
|
||||
1. **Создать единый движок.** Предлагаю `src/stage_engine.py` с одной функцией, например:
|
||||
`def advance_stage(task_id, current_stage, repo, work_item_id, branch, finished_agent=None) -> AdvanceResult`
|
||||
- sync (launcher вызывает напрямую; plane-webhook вызывает через `run_in_threadpool`/`asyncio.to_thread`, чтобы не плодить async-дубль). Если проще оставить sync и в plane обернуть — так и сделай, главное ОДНА реализация.
|
||||
- Внутри: lookup task → QG-диспетчеризация (все сигнатуры) → при fail вся rollback/retry-логика → при pass advance + enqueue правильного агента.
|
||||
- `finished_agent` нужен для веток approved/REQUEST_CHANGES (launcher знал агента; в plane его можно вывести из стадии или передать None — тогда approved-спец-логика по агенту просто не триггерится, это OK для webhook-пути).
|
||||
2. **launcher._try_advance_stage** → тонкая обёртка: достаёт task по (repo,branch), зовёт `advance_stage(...)`. Старую 174-строчную логику удалить из launcher (она переезжает в движок).
|
||||
3. **plane._try_advance_stage** → тонкая обёртка над тем же движком (через threadpool). Удалить дублирующее тело.
|
||||
4. НЕ менять `stages.py` STAGE_TRANSITIONS (только читать). Если найдёшь там баг — отчитайся, НЕ правь без согласования.
|
||||
|
||||
## Ограничения
|
||||
- 🚫 НЕ трогай: nginx, openclaw.json, .env-секреты, deploy-хук, Plane-webhook is_active, очередь ORCH-1, resilience ORCH-1b, worktree ORCH-2, multi-repo ORCH-6, watchdog ORCH-7.
|
||||
- ⚠️ Поведение пайплайна на happy-path и на всех rollback-ветках ДОЛЖНО остаться идентичным (кроме исправления бага выбора агента — его исправить и задокументировать).
|
||||
- ⚠️ enqueue_job (очередь ORCH-1) — использовать как сейчас, не возвращаться к прямым потокам.
|
||||
- Conventional Commits, осмысленные коммиты: `refactor(stage): extract unified stage_engine.advance_stage (M-3)`, `refactor(launcher,plane): delegate to stage_engine`, `fix(stage): correct agent-for-stage selection`, `test(stage): ...`, `docs(...)`.
|
||||
|
||||
## Тесты (в контейнере)
|
||||
- Новый `tests/test_stage_engine.py`: happy-path advance каждой стадии; QG fail → не двигает; reviewer REQUEST_CHANGES → rollback+enqueue developer; retry>3 → алерт без enqueue; tester FAIL → rollback; architect conflict → rollback в analysis; правильный агент запускается на каждом переходе.
|
||||
- Проверь что launcher и plane оба зовут движок (мок движка, assert вызван с верными аргументами).
|
||||
- Прогон: `IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q`
|
||||
- Baseline: сейчас **118 passed**, 9 pre-existing webhook-401 не трогать. Новые зелёные, старые не сломать.
|
||||
|
||||
## Acceptance (проверит Стрим вживую)
|
||||
| # | Что | Критерий |
|
||||
|---|-----|----------|
|
||||
| 1 | единый движок | одна реализация `advance_stage`, launcher+plane = тонкие обёртки |
|
||||
| 2 | бизнес-логика | ВСЕ rollback/retry/approved/conflict ветки сохранены |
|
||||
| 3 | баг агента | выбор агента приведён к корректному, задокументирован |
|
||||
| 4 | check_review_approved | сохранён (поиск PR по ветке) |
|
||||
| 5 | тесты | новые зелёные, baseline 118 не сломан |
|
||||
| 6 | прод | пересобран из ветки, health ok, /queue ok |
|
||||
|
||||
## Деплой + отчёт
|
||||
- `docker compose up -d --build && sleep 6 && curl -s :8500/health && curl -s :8500/queue`
|
||||
- После каждого блока — короткий отчёт + результат проверки.
|
||||
- ⚠️ Если при разборе бага выбора агента поймёшь что launcher-поведение (next_stage) на самом деле правильное, а plane (current_stage) — баг (или наоборот) — ОТЧИТАЙСЯ с обоснованием по stages.py, не угадывай.
|
||||
- Запушь ветку (проверь remote!), открой PR в main. **НЕ мержи** — мерж делает Стрим после проверки.
|
||||
- Других исполнителей на этом репо нет — только ты, не паникуй про параллельные сессии.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Dev Report: ORCH-4 — единый stage-engine (M-3)
|
||||
Дата: 2026-06-03
|
||||
Статус: IN PROGRESS
|
||||
|
||||
## Задача
|
||||
Слить две разошедшиеся реализации `_try_advance_stage` (launcher sync ~174 строки + plane async) в один движок `src/stage_engine.py:advance_stage(...)`. launcher и plane → тонкие обёртки. Исправить баг выбора агента, сохранить всю rollback/retry-логику и check_review_approved.
|
||||
|
||||
## Анализ бага выбора агента (КРИТИЧНО)
|
||||
`stages.py` STAGE_TRANSITIONS: каждый `agent` = "agent to launch when advancing FROM this stage" (подтверждено docstring `get_agent_for_stage`).
|
||||
- `analysis: {next: architecture, agent: architect}` — при уходе ИЗ analysis запускается **architect**.
|
||||
- `development: {next: review, agent: reviewer}` — при уходе ИЗ development запускается **reviewer**.
|
||||
|
||||
Значит при переходе current→next правильный агент = `get_agent_for_stage(current_stage)`.
|
||||
|
||||
**Вердикт:**
|
||||
- `plane._try_advance_stage` — ПРАВИЛЬНО: `get_agent_for_stage(current_stage)`.
|
||||
- `launcher._try_advance_stage` (стр.741) — **БАГ**: `get_agent_for_stage(next_stage)`.
|
||||
Пример: analyst finishes в analysis, advance→architecture, launcher брал `get_agent_for_stage("architecture")="developer"` — запускал developer ВМЕСТО architect (пропуск стадии).
|
||||
- Подтверждение: `src/webhooks/gitea.py` (вне scope, не трогаю) ТОЖЕ использует `get_agent_for_stage(current_stage)` в 3 местах — корректная семантика.
|
||||
|
||||
**Фикс:** унифицировано на `get_agent_for_stage(current_stage)`.
|
||||
|
||||
## Сделано
|
||||
- [x] Прочитан весь код: stages.py, launcher.py, plane.py, gitea.py, db.py, qg/checks.py, тесты
|
||||
- [x] Разобран баг выбора агента, вердикт зафиксирован
|
||||
- [x] Создана ветка feature/ORCH-4-stage-engine из свежего main
|
||||
- [ ] Создан src/stage_engine.py с advance_stage(...)
|
||||
- [ ] launcher → тонкая обёртка
|
||||
- [ ] plane → тонкая обёртка (через asyncio.to_thread)
|
||||
- [ ] tests/test_stage_engine.py
|
||||
- [ ] прогон тестов в контейнере (baseline 118)
|
||||
- [ ] деплой + health/queue
|
||||
- [ ] push + PR (проверка remote!)
|
||||
|
||||
## QG-сигнатуры (диспетчеризация)
|
||||
- check_ci_green, check_tests_local → (repo, branch)
|
||||
- check_review_approved → (repo, pr_number) [спец: поиск PR по ветке]
|
||||
- остальные (check_analysis_approved/complete, check_architecture_done, check_tests_passed, check_reviewer_verdict) → (repo, work_item_id, branch)
|
||||
|
||||
## Изменённые файлы
|
||||
- (в процессе)
|
||||
|
||||
## Результат
|
||||
(в процессе)
|
||||
Reference in New Issue
Block a user