architect(ET): auto-commit from architect run_id=306

This commit is contained in:
2026-06-07 14:01:19 +00:00
committed by Dev Agent
parent db83b89467
commit 2bdba532d5
7 changed files with 453 additions and 3 deletions

View File

@@ -91,6 +91,42 @@ sentinel-файлы (`<repos_dir>/.deploy-state-<repo>/<wi>/`), без мигр
Подробнее: [adr-0007](adr/adr-0007-executable-self-deploy.md), детально —
`docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md`.
### Post-deploy наблюдение прода + реакция на деградацию (ORCH-021 — design)
Конвейер заканчивался на `deploy → done` и **забывал про прод**: «успех» = health-check
в момент рестарта (~60с). Класс «зелёный деплой, красный прод» (прецедент ET-8 —
деградация через минуты под трафиком, health `200 ok`, фича сломана). ORCH-021 продлевает
ответственность **ЗА** `done`: для применимого репо после терминального перехода армится
наблюдение окна `post_deploy_window_s` (~15 мин) с интервалом `post_deploy_interval_s`;
деградация фиксируется по детерминированным порогам, при подтверждении — реакция.
Механизм — **reserved-agent job `post-deploy-monitor`** (калька `deploy-finalizer`, НЕ
стадия и НЕ daemon): арм в `advance_stage` в блоке `next_stage == "done"`
(`post_deploy.arm_monitor`, sentinel `armed` = идемпотентность); тик перехватывается в
`launcher.launch_job` ДО `_spawn``stage_engine.run_post_deploy_monitor` (один опрос →
append в `series` → классификация → перепостановка с задержкой ИЛИ реакция+артефакт+`done`).
Чистая логика — новый leaf-модуль `src/post_deploy.py` (never-raise): `post_deploy_applies`,
`probe_signals` (`/health` 200+`{"status":"ok"}` + доля 5xx на `/status`,`/queue`),
`classify` (HEALTHY|DEGRADED — главный предмет юнит-тестов), `decide_action`,
sentinel-state, `write_post_deploy_log`.
- **Пороги (BR-3):** `DEGRADED``≥ post_deploy_fail_threshold` ПОСЛЕДОВАТЕЛЬНЫХ провалов
health ИЛИ доля 5xx `> post_deploy_5xx_threshold`; одиночный глюк → HEALTHY (нет ложных
откатов).
- **Реакция:** self-hosting (`orchestrator`) — ВСЕГДА `ALERT_ONLY` (Telegram+Plane, ручной
approve; тик НИКОГДА не откатывает/рестартит прод-контейнер); не-self +
`post_deploy_auto_rollback=true` → хук `--rollback` (`0→ROLLBACK_OK`,
`1/2→ROLLBACK_FAILED`+алерт); дефолт → `ALERT_ONLY`.
- **Артефакт** `16-post-deploy-log.md` (YAML-frontmatter `post_deploy_status`/
`action_taken`/…) — машиночитаемо для петли уроков ORCH-8; best-effort.
- **Наблюдаемость** — блок `post_deploy` в `GET /queue` (образец `reconcile`).
- **Инварианты:** `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_deploy_status`, terminal-sync,
merge-gate, exit-коды хука (0/1/2), схема БД — НЕ меняются. Restart-safe (sentinel
`.post-deploy-state-<repo>/<wi>/` + jobs-очередь). Kill-switch
`post_deploy_monitor_enabled`, область `post_deploy_repos` (пусто → self-hosting).
Условность как ORCH-35/36/43/58.
Подробнее: [adr-0010](adr/adr-0010-post-deploy-monitor.md), детально —
`docs/work-items/ORCH-021/06-adr/ADR-001-post-deploy-monitor.md`.
### Свежесть артефакта BUILD-ONCE: провенанс staging-образа (ORCH-058 — реализовано)
BUILD-ONCE retag (ORCH-36) промоутит `SOURCE_IMAGE=orchestrator-orchestrator-staging` в прод
**без rebuild**, полагаясь на «staging-образ свеж и провалидирован». Этой гарантии нет:
@@ -197,7 +233,7 @@ never-raise на единицу работы; тишина при синхрон
|--------|------|----------|
| GET | `/health` | health check |
| GET | `/status` | активные задачи (stage != done) |
| GET | `/queue` | очередь: counts + max_concurrency + resilience + reconcile (ORCH-053) + последние jobs |
| GET | `/queue` | очередь: counts + max_concurrency + resilience + reconcile (ORCH-053) + post_deploy (ORCH-021) + последние jobs |
| POST | `/webhook/plane` | Plane webhook |
| POST | `/webhook/gitea` | Gitea webhook (push, PR, CI status) |
@@ -211,4 +247,4 @@ never-raise на единицу работы; тишина при синхрон
Схема БД, потоки данных, resilience-слой, детали Dockerfile — [internals.md](internals.md).
---
*Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой `deploy`, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, `docs/work-items/ORCH-060/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-060 (Guard 1 `developer_retry_count>=MAX_DEVELOPER_RETRIES` + Guard 2 `plane_sync.fetch_issue_state` Blocked/Needs-Input, флаг `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED`); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, `docs/work-items/ORCH-061/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled).*
*Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой `deploy`, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, `docs/work-items/ORCH-060/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-060 (Guard 1 `developer_retry_count>=MAX_DEVELOPER_RETRIES` + Guard 2 `plane_sync.fetch_issue_state` Blocked/Needs-Input, флаг `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED`); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, `docs/work-items/ORCH-061/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, `docs/work-items/ORCH-021/06-adr/ADR-001`) — design, ветка feature/ORCH-021-post-deploy-rollback (`arch:major-change`; при реализации обновлять также при изменении src/post_deploy.py, src/stage_engine.py арм/run_post_deploy_monitor, src/agents/launcher.py перехват, флаги post_deploy_*; артефакт 16-post-deploy-log.md).*

