130 lines
13 KiB
Markdown
130 lines
13 KiB
Markdown
---
|
||
work_item: ORCH-094
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-09
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-094 — устранение флаппа deploy-статусов у терминальной (done) задачи
|
||
|
||
Work Item: **ORCH-094** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ описывает **конкретные изменения к реализации**, выведенные из BRD и фактического кода ветки.
|
||
> Архитектурное обоснование (ГДЕ ставить гард: setter `plane_sync` vs caller `stage_engine` vs
|
||
> реконсилятор) — задача архитектора (`06-adr/`). Здесь — ЧТО должно выполняться и ГДЕ искать.
|
||
|
||
## 1. Сводка изменения
|
||
|
||
Задача с БД `stage=done` и 0 активных job'ов в Plane стабильно держит `Done`: нужно (а) закрыть
|
||
источник, который шлёт ей PATCH-и deploy-статусов (`Awaiting Deploy`/`Monitoring after Deploy`),
|
||
(б) сделать выставление любого **deploy-фазового** статуса **терминал-aware / идемпотентным** —
|
||
для задачи, чья БД-стадия терминальна (`done`/`cancelled`), любой sync/монитор/прямой вызов
|
||
сходится к `Done`, а не к промежуточному статусу, (в) гарантировать детерминированный конец
|
||
post-deploy-monitor с привязкой тиков к активному job'у (нет job → нет тиков), (г) добавить
|
||
наблюдаемость «кто/почему ставит deploy-статус».
|
||
|
||
Изменение **аддитивное, под обратимым флагом, never-raise**, в зоне self-hosting deploy/post-deploy/sync.
|
||
`STAGE_TRANSITIONS` / `QG_CHECKS` / machine-verdict ключи — **не трогаются**.
|
||
|
||
## 2. Задействованные модули / пути
|
||
|
||
| Путь | Действие | Зачем |
|
||
|------|----------|-------|
|
||
| `src/plane_sync.py` | изменить | Сеттеры `set_issue_awaiting_deploy` (~954), `set_issue_deploying` (~964), `set_issue_monitoring` (~974), `set_issue_done` (~913) — кандидат на единый терминал-aware гард (FR-2). Терминал-детект статуса (группа/UUID, ORCH-068) уже здесь. |
|
||
| `src/stage_engine.py` | изменить | Три писателя deploy-статуса: `advance_stage` стр. 404 (`set_issue_monitoring` на `deploy→done`), `_handle_self_deploy_phase_a` стр. 1218, `_handle_self_deploy_phase_b` стр. 1316. `run_post_deploy_monitor` (~1698–1850) — детерминированный конец, привязка к job. `arm_monitor`-вызов (~431). Логирование caller/причины (FR-4). |
|
||
| `src/post_deploy.py` | изменить (вероятно) | `arm_monitor` (~388–411), маркеры `armed`/`series`/`done`, `enqueue_job("post-deploy-monitor", …)` — гарантия отсутствия «зомби»-тиков и привязки к активному job (FR-3). |
|
||
| `src/reconciler.py` | изменить (вероятно) | F-2 опрашивает только `[to_analyse, approved, rejected]` (стр. ~387). Нет схождения «done-задача на deploy-статусе → Done». Добавить идемпотентное схождение/терминал-детект для deploy-статусов (FR-1/FR-2) ИЛИ убедиться, что гард в setter'е делает это излишним. |
|
||
| `src/config.py` | изменить | Kill-switch/флаг новой логики (FR-5). |
|
||
| `src/webhooks/plane.py` | прочитать (диагностика) | `handle_issue_updated` (~129–180): подтверждено, что для `Awaiting`/`Monitoring` логирует «no pipeline action» и не переотправляет — echo-loop исключён; править не требуется (если локализация не покажет иное). |
|
||
| `tests/test_*` | создать/изменить | Анти-регресс по FR-1…FR-5 (см. `04-test-plan.yaml`). |
|
||
| `CHANGELOG.md`, `docs/architecture/README.md`, `CLAUDE.md` | изменить | Документация = golden source; зафиксировать фикс + локализованный источник флаппа (BR-7). |
|
||
|
||
> **Трассировка маркеров (CLAUDE.md прав. 9):** перед правкой строк с маркерами `ORCH-066`/`ORCH-068`/
|
||
> `ORCH-086`/`ORCH-036`/`ORCH-059`/`ORCH-071`/`ORCH-088` прочитать их `06-adr/` и не сломать инвариант
|
||
> (особенно: deploy→done ставит `Monitoring`, монитор-close ставит `Done`; терминал-скип реконсилятора;
|
||
> post-deploy DEGRADED → freeze ORCH-088).
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — Источник флаппа локализован и устранён
|
||
Инструментально воспроизвести флапп на ORCH-061 (или эквивалентной терминальной задаче), определить
|
||
**фактического актора** (функция/путь под бот-токеном орка ИЛИ внешняя Plane-automation) и устранить
|
||
его так, чтобы терминальная задача не получала deploy-статус-PATCH-ей.
|
||
- Зацепки (BR diagnostics): единственные code-писатели — `stage_engine.py:404/1218/1316`; реконсилятор
|
||
F-1 done-skip есть, F-2 эти статусы не перебирает; live-overlay `notifications.py` — read-only.
|
||
- Если актор — внешняя Plane-automation (вне кода орка), это **фиксируется в ADR** (BR-7) и закрывается
|
||
буфером FR-2 (идемпотентное схождение к Done гасит маятник на стороне орка).
|
||
- Привязка: BR-1, BR-7.
|
||
|
||
### FR-2 — Терминал-aware идемпотентность выставления deploy-статуса
|
||
Любая попытка выставить **deploy-фазовый** статус (`Awaiting Deploy`/`Deploying`/`Monitoring after
|
||
Deploy`) для задачи, чья БД-стадия **терминальна** (`stage IN ('done','cancelled')`), должна вместо
|
||
этого привести Plane к `Done` (для `done`) либо к корректному терминалу (для `cancelled`) —
|
||
идемпотентно. Повторные вызовы не качают маятник: уже-`Done` → no-op.
|
||
- Гард — терминал-aware (по БД-стадии задачи, не по живому Plane-статусу), чтобы НЕ подавлять
|
||
легитимный `Monitoring` у реально деплоящейся (нетерминальной) задачи (BR-5/AC-4).
|
||
- Реализация-кандидат (решает архитектор): единая точка в setter'ах `plane_sync` (требует доступа к
|
||
БД-стадии по `work_item_id`) ИЛИ в caller'ах `stage_engine`/`reconciler`. ТЗ требует **результат**:
|
||
done-задача сходится к Done из любого пути.
|
||
- never-raise: невозможность определить стадию/сетевая ошибка → безопасная деградация (не флаппить).
|
||
- Привязка: BR-1, BR-2.
|
||
|
||
### FR-3 — Детерминированный конец post-deploy-monitor + привязка тиков к активному job
|
||
- Монитор завершается детерминированно: HEALTHY+исчерпание `post_deploy_budget` тиков → `set_issue_done`
|
||
+ маркер `done`; DEGRADED → штатный путь (Blocked/freeze ORCH-088); после завершения — **ни одного**
|
||
последующего статус-PATCH (маркер `done` — идемпотентный страж, ~стр. 1729).
|
||
- Тик монитора **обязан** проверять, что задача не терминальна и для неё есть основание тикать (нет
|
||
активного основания/job → тик no-op, новый тик не ставится в очередь). «Зомби»-тик (тик без
|
||
соответствующего активного job'а/при БД=done) → немедленный no-op без статус-PATCH.
|
||
- Гарантировать, что `arm_monitor` не может быть вызван/перезапущен для задачи, уже находящейся в `done`,
|
||
способом, который заново ставит `Monitoring` (повторный `deploy→done` re-drive).
|
||
- restart-safe: после рестарта контейнера нет воскрешения тиков для завершённого окна.
|
||
- Привязка: BR-3, BR-4, NFR-4.
|
||
|
||
### FR-4 — Наблюдаемость выставления deploy-статуса
|
||
Каждый вызов, выставляющий deploy-фазовый статус, логирует структурно: **work_item, caller
|
||
(функция/путь), целевой статус, причина/триггер, БД-стадия задачи на момент вызова**. Достаточно,
|
||
чтобы по логу однозначно определить «кто и почему» при будущем флаппе. Терминал-aware-подавление
|
||
(FR-2) тоже логируется (что подавили и почему).
|
||
- Привязка: BR-6, G4.
|
||
|
||
### FR-5 — Обратимость и совместимость
|
||
- Новая логика — под kill-switch/флагом в `config.py` (env-override); `False` → прежнее поведение
|
||
1:1 (нулевая регрессия).
|
||
- Условность self-hosting, как ORCH-035/036/043/088: для не-self репозиториев — no-op / прежнее
|
||
поведение.
|
||
- Привязка: NFR-3, BR-5.
|
||
|
||
## 4. Изменения API
|
||
|
||
Нет новых внешних эндпоинтов конвейера. Допустимо (на усмотрение архитектора) аддитивное read-only
|
||
поле наблюдаемости в `GET /queue` (напр. блок `post_deploy`/`deploy_status_guard` со счётчиками
|
||
подавлений), по образцу существующих блоков `serial_gate`/`reconcile`/`reaper`. Не обязательно.
|
||
|
||
## 5. Изменения схемы БД
|
||
|
||
Ожидается **без миграции схемы**: терминал-aware гард читает существующую `tasks.stage`; привязка
|
||
тиков к job — существующая таблица `jobs`; состояние монитора — существующие sentinel-файлы
|
||
(`post_deploy.py`). Если архитектор сочтёт необходимым durable-счётчик/маркер — строго аддитивно
|
||
(`_ensure_column`, по образцу ORCH-088/090), без изменения существующих колонок.
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
|
||
**Нет.** `QG_CHECKS` и `check_*` (включая `check_deploy_status`/`check_staging_status`) — **не
|
||
трогаются**; machine-verdict ключи (`deploy_status:`/`staging_status:`/…) — байт-в-байт. ORCH-094 —
|
||
фикс индикации/идемпотентности sync, не гейт.
|
||
|
||
## 7. Совместимость / регресс
|
||
|
||
- **Kill-switch** (FR-5): выключение → прежнее поведение 1:1.
|
||
- **Регресс деплоя (AC-4):** рабочий цикл 063-подобной задачи `Awaiting→Deploying→Monitoring→Done`
|
||
сохраняется — гард срабатывает строго на терминальной БД-стадии, нетерминальная задача проходит
|
||
как раньше.
|
||
- **Не-self репозитории:** условность self-hosting → нулевая регрессия (enduro-trails).
|
||
- **`STAGE_TRANSITIONS`/`QG_CHECKS`/machine-verdict/схема БД** — без изменений (или строго аддитивно).
|
||
- **never-raise / self-hosting безопасность:** не трогать `main`/force-push/прод-контейнер/детач-деплой.
|
||
- **Артефакты pipeline:** обновляются `CHANGELOG.md`, обзорные доки (`README.md`/`docs/architecture/
|
||
README.md`), `CLAUDE.md`; `06-adr/ADR-NNN-…` с локализованным источником флаппа (BR-7).
|