Files
orchestrator/docs/work-items/ORCH-094/01-brd.md

156 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
work_item: ORCH-094
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-09
model_used: claude-opus-4-8
---
# 01 — BRD (бизнес-требования): ORCH-094 — терминальная (done) задача флаппит deploy-статусы в Plane (Awaiting↔Monitoring), не держит Done
Work Item: **ORCH-094** · Repo: **orchestrator** · Стадия: analysis
## 1. Бизнес-контекст и проблема
**Тип:** BUG — рассинхрон БД↔Plane / «зомби»-цикл post-deploy-статуса (self-hosting).
**Симптом (верифицирован живьём 09.06 на ORCH-061):**
Задача ORCH-061 в БД оркестратора = `done` с 07.06 (task 47; фикс задеплоен в прод; конвейер её
не трогает — 0 активных job'ов). При этом карточка задачи в **Plane не держит Done**: непрерывно
флаппит `Monitoring after Deploy ⟷ Awaiting Deploy` парами (туда-обратно за ~2 сек), каждые
несколько минут. Накоплено 273 активности. Доходило до абсурда: 09.06 14:56 встала в `Done`
15:48 её выдернуло обратно `Done → Awaiting Deploy`. Воспроизводится детерминированно: ручной
sync 061→Done (PATCH 200, 16:47) → через ~60 сек снова `Done → Awaiting Deploy → Monitoring`
(16:48). Само **не затихает**.
**Установленные факты (по логам/БД прода + чтение кода ветки):**
- **Сам оркестратор не инициирует переходы из своих штатных стадийных обработчиков для done-задачи.**
В момент флаппа лог орка показывает только **входящие** webhook-и Plane
(`issue … updated to state … (Awaiting Deploy) → no pipeline action`, затем `(Monitoring) →
no pipeline action`). Обработчик `webhooks/plane.py::handle_issue_updated` для статусов
Awaiting/Monitoring логирует «no pipeline action» и **сам статус не переотправляет** (echo-loop
обработчика исключён).
- **Actor всех 273 переходов** = `daf4d3f4-55df-4016-9095-0cf9ddd8fd28` — бот-актор оркестратора
(тот же токен, под которым орк делает гигиену доски / sync). То есть PATCH-и шлёт **что-то под
токеном орка**, не привязанное к активной task/job в БД.
- В БД орка **нет активного post-deploy-monitor** для task 47 (pdm активен только у текущей
063/task 74). `orchestrator-staging` (8501) — не источник (task 061 в его БД отсутствует).
- В коде ветки **единственные три писателя** deploy-статусов — `src/stage_engine.py`:
`set_issue_monitoring` (строка 404, на переходе `deploy → done` для self-hosting),
`set_issue_awaiting_deploy` (строка 1218, Phase A), `set_issue_deploying` (строка 1316, Phase B).
Все три — **внутри стадийных обработчиков** (`advance_stage` / `_handle_self_deploy_phase_*`),
ни один не сидит в фоновом цикле, независимом от таблицы `jobs`.
- `notifications.py::_live_plane_branch_override` **только читает** живой Plane-статус (для рендера
карточки) — писателем не является.
- Реконсилятор: F-1 пропускает задачи со `stage in ('done','cancelled')` (terminal-skip ORCH-086);
F-2 опрашивает issue **только** в статусах `[to_analyse, approved, rejected]` — статусы
`Monitoring`/`Awaiting` он не перебирает. **Механизма «привести done-задачу, застрявшую на
deploy-статусе, обратно к Done» (идемпотентного схождения) — нет.**
**Боль:** карточка вводит наблюдателя в заблуждение («задача деплоится», хотя она в проде и done),
шумит активностью (273 события на одной задаче), **вечно жжёт API-вызовы Plane** флаппом и
маскирует реальное состояние доски. Конвейер технически не нарушен (задача в проде), поэтому
приоритет **MEDIUM**, но дефект бессрочный и самовоспроизводящийся.
**Родственные задачи:** ORCH-091 (врущие/застывшие статусы карточки), ORCH-068/086 (терминал-скип
как защита инвариантов). ORCH-094 распространяет идею терминал-скипа на deploy-статусы и закрывает
источник флаппа.
## 2. Объём (scope)
### В объёме
- **G1 — устранить источник** PATCH-ей deploy-статуса на задачу, у которой в БД `stage=done` и нет
активного job'а. Терминальная (done) задача в Plane должна стабильно держать `Done` и не получать
`Awaiting`/`Monitoring`.
- **G2 — идемпотентность sync/setter'ов:** если БД=`done`, любой sync/монитор/реконсилятор/прямой
вызов приводит Plane к `Done` (не к промежуточному deploy-статусу) — терминал-скип/схождение,
распространённые на статусы `Monitoring`/`Awaiting` (как ORCH-068/086 для других статусов).
- **G3 — детерминированный конец post-deploy-monitor:** монитор завершается чётко (HEALTHY / N тиков
→ Done) и не оставляет «зомби»-таймеров, переживающих завершение задачи/рестарт; тики монитора
привязаны к активному job'у в БД (нет job → нет тиков, нет статус-PATCH).
- **G4 — наблюдаемость:** лог однозначно показывает, **кто и почему** ставит deploy-статус
(caller/функция + причина), для будущей диагностики таких флаппов.
- Инструментальная локализация фактического актора флаппа на проде (воспроизведение на 061) и его
документирование (что это было) — в рамках выполнения задачи (developer/architect).
### Вне объёма
- Изменение конвейера стадий (`STAGE_TRANSITIONS`), состава `QG_CHECKS`, семантики machine-verdict
ключей (`deploy_status:`/`staging_status:`/…) — **не трогать**.
- Изменение рабочего deploy-цикла для **реально деплоящейся** задачи (Phase A→B→C, post-deploy
HEALTHY-окно) — поведение должно сохраниться 1:1 (регресс, AC-4).
- Поведение для не-self-hosting репозиториев (enduro-trails) — нулевая регрессия.
- Архитектурное решение «где именно поставить гард» (на уровне setter'а в `plane_sync` vs на уровне
вызывающего в `stage_engine` vs реконсилятор) — определяет **архитектор** в `06-adr/`.
## 3. Заинтересованные стороны
- **Заказчик/репортёр:** Слава (владелец) — обнаружил на ORCH-061 09.06.
- **Затрагивает:** всех наблюдателей доски Plane проекта ORCH (ложная индикация); лимиты Plane API
(вечный флапп жжёт вызовы под общим бот-токеном).
- **Принимает результат:** Owner / CI на финальной стадии конвейера.
- **Особый риск:** self-hosting — правка идёт в инструмент, обслуживающий прод всех проектов из
общего инстанса; рабочий deploy-цикл нельзя сломать.
## 4. Бизнес-требования (BR)
- **BR-1** — Терминальная задача (БД `stage=done`, 0 активных job'ов), выставленная в Plane=`Done`,
**остаётся `Done`** и не получает авто-переходов в `Awaiting Deploy`/`Monitoring after Deploy`.
- **BR-2** — Любой источник синхронизации (реконсилятор, монитор, прямой вызов setter'а deploy-статуса)
для задачи с БД=`done` приводит Plane к **`Done` идемпотентно**, а не к промежуточному deploy-статусу;
повторные срабатывания не качают маятник.
- **BR-3** — Post-deploy-monitor имеет **детерминированный конец** (HEALTHY / исчерпание N тиков → Done,
или DEGRADED → Blocked+freeze) и после завершения **не производит ни одного** последующего
статус-PATCH для этой задачи; не оставляет таймера/состояния, переживающего завершение или рестарт.
- **BR-4** — Тики post-deploy-monitor **привязаны к активному job'у** в таблице `jobs`: нет активного
job'а для задачи → нет тиков → нет статус-PATCH. «Зомби»-монитор (тики без соответствующего активного
job'а) исключён.
- **BR-5** — Для **реально деплоящейся** задачи (063-подобной) deploy-окно
`Awaiting → Deploying → Monitoring → Done` работает в точности как раньше (нет регресса).
- **BR-6** — Каждый вызов, выставляющий deploy-статус, оставляет в логе однозначную запись **кто
(функция/путь) и почему** ставит статус (наблюдаемость для будущей диагностики флаппов).
- **BR-7** — Фактический источник флаппа на проде локализован и **задокументирован** (что это было)
в `06-adr/` и/или `CHANGELOG.md`.
## 5. Нефункциональные требования (NFR)
- **NFR-1 — never-raise:** вся новая логика (гарды/терминал-скип/идемпотентность) не бросает
исключений в горячих путях; сетевая ошибка Plane при сверке статуса → безопасная деградация
(не флаппить и не падать), а не блокировка конвейера всех проектов.
- **NFR-2 — self-hosting безопасность:** не перезапускать/не ронять прод-контейнер; не трогать
`main`/force-push/прод-деплой; правка не меняет рабочий критический путь self-deploy.
- **NFR-3 — обратимость:** поведение под kill-switch (или иным обратимым флагом) — при выключении
возврат к прежнему поведению; нулевая регрессия для не-self репозиториев.
- **NFR-4 — restart-safe:** состояние монитора/гардов корректно после рестарта контейнера (нет
«воскрешения» тиков для уже завершённой задачи).
- **NFR-5 — `pytest tests/ -q` зелёный**; `STAGE_TRANSITIONS` / `QG_CHECKS` / machine-verdict ключи /
схема БД (если без миграции) — без изменений или строго аддитивно.
## 6. Допущения и ограничения
- Допущение: статусы `Monitoring after Deploy` / `Awaiting Deploy` существуют в Plane-проекте ORCH
как реальные статусы (иначе alias-fallback маппит их на базовые UUID — это часть диагностики
терминал-детекта).
- Допущение: бот-токен орка (`daf4d3f4-…`) — единственный актор переходов; внешняя Plane-automation
под другим токеном считается отдельной гипотезой и проверяется при локализации (H-внешнее).
- Ограничение: установленные факты выше **не изобретать** — они верифицированы на проде; точный
актор флаппа требует инструментального воспроизведения (фикс — после локализации).
- Ограничение: правка строго в зоне self-hosting deploy/post-deploy/sync; конвейер и гейты неизменны.
## 7. Критерии успеха
Терминальная задача стабильно держит `Done` ≥10 мин без авто-переходов (AC-1); любой sync для done
идемпотентно сходится к `Done` (AC-2); post-deploy-monitor завершается детерминированно и не
оставляет тиков/таймеров (AC-3); рабочий deploy-цикл 063-подобной задачи не регрессирует (AC-4);
never-raise + зелёный pytest + источник флаппа задокументирован (AC-5). Детальные PASS/FAIL — в
`03-acceptance-criteria.md`.
## 8. Риски
- Гард терминал-скипа поставлен слишком широко → подавит легитимный `Monitoring` у реально
деплоящейся задачи (регресс AC-4). Митигировать тонкой привязкой к БД `stage=done` + активность job.
- Фактический актор флаппа окажется внешней Plane-automation (вне кода орка) → код-фикс не закроет
G1 полностью; нужно зафиксировать в ADR и, при необходимости, защититься идемпотентным схождением
к Done (BR-2) как буфером.
- Детали — `10-tech-risks.md` (заполняет архитектор).