From eb53ce5bc25e55a419cbbf0f8701d7d557537f98 Mon Sep 17 00:00:00 2001 From: Stream Date: Sun, 7 Jun 2026 23:50:01 +0300 Subject: [PATCH] auto-sync: 2026-06-07 23:50:01 --- .../STATUS_MODEL_DEEP_ANALYSIS.md | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 tasks/orchestrator/STATUS_MODEL_DEEP_ANALYSIS.md diff --git a/tasks/orchestrator/STATUS_MODEL_DEEP_ANALYSIS.md b/tasks/orchestrator/STATUS_MODEL_DEEP_ANALYSIS.md new file mode 100644 index 0000000..3b589c3 --- /dev/null +++ b/tasks/orchestrator/STATUS_MODEL_DEEP_ANALYSIS.md @@ -0,0 +1,133 @@ +# Глубокий аудит ядра орка под новую статусную модель + +Дата: 2026-06-07 · Автор: Стрим · Уровень: ЯДРО (stage_engine/webhooks/plane_sync/stages) + +## 0. Метод +Прочитан реальный код прода: `src/stages.py`, `src/stage_engine.py`, `src/webhooks/plane.py`, +`src/plane_sync.py`, `src/reconciler.py`. Ниже — что есть, какие зазоры с моделью, и что +доработать. Каждый пункт — с привязкой к коду. + +## 1. КРИТИЧЕСКОЕ различие: «стадия конвейера» ≠ «Plane-статус» +Это два РАЗНЫХ слоя, их легко спутать и сломать. + +### Слой A — STAGE_TRANSITIONS (`stages.py`) — машина СТАДИЙ (internal) +``` +created → analysis → architecture → development → review → testing → +deploy-staging → deploy → done +``` +Каждая стадия: `{next, agent, qg}`. Это движок: какой агент запускается, какой QG-гейт +проходится. **Здесь НЕТ Awaiting Deploy / Deploying / Monitoring** — это внутренние стадии, +а наши новые статусы — отображение на доске Plane. + +### Слой B — Plane-статусы (`plane_sync.py`) — ИНДИКАЦИЯ на доске +`_STAGE_TO_STATE_KEY` маппит стадию → Plane-статус. Сейчас: +- analysis → in_progress, deploy → in_progress (ПЕРЕГРУЗ), остальные → одноимённые. + +### ⚠️ Вывод +Новая модель меняет в основном **слой B (Plane-индикацию)** + точки, где Phase A/B/C ставят +статусы вручную. **STAGE_TRANSITIONS трогать минимально** (риск регресса всего конвейера). +Новые статусы — это переименования/добавления в маппинге + ручных set_issue_*. + +## 2. Маппинг новой модели на стадии (что куда) +| Стадия (слой A) | Сейчас Plane | НАДО (новая модель) | Как ставится | +|---|---|---|---| +| (человек жмёт старт) | In Progress | **To Analyse** (вход-триггер, переименование In Progress) | человек | +| analysis | In Progress | **Analysis** (новый выходной) | орк при старте analyst | +| analysis → approve-pending | In Review | In Review (без изменений) | `_handle_analysis_approved_flow` | +| analysis → вопросы | Needs Input | Needs Input (уже есть!) | `set_issue_needs_input` (01-questions.md) | +| architecture | Architecture | Architecture | stage_to_state | +| development | Development | Development | stage_to_state | +| review | Review | **Code-Review** (переименование) | stage_to_state | +| testing | Testing | Testing | stage_to_state | +| deploy-staging → Phase A | In Review | **Awaiting Deploy** | `_handle_self_deploy_phase_a` (стр 1012) | +| deploy → Phase B старт | (In Progress) | **Deploying** | `_handle_self_deploy_phase_b` | +| deploy → Phase C health-OK | Done | **Monitoring after Deploy** | deploy-finalizer | +| post-deploy окно closed HEALTHY | (Done) | **Done** | post-deploy-monitor (ORCH-21) | +| любой фейл деплоя/окна | Blocked | **Blocked** | set_issue_blocked | + +## 3. Needs Input — ГЛАВНЫЙ запрос Славы. Что есть, чего нет. + +### ✅ ЧТО УЖЕ РАБОТАЕТ (нашла в коде!) +`_handle_analysis_approved_flow` (stage_engine.py ~502-549): +- analyst пишет файл `docs/work-items//01-questions.md` → орк видит его → + `set_issue_needs_input(work_item_id)` + коммент «❓ Analyst нуждается в уточнении: ...». +- **Возврат:** человек отвечает в Plane → переводит issue обратно в **In Progress** → + `handle_status_start` видит существующий task + idle-агента → **relaunch analyst** (он + читает свежие комменты). Механизм статус-driven. + +### ❌ ЧЕГО НЕТ / зазоры +1. **Needs Input ТОЛЬКО у analyst — ПОДТВЕРЖДЕНО ГРЕПОМ (Слава поправил).** + `set_issue_needs_input` вызывается РОВНО в ОДНОМ месте во всём src/: `stage_engine.py:538` + внутри `_handle_analysis_approved_flow`, у которой ЖЁСТКИЙ гард: + `if not (agent == "analyst" and work_item_id): return`. Для любого НЕ-аналитика — просто return. + `01-questions.md` захардкожен под analysis (префикс 01-), читается только там. + => architect/developer/reviewer/tester **ФИЗИЧЕСКИ НЕ МОГУТ** уйти в Needs Input. Механизма НЕТ. + «И Т.Д.» = ПОСТРОИТЬ С НУЛЯ universal-механизм для всех стадий — ПОЛНОЦЕННАЯ фича (D6). +2. **Возврат завязан на `In Progress`** (`handle_status_start`). Если переименуем In Progress + → To Analyse, **сломается возврат из Needs Input** (webhook слушает in_progress UUID). + ⚠️ ГРАБЛИ: либо возврат тоже на To Analyse, либо оставить отдельный «Resume»-триггер. +3. **Нет матрицы разрешённых переходов** (вопрос Славы про «только доступные значения»). + Plane не умеет (404 на transitions/). Нужен guard в `handle_issue_updated`. + +## 4. Доработки ядра (полный список с приоритетом) + +### P0 — без этого модель не работает +- **D1.** `plane_sync.py`: добавить ключи/маппинг `to_analyse, analysis, code_review, + awaiting_deploy, deploying, monitoring` в `_DEFAULT_STATES` + `_PLANE_NAME_TO_KEY` + + обновить `_STAGE_TO_STATE_KEY` (analysis→analysis, review→code_review, deploy→deploying). + Хелперы `set_issue_`. +- **D2.** `webhooks/plane.py` `handle_status_start`: триггер старта конвейера и возврата из + Needs Input привязать к **To Analyse** (новый вход), НЕ к In Progress. Учесть fork + «старт vs возврат» (как сейчас по active-job). +- **D3.** Phase A (`_handle_self_deploy_phase_a`): `set_issue_in_review` → `set_issue_awaiting_deploy`. +- **D4.** Phase B: при старте detached-деплоя ставить **Deploying**. +- **D5.** Phase C / post-deploy: health-OK → **Monitoring after Deploy** (не Done); окно + HEALTHY closed → **Done**; UNHEALTHY → **Blocked**. + +### P1 — то, что Слава просит «и т.д.» (расширение Needs Input) +- **D6 (ПОЛНОЦЕННАЯ ФИЧА, НЕ детект).** Universal «агент просит уточнение» для ВСЕХ стадий: + (1) **договор сигнала**: агент пишет verdict-маркер `status: NEEDS_INPUT` в своём + артефакте ИЛИ `NN-questions.md` — единый контракт для всех; + (2) **детект для каждого агента** в advance_stage: сейчас ветка жёстко analyst-only + (`if not (agent==analyst)`), надо вынести в общий pre-QG чек: NEEDS_INPUT → + set_issue_needs_input + коммент + Telegram, НЕ запускать QG/advance; + (3) **resume**: возврат relaunch'ит ТЕКУЩУЮ стадию. `handle_status_start` уже умеет + relaunch по current_stage (переиспользуемо), НО триггерится только через In Progress — + перепривязать на resume-триггер (D7). + **Детект-сторона + контракт сигнала — ПОЛНОСТЬЮ НОВЫЕ.** + правка промптов всех агентов + (научить писать NEEDS_INPUT когда застряли без ввода). +- **D7.** Чёткий «Resume» semantics: из Needs Input/Blocked человек возвращает задачу — какой + статус-триггер relaunch'ит текущую стадию? Предложение: отдельный вход **To Analyse** + только для старта; для resume — возврат в статус ТЕКУЩЕЙ стадии ИЛИ спец-триггер. РЕШИТЬ. + +### P2 — guard переходов (вопрос Славы про «только доступные») +- **D8.** `ALLOWED_TRANSITIONS` матрица (из нашей схемы) в коде. В `handle_issue_updated`: + входящий человеческий переход проверяется по матрице {current_stage/status → allowed[]}. + Недопустимый → revert статуса назад + коммент «Недопустимый переход. Разрешено: [...]». + Это server-side state-machine вместо UI-ограничения (Plane его не умеет). + +## 5. Грабли/риски (НЕ сломать прод) +- **R1.** Переименование In Progress ломает 3 вещи: старт конвейера, возврат из Needs Input, + и `_STAGE_TO_STATE_KEY[deploy]=in_progress`. Все три перепривязать атомарно. +- **R2.** `reconciler.py` Guard 2 пропускает blocked/needs_input — при новых статусах + проверить, что reconciler не «оживляет» Awaiting Deploy/Monitoring (это активные ожидания, + не зависшие). Возможно добавить их в skip-список. +- **R3.** fail-closed везде: нет статуса в проекте (enduro/fallback) → не падать, вести себя + как раньше (как сделали в ORCH-59 AC-7). +- **R4.** STAGE_TRANSITIONS НЕ менять (инвариант AC-9 из ORCH-59). Новые статусы — только + индикация + ручные set_issue_*, не новые стадии конвейера. +- **R5.** Идемпотентность: повторный webhook одного статуса → no-op (как Phase B marker). + +## 6. Объём → 2 work item (предложение) +- **WI-1 «Статусная модель + индикация»** (D1-D5): переименования/новые статусы, Phase A/B/C + ставят правильные статусы, Monitoring-этап перед Done. Средний размер, хирургично. +- **WI-2 «Needs Input для всех стадий + guard переходов»** (D6-D8): расширение clarification- + механизма + server-side state-machine. Содержательный, можно отдельным PR после WI-1. ++ Задача гигиены Plane (создать/переименовать статусы через API) — инфра-предусловие, делаю я. + +## 7. Открытые решения Славе +1. **Resume из Needs Input:** какой триггер relaunch'ит текущую стадию? (а) возврат в статус + текущей стадии; (б) отдельный «Resume»-статус; (в) To Analyse только для analysis, для + остальных — статус стадии. Влияет на D2/D7. +2. **Один большой WI или два** (модель + needs-input/guard)? +3. **Guard переходов сейчас или потом** (P2 можно отложить)?