auto-sync: 2026-06-03 08:50:01

This commit is contained in:
Stream
2026-06-03 08:50:01 +03:00
parent b39683a288
commit d0ea5c959c
3 changed files with 179 additions and 0 deletions

View 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. **НЕ мержи** — мерж делает Стрим после проверки.
- Других исполнителей на этом репо нет — только ты, не паникуй про параллельные сессии.

View File

@@ -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)
## Изменённые файлы
- (в процессе)
## Результат
(в процессе)