Files
wiki/tasks/orchestrator/STATUS_MODEL_DEEP_ANALYSIS.md
2026-06-08 00:00:01 +03:00

12 KiB
Raw Blame History

Глубокий аудит ядра орка под новую статусную модель

Дата: 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/<WI>/01-questions.md → орк видит егоset_issue_needs_input(work_item_id) + коммент « Analyst нуждается в уточнении: ...».
  • Возврат: человек отвечает в Plane → переводит issue обратно в In Progresshandle_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_<status>.
  • 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_reviewset_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 ОТМЕНЁН СЛАВОЙ (2026-06-07): Needs Input НЕ расширяем на других агентов!

Needs Input остаётся ТОЛЬКО у аналитика (как сейчас). D6/D7 НИЖЕ — ВЫЧЕРКНУТЫ, НЕ делаем.

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. Объём → work items (обновлено после отмены D6/D7)

  • WI-1 «Статусная модель + индикация» (D1-D5): переименования/новые статусы, Phase A/B/C ставят правильные статусы, Monitoring-этап перед Done. Средний размер, хирургично.
  • WI-2 «guard переходов» (D8 только): server-side state-machine ALLOWED_TRANSITIONS + revert недопустимого перехода. D6/D7 (universal Needs Input) — ОТМЕНЕНЫ Славой.
  • Задача гигиены Plane (создать/переименовать статусы через API) — инфра-предусловие, делаю я.

⚠️ НО: Needs Input ОСТАЁТСЯ в жизненном цикле (только analyst). Грабли R1 остаются: переименование In Progress → To Analyse ломает возврат analyst из Needs Input (сейчас возврат = In Progress relaunch). В WI-1 ОБЯЗАТЕЛЬНО перепривязать этот возврат на To Analyse (или оставить In Progress как resume-триггер).

7. Открытые решения Славе

  1. Resume из Needs Input: какой триггер relaunch'ит текущую стадию? (а) возврат в статус текущей стадии; (б) отдельный «Resume»-статус; (в) To Analyse только для analysis, для остальных — статус стадии. Влияет на D2/D7.
  2. Один большой WI или два (модель + needs-input/guard)?
  3. Guard переходов сейчас или потом (P2 можно отложить)?