analyst(ET): auto-commit from analyst run_id=518

This commit is contained in:
2026-06-09 23:01:19 +03:00
committed by orchestrator-deployer
parent 5e01df00eb
commit f36528705e
4 changed files with 475 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
---
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` (заполняет архитектор).

View File

@@ -0,0 +1,129 @@
---
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` (~16981850) — детерминированный конец, привязка к job. `arm_monitor`-вызов (~431). Логирование caller/причины (FR-4). |
| `src/post_deploy.py` | изменить (вероятно) | `arm_monitor` (~388411), маркеры `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` (~129180): подтверждено, что для `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).

View File

@@ -0,0 +1,94 @@
---
work_item: ORCH-094
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-09
model_used: claude-opus-4-8
---
# 03 — Критерии приёмки (Acceptance Criteria): ORCH-094 — флапп deploy-статусов у терминальной (done) задачи
Work Item: **ORCH-094** · Repo: **orchestrator** · Стадия: analysis
Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** (что
считается провалом). Reviewer/CI проверяет их буквально по файлам репозитория и/или прод-проверкой.
---
## AC-1 — Терминальная задача стабильно держит Done
**Условие:** задача с БД `stage=done` и 0 активных job'ов, выставленная в Plane=`Done`, наблюдается
≥10 минут (воспроизводящий тест на 061-подобной фикстуре и/или прод-проверка на ORCH-061).
- **PASS:** за окно наблюдения **ни одного** авто-перехода в `Awaiting Deploy`/`Monitoring after
Deploy`; статус остаётся `Done`. В тесте: после выставления `Done` ни один кодовый путь орка не
порождает PATCH deploy-статуса для этой задачи.
- **FAIL:** зафиксирован хотя бы один авто-переход done-задачи в `Awaiting`/`Monitoring`, либо флапп
продолжается.
---
## AC-2 — Идемпотентное схождение к Done для done-задачи
**Условие:** для задачи с БД `stage IN ('done','cancelled')` инициируется любой источник sync
(реконсилятор-тик, монитор-тик, прямой вызов setter'а deploy-статуса).
- **PASS:** результат — `Done` (для `done`) / корректный терминал (для `cancelled`); промежуточный
deploy-статус (`Awaiting`/`Deploying`/`Monitoring`) **не** выставляется; повторный вызов на
уже-`Done` — no-op (без PATCH-маятника). Подавление логируется (что/почему).
- **FAIL:** sync для done-задачи выставляет промежуточный deploy-статус, либо повторные вызовы
качают `Done ⟷ deploy-статус`.
---
## AC-3 — Детерминированный конец post-deploy-monitor, без «зомби»-тиков
**Условие:** post-deploy-monitor отрабатывает свой жизненный цикл (HEALTHY до исчерпания
`post_deploy_budget` тиков, либо DEGRADED).
- **PASS:** по достижении HEALTHY/N-тиков → `set_issue_done` + маркер `done`; **после завершения —
0 последующих статус-PATCH** для этой задачи (тест: монитор отработал → последующих
`set_issue_*`-вызовов нет). Тик при БД=`done`/отсутствии активного основания → немедленный no-op
без PATCH. После рестарта контейнера тики завершённого окна не воскресают.
- **FAIL:** после завершения монитора фиксируется хотя бы один статус-PATCH; либо «зомби»-тик
выполняется без активного job'а/при БД=done и шлёт статус; либо `arm_monitor` повторно ставит
`Monitoring` уже-done-задаче.
---
## AC-4 — Регресс: рабочий deploy-цикл реально деплоящейся задачи
**Условие:** реально деплоящаяся 063-подобная задача проходит self-deploy.
- **PASS:** последовательность статусов `Awaiting Deploy → Deploying → Monitoring after Deploy →
Done` работает в точности как до ORCH-094; Phase A/B/C, merge-gate, post-deploy HEALTHY-окно,
freeze-на-DEGRADED (ORCH-088) — не затронуты; терминал-aware гард (FR-2) **не** подавляет
легитимный `Monitoring` у нетерминальной задачи.
- **FAIL:** любой шаг рабочего deploy-цикла нетерминальной задачи изменён/подавлён/сломан.
---
## AC-5 — Наблюдаемость, безопасность, документация, зелёный pytest
**Условие:** реализация завершена.
- **PASS:**
- Лог однозначно показывает **кто (функция/путь) и почему** ставит deploy-статус, и что/почему
подавлено терминал-aware гардом (FR-4).
- never-raise: новая логика не бросает исключений в горячих путях; сетевая ошибка Plane → безопасная
деградация. Не трогаются `main`/force-push/прод-контейнер/детач-деплой.
- `STAGE_TRANSITIONS` / `QG_CHECKS` / machine-verdict ключи — без изменений; новая логика под
kill-switch (`False` → прежнее поведение 1:1); не-self репозитории не затронуты.
- `pytest tests/ -q` зелёный; добавлены тесты по `04-test-plan.yaml`.
- **Источник флаппа задокументирован** (что это было) в `06-adr/ADR-NNN-…` + `CHANGELOG.md`;
обновлены `CLAUDE.md` / `docs/architecture/README.md` (golden source).
- **FAIL:** нет логирования caller/причины; new-логика бросает/без флага; тронуты гейты/verdict-ключи;
красный pytest; источник флаппа не задокументирован; затронут не-self репозиторий.
---
## Сводная матрица AC ↔ FR/BR
| AC | Покрывает |
|----|-----------|
| AC-1 | BR-1 / FR-1 |
| AC-2 | BR-2 / FR-2 |
| AC-3 | BR-3, BR-4 / FR-3 |
| AC-4 | BR-5 / FR-2, FR-5 |
| AC-5 | BR-6, BR-7 / FR-4, FR-5, NFR-1…NFR-5 |

View File

@@ -0,0 +1,97 @@
work_item: ORCH-094
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-09
model_used: claude-opus-4-8
title: "Тест-план: терминальная (done) задача не флаппит deploy-статусы, держит Done"
framework: pytest
scope: >
Покрывается: терминал-aware идемпотентность выставления deploy-статусов
(Awaiting/Deploying/Monitoring) для задач с БД stage=done/cancelled; детерминированный
конец post-deploy-monitor и отсутствие "зомби"-тиков/статус-PATCH после завершения;
привязка тиков монитора к активному job; наблюдаемость caller/причины; обратимость
(kill-switch) и регресс рабочего deploy-цикла реально деплоящейся задачи.
Вне покрытия: изменение STAGE_TRANSITIONS/QG_CHECKS/machine-verdict ключей (не трогаются);
поведение не-self репозиториев (проверяется как нулевая регрессия). Точный актор флаппа на
проде локализуется инструментально (developer) и фиксируется в ADR — на это отдельный
smoke/прод-чек, не unit.
notes: >
Полный регресс tests/ должен оставаться зелёным (pytest tests/ -q). Setter'ы Plane и сетевые
вызовы — мокать (никаких реальных PATCH в Plane из тестов). Регресс = любой авто-переход
done-задачи в deploy-статус, либо статус-PATCH после завершения монитора, либо подавление
легитимного Monitoring у нетерминальной задачи. Тесты опираются на фикстуры задач со стадиями
done/deploy и на счётчики вызовов set_issue_* (через мок).
tests:
- id: TC-01
type: unit
description: "deploy-статус для задачи с БД stage=done сходится к Done: попытка set_issue_monitoring/awaiting/deploying при terminal-стадии выставляет Done (или no-op, если уже Done), а не промежуточный статус."
module: tests/test_deploy_status_terminal_guard.py
expected: PASS
- id: TC-02
type: unit
description: "Идемпотентность: повторный вызов терминал-aware setter'а на уже-Done задаче — no-op (0 дополнительных PATCH), маятник Done<->deploy-статус не возникает."
module: tests/test_deploy_status_terminal_guard.py
expected: PASS
- id: TC-03
type: unit
description: "Нетерминальная задача (stage=deploy) не подавляется: set_issue_monitoring/awaiting/deploying проходит штатно (регресс AC-4)."
module: tests/test_deploy_status_terminal_guard.py
expected: PASS
- id: TC-04
type: unit
description: "Kill-switch выключен -> прежнее поведение 1:1 (терминал-aware гард не вмешивается); включён -> done-задача сходится к Done."
module: tests/test_deploy_status_terminal_guard.py
expected: PASS
- id: TC-05
type: unit
description: "never-raise: при невозможности определить БД-стадию / сетевой ошибке Plane сеттер деградирует безопасно (не флаппит, не бросает исключение)."
module: tests/test_deploy_status_terminal_guard.py
expected: PASS
- id: TC-06
type: unit
description: "post-deploy-monitor: после завершения окна (HEALTHY, ticks==budget -> set_issue_done + маркер done) последующих статус-PATCH для задачи нет (0 set_issue_* вызовов)."
module: tests/test_post_deploy_monitor_termination.py
expected: PASS
- id: TC-07
type: unit
description: "post-deploy-monitor тик при БД stage=done / отсутствии активного основания -> немедленный no-op без статус-PATCH и без постановки следующего тика ('зомби'-тик исключён)."
module: tests/test_post_deploy_monitor_termination.py
expected: PASS
- id: TC-08
type: unit
description: "arm_monitor не пере-арминг для задачи, уже находящейся в done: повторный deploy->done re-drive не выставляет Monitoring заново (маркер armed/done -> no-op)."
module: tests/test_post_deploy_monitor_termination.py
expected: PASS
- id: TC-09
type: unit
description: "Наблюдаемость: каждый вызов выставления deploy-статуса логирует work_item, caller/путь, целевой статус, причину и БД-стадию; подавление терминал-aware гардом тоже логируется."
module: tests/test_deploy_status_observability.py
expected: PASS
- id: TC-10
type: integration
description: "Реконсилятор/sync для задачи с БД=done и Plane=Monitoring приводит к Done идемпотентно (а не к промежуточному deploy-статусу) и не качает маятник на повторных тиках."
module: tests/test_reconciler_done_deploy_convergence.py
expected: PASS
- id: TC-11
type: integration
description: "Регресс рабочего deploy-цикла: реально деплоящаяся (нетерминальная) 063-подобная задача проходит Awaiting -> Deploying -> Monitoring -> Done без подавления (Phase A/B/C, post-deploy HEALTHY-окно как раньше)."
module: tests/test_self_deploy_cycle_regression.py
expected: PASS
- id: TC-12
type: integration
description: "Не-self репозиторий (enduro-подобный): нулевая регрессия — терминал-aware гард deploy-статусов инертен (условность self-hosting)."
module: tests/test_deploy_status_terminal_guard.py
expected: PASS