# 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 лишь авто-инициирует существующий путь деплоя.
