211 lines
16 KiB
Markdown
211 lines
16 KiB
Markdown
# 02 — ТЗ: Авто-режим по лейблам (autoApprove + autoDeploy)
|
||
|
||
**Work Item:** ORCH-089
|
||
**Базируется на BRD:** `01-brd.md`
|
||
|
||
> ТЗ фиксирует **что** должно измениться (модули, API, БД, гейты, артефакты,
|
||
> флаги) и предметные требования к поведению. Архитектурное **как** (структура
|
||
> leaf-модуля, стратегия кэша лейблов, точная сигнатура хелперов) — за архитектором
|
||
> (ADR `06-adr/`). ТЗ задаёт границы, которые архитектура обязана соблюсти.
|
||
|
||
---
|
||
|
||
## 1. Обзор изменения
|
||
|
||
Ввести два независимых авто-прохода человеческих гейтов, управляемых лейблами Plane
|
||
на конкретной задаче:
|
||
|
||
- **autoApprove** — авто-проход гейта BRD (`analysis`: `In Review → Approved`).
|
||
- **autoDeploy** — авто-проход гейта прод-деплоя (`deploy`: `Confirm Deploy` → Phase B).
|
||
|
||
Принцип врезки — аддитивно, по образцу условных под-гейтов (ORCH-035/043/058/088):
|
||
leaf-модуль чистой логики (never-raise) + точечные врезки в существующие точки
|
||
принятия решений + флаги в `config.py`. **`STAGE_TRANSITIONS` и реестр `QG_CHECKS`
|
||
НЕ трогаются** — авто-режим переиспользует уже существующие переходы и гейты, лишь
|
||
устраняя ожидание человеческого сигнала.
|
||
|
||
---
|
||
|
||
## 2. Задействованные модули `src/`
|
||
|
||
| Модуль | Роль изменения |
|
||
|--------|----------------|
|
||
| `src/labels.py` (**новый, leaf**) | Чистая логика авто-режима: `auto_approve_applies(repo)`, `auto_deploy_applies(repo)`, `has_label(work_item_id, label, project_id) -> bool/None`, нормализация имён лейблов, fail-safe. never-raise. |
|
||
| `src/plane_sync.py` | Новая функция чтения лейблов issue из Plane API (`fetch_issue_labels`) + резолв карты лейблов проекта (имя↔uuid, с кэшем по образцу `get_project_states`). Новый сеттер статуса `set_issue_approved` (PATCH в Approved-UUID) для индикации авто-аппрува. |
|
||
| `src/stage_engine.py` | Врезка autoApprove в `_handle_analysis_approved_flow` (ветка `files_ok`, после `set_issue_in_review`). Врезка autoDeploy в `_handle_self_deploy_phase_a` (после advance на `deploy`, перед возвратом). |
|
||
| `src/config.py` | Новые флаги `auto_label_enabled`, `auto_approve_label`, `auto_deploy_label`, `auto_label_repos` (+ при необходимости TTL кэша лейблов). |
|
||
| `src/main.py` (`GET /queue`) | Аддитивный блок наблюдаемости `auto_labels` (опционально: счётчики авто-проходов). |
|
||
| `src/webhooks/plane.py` | (Опц.) если payload вебхука несёт `labels` — использовать как быстрый путь; иначе чтение через `fetch_issue_labels`. Источник истины лейблов — Plane API (надёжнее payload). |
|
||
|
||
> Точные имена функций/флагов — ориентир; финальные сигнатуры закрепляет ADR.
|
||
> Обязательное требование: вся логика определения авто-режима — **never-raise** и
|
||
> при ошибке возвращает «нет авто» (fail-safe к ручному гейту, BR-6).
|
||
|
||
---
|
||
|
||
## 3. Точки врезки (insertion points) — предметные требования
|
||
|
||
### 3.1 Гейт BRD (autoApprove)
|
||
|
||
**Текущее поведение** (`src/stage_engine.py::_handle_analysis_approved_flow`, ветка
|
||
`files_ok`, ~стр. 584–599):
|
||
1. `set_issue_in_review(work_item_id)`;
|
||
2. Plane-коммент «артефакты готовы»;
|
||
3. `notify_approve_requested(task_id)`;
|
||
4. `return` — **без advance**; ждёт ручного Approved через webhook
|
||
(`handle_verdict(approved=True)` → `_try_advance_stage` → advance на `architecture`).
|
||
|
||
**Требуемое поведение при `autoApprove`:**
|
||
- ПОСЛЕ установки `In Review` и коммента (для прозрачности и клока) проверить лейбл
|
||
`autoApprove` на задаче;
|
||
- если лейбл есть И `auto_approve_applies(repo)` И `auto_label_enabled`:
|
||
- выставить Plane-статус **Approved** (индикация; `set_issue_approved`);
|
||
- залогировать авто-проход (`label autoApprove → BRD auto-approved`);
|
||
- отправить Telegram + Plane-коммент о факте авто-аппрува (BR-7, прозрачность);
|
||
- инициировать тот же advance, что делает ручной Approved, т.е. переход
|
||
`analysis → architecture` через штатный путь (`advance_stage(..., finished_agent=None)`
|
||
с `qg_passed`/`approved-via-status`-семантикой), чтобы:
|
||
- закрылся клок `brd_review_ended_at` (`mark_brd_review_ended`),
|
||
- выполнились все стандартные пост-переходные эффекты (карточка, plane-sync);
|
||
- если лейбла нет / ошибка чтения → **прежнее поведение** (return, ждём человека).
|
||
|
||
> Требование к реализации advance: НЕ дублировать переходную логику. Авто-аппрув
|
||
> обязан идти через тот же advance-путь, что и человеческий Approved (единый источник
|
||
> истины перехода). Защита от двойного advance/гонки с реальным webhook — идемпотентность
|
||
> (advance применяется один раз; повторный сигнал — no-op).
|
||
|
||
### 3.2 Гейт прод-деплоя (autoDeploy)
|
||
|
||
**Текущее поведение** (`src/stage_engine.py::_handle_self_deploy_phase_a`, ~стр. 1151):
|
||
вызывается на ребре `deploy-staging → deploy` ПОСЛЕ зелёных под-гейтов (security →
|
||
merge-gate → image-freshness → staging). Делает:
|
||
1. `update_task_stage(task_id, "deploy")` + `notify_stage_change`;
|
||
2. `set_issue_awaiting_deploy`;
|
||
3. `write_marker(APPROVE_REQUESTED)`;
|
||
4. Plane-коммент + Telegram «смените статус на Confirm Deploy»;
|
||
5. `return` — ждёт ручного `Confirm Deploy` → `handle_confirm_deploy` →
|
||
`advance_stage(confirm_deploy=True)` → `_handle_self_deploy_phase_b` (initiate_deploy).
|
||
|
||
**Требуемое поведение при `autoDeploy`:**
|
||
- Все тех-гейты ребра `deploy-staging → deploy` уже зелёные к моменту Phase A
|
||
(иначе сюда не дошли бы) — это структурно гарантирует BR-5 (авто не деплоит сломанное);
|
||
- ПОСЛЕ advance на `deploy` (шаг 1) проверить лейбл `autoDeploy`;
|
||
- если лейбл есть И `auto_deploy_applies(repo)` И `auto_label_enabled`:
|
||
- залогировать авто-проход (`label autoDeploy → prod deploy auto-confirmed`);
|
||
- Telegram + Plane-коммент о факте авто-деплоя (BR-7);
|
||
- инициировать Phase B тем же путём, что ручной Confirm Deploy
|
||
(`_handle_self_deploy_phase_b(...)`), сохранив идемпотентность (маркер `INITIATED`);
|
||
- индикация статуса — `Deploying` (ставит уже сам Phase B);
|
||
- если лейбла нет / ошибка → **прежнее поведение** (Phase A ждёт человека).
|
||
|
||
> Требование: НЕ обходить и НЕ дублировать тех-гейты. autoDeploy запускается строго
|
||
> в точке, где Phase A уже прошёл все под-гейты. Phase C (finalizer) + merge-verify +
|
||
> regression-guard + post-deploy monitor остаются неизменны и продолжают верифицировать
|
||
> результат деплоя.
|
||
|
||
---
|
||
|
||
## 4. Изменения Plane API
|
||
|
||
Новых endpoint оркестратора (FastAPI) — **нет**. Изменяется только клиентское
|
||
взаимодействие с Plane API v1:
|
||
|
||
| Действие | Endpoint Plane | Назначение |
|
||
|----------|----------------|------------|
|
||
| Чтение лейблов issue | `GET /workspaces/{slug}/projects/{pid}/issues/{issue_id}/` → поле `labels` (список uuid) | Узнать, какие лейблы навешены на задачу. |
|
||
| Карта лейблов проекта | `GET /workspaces/{slug}/projects/{pid}/labels/` → `[{id,name}]` | Сопоставить uuid лейбла ↔ имя (`autoApprove`/`autoDeploy`). Кэшировать (TTL, образец `get_project_states`/`plane_states_ttl_s`). |
|
||
| Установка Approved | `PATCH /…/issues/{issue_id}/` `{"state": <approved_uuid>}` | Индикация авто-аппрува BRD (`set_issue_approved`, через `get_project_states(...)["approved"]`). |
|
||
| (Инфра) создание лейблов | `POST /…/labels/` | Однократно создать `autoApprove` и `autoDeploy` в проекте ORCH (см. `07-infra-requirements.md`). |
|
||
|
||
**Требования:**
|
||
- Все GET/PATCH к Plane — через существующие `PLANE_HEADERS`/`_resolve_project_id`,
|
||
таймаут как у соседей (10с), never-raise.
|
||
- Сопоставление лейбла — по **имени** (нормализованному: регистр/пробелы), резолвенному
|
||
из карты лейблов проекта; неоднозначность/нет совпадения → «нет лейбла» (fail-safe).
|
||
- Чтение лейблов НЕ должно блокировать конвейер при недоступности Plane: ошибка →
|
||
«нет авто» → ручной гейт.
|
||
|
||
---
|
||
|
||
## 5. Изменения схемы БД
|
||
|
||
**Не требуются.** Авто-режим — stateless относительно БД:
|
||
- источник истины лейблов — Plane (читается на гейте);
|
||
- идемпотентность авто-деплоя обеспечена существующими sentinel-маркерами
|
||
(`APPROVE_REQUESTED`/`INITIATED`, ORCH-036), а не новой колонкой;
|
||
- клок `brd_review_*` уже существует (ORCH-087).
|
||
|
||
Если архитектура решит кэшировать факт авто-прохода для наблюдаемости — допускается
|
||
**аддитивная** идемпотентная миграция (`_ensure_column`, образец ORCH-065 `jobs.pid`),
|
||
но это не требование ТЗ (предпочтительно без миграции, restart-safe через Plane/маркеры).
|
||
|
||
---
|
||
|
||
## 6. Новые QG checks
|
||
|
||
**Не вводятся.** Авто-режим не добавляет проверок качества — он устраняет ожидание
|
||
человеческого сигнала на существующих гейтах. Реестр `QG_CHECKS` и
|
||
`check_analysis_approved` / `check_deploy_status` / `check_staging_status` —
|
||
**без изменений**. (Это сознательно: добавление QG-чека усложнило бы матрицу и нарушило
|
||
инвариант «STAGE_TRANSITIONS/QG_CHECKS не трогаются», характерный для соседних под-гейтов.)
|
||
|
||
---
|
||
|
||
## 7. Конфигурация (флаги `src/config.py`)
|
||
|
||
По образцу ORCH-035/043/059/088 (kill-switch + CSV scope):
|
||
|
||
| Флаг | Тип / дефолт | Назначение |
|
||
|------|--------------|------------|
|
||
| `auto_label_enabled` | `bool = True` (env `ORCH_AUTO_LABEL_ENABLED`) | Глобальный kill-switch обоих авто-режимов. `False` → строго прежнее поведение (оба гейта ручные). |
|
||
| `auto_approve_label` | `str = "autoApprove"` | Имя лейбла гейта BRD. |
|
||
| `auto_deploy_label` | `str = "autoDeploy"` | Имя лейбла гейта деплоя. |
|
||
| `auto_label_repos` | `str = ""` (CSV) | Scope. Пусто → self-hosting only (как ORCH-035/043), либо «все репо» — выбор фиксирует ADR; дефолт безопасный (self-hosting). |
|
||
| `auto_label_states_ttl_s` | `int` (опц.) | TTL кэша карты лейблов проекта (образец `plane_states_ttl_s`). |
|
||
|
||
**Требование:** при `auto_label_enabled=False` — нулевая регрессия (ни одного нового
|
||
сетевого вызова на гейтах, поведение 1:1 как до ORCH-089).
|
||
|
||
---
|
||
|
||
## 8. Наблюдаемость
|
||
|
||
- Каждый авто-проход → `logger.info` с причиной (label X → действие).
|
||
- Telegram-уведомление + обновление live-карточки (ORCH-042/087, never-raise).
|
||
- Plane-коммент в задаче (автор — `analyst` для BRD, `deployer` для деплоя — по образцу
|
||
существующих комментов гейтов).
|
||
- (Опц.) аддитивный блок `auto_labels` в `GET /queue` (enabled, label-имена, scope,
|
||
счётчики `auto_approved_total`/`auto_deployed_total`) — образец блоков
|
||
`reconcile`/`serial_gate`.
|
||
|
||
---
|
||
|
||
## 9. Артефакты pipeline
|
||
|
||
Новых обязательных артефактов задачи **нет**. Авто-проходы отражаются в:
|
||
- Plane-комментах и Telegram/карточке (прозрачность, BR-7);
|
||
- существующих логах деплоя (`14-deploy-log.md` для autoDeploy — пишется Phase C как сейчас).
|
||
|
||
Документация golden-source (обязательно в этом же PR):
|
||
- `CLAUDE.md` — раздел про авто-режим по лейблам (флаги, инвариант «снимает только
|
||
человеческое решение»);
|
||
- `docs/architecture/README.md` — описание врезок autoApprove/autoDeploy + флаги;
|
||
- `06-adr/ADR-001-*.md` — архитектурное решение (точки врезки, fail-safe, чтение лейблов);
|
||
- `07-infra-requirements.md` — создание лейблов `autoApprove`/`autoDeploy` в Plane ORCH;
|
||
- `CHANGELOG.md` — `## [Unreleased]`.
|
||
|
||
---
|
||
|
||
## 10. Инварианты (что НЕ должно измениться)
|
||
|
||
- `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, `check_analysis_approved`,
|
||
`check_deploy_status`, `check_staging_status` — без изменений.
|
||
- Все технические под-гейты (security/merge-gate/image-freshness/merge-verify/
|
||
regression-guard/post-deploy) — без изменений; авто-режим их не обходит.
|
||
- Ручной путь (без лейблов) — 1:1 как сейчас.
|
||
- Схема БД, exit-коды deploy-хука, merge-lease, sentinel-маркеры self-deploy — без изменений.
|
||
- never-raise: ни одна ошибка авто-режима не роняет конвейер и не пропускает гейт
|
||
ошибочно (fail-safe к ручному).
|
||
- Self-hosting: авто-режим НЕ рестартит/не роняет прод вне штатного Phase B (который
|
||
и так есть); autoDeploy лишь авто-инициирует существующий путь деплоя.
|