# 02 — Техническое задание (ТЗ) **Work Item:** ORCH-066 **Стадия:** analysis → (вход для architecture) **Автор:** Analyst > ТЗ фиксирует ТРЕБУЕМОЕ ПОВЕДЕНИЕ и затронутые точки кода. Конкретную архитектуру > резолвера (точные имена ключей/функций) финализирует архитектор в ADR. Ниже — > опорный контракт, согласованный с бизнес-запросом Owner. --- ## 1. Задействованные модули `src/` | Модуль | Роль в задаче | |--------|---------------| | `src/plane_sync.py` | **Ядро изменений (слой B):** реестр логических статусов (`_DEFAULT_STATES`), `_PLANE_NAME_TO_KEY`, маппинг стадия→статус (`_STAGE_TO_STATE_KEY`, `STAGE_VISIBILITY_STATE`), хелперы `set_issue_*`. | | `src/webhooks/plane.py` | Маршрутизация входящего статуса (`handle_issue_updated`): `To Analyse` → `handle_status_start` (старт **или** resume). | | `src/stage_engine.py` | Точки ручной простановки статуса: analyst-flow (`Analysis`/`Needs Input`/`In Review`), Phase A (`Awaiting Deploy`), Phase B (`Deploying`), Phase C → `Monitoring after Deploy`, post-deploy monitor → `Done`/`Blocked`. | | `src/reconciler.py` | F-2 запрос статусов (`To Analyse` в список), Guard 2 skip-list (активные ожидания). | | `src/stages.py` | **НЕ менять** (инвариант слоя A). Используется только для чтения переходов. | | `src/config.py` | (При необходимости) kill-switch для новой статусной модели — на усмотрение архитектора (см. §6). | --- ## 2. Изменения статусной модели (слой B) ### 2.1. Реестр логических статусов (`src/plane_sync.py`) Ввести новые **логические ключи** и их имена в `_PLANE_NAME_TO_KEY`: | Логический ключ | Plane name | Назначение | |-----------------|-----------|------------| | `to_analyse` | `To Analyse` | Вход-триггер (старт + resume аналитика). | | `analysis` | `Analysis` | Индикация стадии analysis (орк). | | `code_review` | `Code-Review` | Индикация стадии review (орк). Заменяет `review`. | | `awaiting_deploy` | `Awaiting Deploy` | Phase A approval-pending (орк). | | `deploying` | `Deploying` | Phase B прод-деплой идёт (орк). | | `monitoring` | `Monitoring after Deploy` | Phase C / post-deploy окно (орк). | Сохранить существующие: `backlog`, `todo`, `in_progress` (backward-compat), `needs_input`, `in_review`, `blocked`, `done`, `cancelled`, `architecture`, `development`, `testing`, `approved`, `rejected`. `Cancelled` уже присутствует в `_PLANE_NAME_TO_KEY`. ### 2.2. Fail-closed резолюция (КРИТИЧНО — BR-12) `get_project_states()` после резолва по API делает `setdefault(k, v)` из `_DEFAULT_STATES`. Чтобы отсутствие нового статуса в проекте (enduro / Plane down / частичная конфигурация) **не ломало** конвейер, новые логические ключи в `_DEFAULT_STATES` должны **алиаситься на существующие UUID** (degrade-to-current): | Новый ключ | Default-алиас (UUID) | Деградированное поведение | |------------|----------------------|---------------------------| | `to_analyse` | = `in_progress` | enduro/старый проект: `In Progress` по-прежнему триггерит старт/resume. | | `analysis` | = `in_progress` | analysis показывается как `In Progress` (как сейчас). | | `code_review` | = `review` | review показывается как `Review` (как сейчас). | | `awaiting_deploy` | = `in_review` | Phase A показывается как `In Review` (как сейчас). | | `deploying` | = `in_progress` | Phase B показывается как `In Progress` (как сейчас). | | `monitoring` | = `done` | Phase C показывается как `Done` (как сейчас); монитор затем держит Done / флипает Blocked. | > Эффект: если оператор НЕ создал новый статус — система работает строго как до ORCH-066 > (никаких падений, никаких 404 от Plane PATCH). Если создал — резолвится по имени и > используется новый UUID. Это ровно паттерн ORCH-059 AC-7. ### 2.3. Маппинг стадия → статус `src/plane_sync.py`: - `_STAGE_TO_STATE_KEY`: `analysis` → `analysis` (было `in_progress`); `review` → `code_review` (было `review`). `deploy` остаётся (управляется Phase A/B/C напрямую, не через `update_issue_state`). `created`/`architecture`/`development`/`testing`/`done` — без изменений. - `STAGE_VISIBILITY_STATE`: `review` → `code_review` (было `review`). Добавить `analysis` → `analysis`, если индикация analysis ставится через `set_issue_stage_state` (решает архитектор; альтернатива — отдельный хелпер `set_issue_analysis`). - Сохранить совместимость `STAGE_TO_STATE` / `PLANE_STATES` алиасов (импортируются тестами). ### 2.4. Точки простановки статуса | Место (файл:симв.) | Сейчас | Должно стать | |--------------------|--------|--------------| | `webhooks/plane.py` `handle_issue_updated` | `new_state == in_progress` → `handle_status_start` | `new_state == to_analyse` (с fail-closed: при алиасе совпадает с `in_progress`) → `handle_status_start` | | `webhooks/plane.py` `start_pipeline` (старт) | статус остаётся `In Progress` | при старте/enqueue analyst орк ставит `Analysis` | | `webhooks/plane.py` `handle_status_start` (resume из Needs Input) | relaunch на `In Progress`-триггере | relaunch на `To Analyse`-триггере; при relaunch орк ставит `Analysis`. Fork «старт vs resume» (по `get_task_by_plane_id` + `has_active_job_for_task`) — **сохранить как есть.** | | `stage_engine.py` `_handle_analysis_approved_flow` (artifacts ready) | `set_issue_in_review` | оставить `In Review` (BR-9: In Review только за approve-pending конвейера) ✔ без изменений | | `stage_engine.py` `_handle_analysis_approved_flow` (questions) | `set_issue_needs_input` | **без изменений** (BR-10) | | `stage_engine.py` `_handle_self_deploy_phase_a` | `set_issue_in_review` | `Awaiting Deploy` (`set_issue_awaiting_deploy` или аналог) | | `stage_engine.py` `_handle_self_deploy_phase_b` | (статус не меняет) | `Deploying` | | `stage_engine.py` advance `deploy → done` (terminal-sync, строка ~338) | `set_issue_done` для всех | **self-hosting:** `Monitoring after Deploy` (перед/вместо арма монитора); **не-self:** `Done` как сейчас | | `stage_engine.py` `run_post_deploy_monitor` (HEALTHY, окно закрыто) | пишет лог + коммент, статус Plane НЕ трогает (остаётся Done) | `Done` (явно) | | `stage_engine.py` `run_post_deploy_monitor` (DEGRADED) | пишет лог + alert | `Blocked` | > **Замечание по terminal-sync (важно для архитектора):** сейчас `advance_stage` на > `next_stage == "done"` вызывает `set_issue_done` безусловно (строка ~338), затем армит > post-deploy monitor для self-hosting (~361). Нужно развести: для репо, где > `post_deploy.post_deploy_applies(repo)` истинно (self-hosting) — ставить `Monitoring > after Deploy` вместо `Done`, и переложить простановку `Done`/`Blocked` на финал > монитора (`run_post_deploy_monitor`). Для прочих репо — `Done` как сейчас. ### 2.5. Новые хелперы `src/plane_sync.py` Добавить тонкие обёртки по образцу `set_issue_in_review` (резолв per-project UUID + `_set_issue_state_direct`), never-raise при отсутствии issue: - `set_issue_analysis(work_item_id, project_id=None)` - `set_issue_code_review(...)` (или через `set_issue_stage_state("review")`) - `set_issue_awaiting_deploy(...)` - `set_issue_deploying(...)` - `set_issue_monitoring(...)` (Точный набор/именование — на усмотрение архитектора; контракт: per-project резолв + fail-closed.) --- ## 3. Изменения reconciler (`src/reconciler.py`) - **F-2** `_reconcile_plane_project`: добавить `to_analyse` в список запрашиваемых статусов (`list_issues_by_state([... , to_analyse])`) и в `_reconcile_plane_issue` маршрутизировать `new_state == to_analyse` → `handle_status_start` (старт при `task is None`, resume при существующем task без active-job — логика уже в `handle_status_start`). Сохранить обработку `approved`/`rejected`. При fail-closed алиасе `to_analyse==in_progress` поведение не дублируется (один и тот же UUID). - **Guard 2** `_is_blocked_or_needs_input` (F-1 skip): расширить skip-множество активными ожиданиями — `awaiting_deploy`, `deploying`, `monitoring` — чтобы реконсилер НЕ «оживлял» их как зависшие (BR-13). Имя метода/семантику можно обобщить («human-or-active-wait»), флаг `reconcile_skip_blocked_enabled` продолжает управлять этим networked-чеком. > Примечание: F-1 и так не тронет Phase A (`check_deploy_status` red → silent), > Deploying (active finalizer job), Monitoring (стадия `done`). Guard 2 — явная > defense-in-depth по требованию Owner. --- ## 4. Изменения API / эндпоинтов **Нет** новых HTTP-эндпоинтов. `GET /queue` / `GET /status` — без изменений контракта (статусы Plane там не отражаются). Изменения только во внешней индикации Plane (PATCH issue state — существующий механизм). --- ## 5. Изменения схемы БД **Нет.** `tasks` не хранит Plane-статус (источник истины — стадия в БД + Plane API). Миграции не требуются. --- ## 6. Требования к новым QG checks **Нет.** `QG_CHECKS` не расширяется. Статусы — индикация, не управление (канон: машинные вердикты читаются из YAML-frontmatter артефактов, не из Plane-статуса). Опционально (на усмотрение архитектора): единый kill-switch новой статусной модели (env-флаг) для безопасного раската, по образцу `staging_infra_tolerance_enabled` / `reconcile_skip_blocked_enabled`. Не обязателен, т.к. fail-closed алиасинг (§2.2) уже даёт backward-compatible деградацию. --- ## 7. Артефакты pipeline, создаваемые/обновляемые - `06-adr/ADR-001-plane-status-model.md` — архитектор (решение по резолверу, алиасингу, разводке terminal-sync). - `07-infra-requirements.md` — архитектор (список Plane-статусов для ручного создания оператором + Plane API инструкция). - Документация (golden source, тот же PR): `CLAUDE.md` (секция статусной модели), `docs/architecture/README.md` (секция статусов рядом с ORCH-036/ORCH-021), `CHANGELOG.md`. --- ## 8. Инварианты (проверяемые) - `src/stages.py` `STAGE_TRANSITIONS` — байт-в-байт без изменений. - `QG_CHECKS`, `check_deploy_status`/`_parse_deploy_status`, exit-коды хука, merge-gate, схема БД, `Confirm Deploy`, механизм `Needs Input` — без изменений. - Все новые `set_issue_*` / резолв — never-raise (Plane down ⇒ degrade, не crash). - Поведение enduro (не-self) и его терминальный `Done` — без регресса.