View File

@@ -15,11 +15,12 @@ Per-work-item решения живут в `docs/work-items/<id>/06-adr/ADR-NNN-
| adr-0007 | Исполняемый самодеплой стадии `deploy` (файл adr-0007-executable-self-deploy) | accepted | 2026-06-06 | ORCH-036 |
| adr-0008 | Провенанс staging-образа перед BUILD-ONCE retag | accepted | 2026-06-06 | ORCH-058 |
| adr-0009 | Толерантность staging-вердикта к инфраструктурным FAIL | accepted | 2026-06-07 | ORCH-061 |
| adr-0010 | Post-deploy мониторинг прода + реакция на деградацию | proposed | 2026-06-07 | ORCH-021 |
> ⚠️ Историческая коллизия: номер `0007` занят двумя файлами —
> `adr-0007-reconciler.md` (ORCH-053) и `adr-0007-executable-self-deploy.md`
> (ORCH-036). Оба accepted; для новых сквозных ADR использовать следующий
> свободный номер (текущий максимум — `0009`).
> свободный номер (текущий максимум — `0010`).
## Формат
**Контекст → Решение → Альтернативы → Последствия → Связи.** Статус: proposed / accepted / superseded.

View File

@@ -0,0 +1,85 @@
# adr-0010: Post-deploy мониторинг прода + реакция на деградацию
- **Статус:** proposed (design) — реализация в ветке `feature/ORCH-021-post-deploy-rollback`
- **Дата:** 2026-06-07
- **Задача:** ORCH-021
- **Метка:** `arch:major-change` (новая под-компонента + новый reserved-agent job-kind)
- **Детальный ADR:** `docs/work-items/ORCH-021/06-adr/ADR-001-post-deploy-monitor.md`
## Контекст
Конвейер заканчивается на `deploy → done`: `check_deploy_status` видит
`deploy_status: SUCCESS` → terminal-sync (Plane → Done, release merge-lease), и
оркестратор **забывает про прод**. «Успех» сегодня = health-check в момент рестарта
(~60с окно в `orchestrator-deploy-hook.sh`). Класс инцидентов «зелёный деплой, красный
прод» (прецедент **ET-8**): деградация проявляется через минуты под боевым трафиком,
health отвечает `200 ok`, фича сломана. Для self-hosting опасно вдвойне — сломанный
прод-орк (8500) обслуживает ВСЕ проекты из общего инстанса.
## Решение
Продлить ответственность конвейера **ЗА** `done`: после терминального перехода для
применимого репо армится пост-деплой наблюдение окна `post_deploy_window_s` (дефолт
~15 мин) с интервалом `post_deploy_interval_s`; деградация фиксируется по
**детерминированным порогам**, при подтверждении выполняется реакция.
**Механизм — reserved-agent job `post-deploy-monitor`** (калька `deploy-finalizer`,
ORCH-36), НЕ отдельная стадия и НЕ daemon-поток:
- **Арм:** в `stage_engine.advance_stage`, в блоке `next_stage == "done"`, при
`post_deploy.post_deploy_applies(repo)``post_deploy.arm_monitor(...)` (sentinel
`armed` = идемпотентность, первый job через `enqueue_job(available_at_delay_s=...)`).
- **Тик:** `launcher.launch_job` перехватывает `agent == "post-deploy-monitor"` ДО
`_spawn``stage_engine.run_post_deploy_monitor(job)`: один опрос сигналов, append в
персистентный `series`, классификация; HEALTHY и окно не истекло → перепостановка с
задержкой; иначе → реакция + артефакт + `mark_done`.
- **Чистая логика — новый leaf-модуль `src/post_deploy.py`** (never-raise, по образцу
`self_deploy.py`/`staging_verdict.py`): `post_deploy_applies`, `probe_signals`
(опрос `/health` + доля 5xx на `/status`,`/queue`), `classify` (HEALTHY|DEGRADED —
главный предмет юнит-тестов), `decide_action` (NONE|ROLLBACK|ALERT_ONLY с учётом
self-hosting), sentinel-state хелперы, `write_post_deploy_log`.
**Сигналы и пороги (детерминированно, AC-3…AC-6):** `DEGRADED``≥
post_deploy_fail_threshold` ПОСЛЕДОВАТЕЛЬНЫХ провалов health ИЛИ доля 5xx на окне `>
post_deploy_5xx_threshold`. Одиночный глюк < порога → HEALTHY (нет ложных откатов).
**Реакция (BR-4/BR-5):**
- **Self-hosting (`orchestrator`) — ВСЕГДА `ALERT_ONLY`:** громкий Telegram + Plane,
запрос ручного approve отката. Тик НИКОГДА не откатывает/рестартит прод-контейнер
(структурный инвариант). Откат прод-орка, если оператор решит, — только detached
host-процесс (`self_deploy.initiate_deploy`), вне тика (MVP).
- **Не-self + `post_deploy_auto_rollback=True`:** хук `--rollback` с прод-env; exit
`0 → ROLLBACK_OK`, `1/2 → ROLLBACK_FAILED` + громкий алерт.
- Дефолт (`auto_rollback=False`) → `ALERT_ONLY`.
**Артефакт `16-post-deploy-log.md`** (новый) с YAML-frontmatter (`post_deploy_status`,
`action_taken`, `window_s`, `checks_total/failed`) — машиночитаемо для петли уроков
ORCH-8; best-effort. **Наблюдаемость** — блок `post_deploy` в `GET /queue` (образец
`reconcile.status()`).
## Альтернативы
- **Daemon-watchdog (как reconciler)** — отклонён: per-task серия опросов в памяти не
restart-safe (а деплой орка = рестарт); restart-safe-вариант требует тех же sentinel,
reserved-agent проще и уже имеет проверенную jobs+sentinel машинерию.
- **Отдельная пост-deploy стадия + QG** — отклонён: меняет `STAGE_TRANSITIONS`/
`QG_CHECKS`, ломает семантику терминального `done`; наблюдение принципиально ПОСЛЕ
`done`.
- **Авто-rollback прод-орка из тика** — отклонён (self-hosting safety): групповой риск;
контейнер не откатит себя надёжно. Self → alert + ручной approve (как ORCH-54).
- **Колонка в `tasks`** — отклонён: миграция на проде; sentinel-файлы restart-safe
(как ORCH-36/53/58).
## Последствия
- Класс «зелёный деплой, красный прод» закрыт измеримыми порогами; деградация =
сигнал для ORCH-8.
- Реестры (`STAGE_TRANSITIONS`/`QG_CHECKS`), контракт `check_deploy_status`,
terminal-sync, merge-gate, exit-code-контракт хука, схема БД — **не меняются**.
- Дефолты безопасны: kill-switch on, auto-rollback off, self только alert.
- Ограничение: монитор self бежит внутри наблюдаемого прода — полностью wedged
контейнер = пропущенный тик/алерт (known MVP gap; внешний watchdog — follow-up).
- Self-hosting: тик не рестартит/не роняет прод-контейнер; kill-switch
`post_deploy_monitor_enabled` обязателен; поэтапный раскат через `post_deploy_repos`.
## Связи
adr-0007-executable-self-deploy (ORCH-36 — sentinel/detached-host/finalizer образец,
`map_exit_code_to_status`), adr-0007-reconciler (ORCH-53 — daemon/`status()` образец,
отклонён как основной механизм), adr-0006 (merge-gate — условность/флаги раската),
adr-0003 (staging-gate — образец условности), adr-0008 (provenance — `.deploy-prev-image`/
хук-откат). Прецедент ET-8. Будущее: ORCH-8 (петля уроков), ORCH-54 (полный авто).