# BRD — ORCH-058: Self-deploy retag берёт устаревший staging-образ (риск тихого регресса) Work Item ID: ORCH-058 Тип: bug / техдолг инфраструктуры self-deploy Источник: `docs/history/LESSONS_ORCH-036-selfdeploy.md` п.4 (самый опасный из 4 багов bootstrap ORCH-36) ## 1. Контекст ORCH-36 сделал стадию `deploy` исполняемой для self-hosting репозитория `orchestrator`: - Phase B (`src/self_deploy.py::build_deploy_command`) запускает детачед host-хук `scripts/orchestrator-deploy-hook.sh` с параметром `SOURCE_IMAGE=orchestrator-orchestrator-staging`. - Хук (шаг **2b**, BUILD-ONCE, ORCH-36 BR-6) делает `docker tag $SOURCE_IMAGE → $TARGET_IMAGE` **без `docker build`** — «прод получает ровно тот артефакт, что прошёл staging». Дизайн-предпосылка BUILD-ONCE: **staging-образ свеж и провалидирован**. На практике этой гарантии НЕТ. ## 2. Проблема (корень) Конвейер **нигде не пересобирает** образ `orchestrator-orchestrator-staging` из текущего кода (HEAD `main` / провалидированной ветки): - Стадия `deploy-staging` запускает только `scripts/staging_check.py` (e2e-проверка) против **уже работающего** контейнера `orchestrator-staging` (8501) — что бы в нём ни крутилось. Сборка staging-образа — ручная операция (STAGING.md / ORCH-34), вне конвейера. - Между «образ собран» и «retag в прод» нет провенанс-связи с провалидированным коммитом. Следствие (инцидент ORCH-36): staging-образ не пересобрали из нового `main` → `staging_check` прошёл против СТАРОГО кода → BUILD-ONCE retag промоутнул СТАРЫЙ образ в прод. Деплой «зелёный» (`result=0`, health ok), но прод молча откатился на код 2-дневной давности: пропал `deploy-finalizer` → задача не закрылась → бесконечная петля Phase B. ## 3. Почему это критично > Это **самый опасный** из четырёх багов self-deploy: он **не падает**, а **тихо откатывает > прод**. Зелёный гейт = ложный позитив. Орк обслуживает все проекты (enduro-trails) из одного > прод-инстанса → тихий регресс инструмента = групповой инцидент для всех проектов. Текущая защита (staging-гейт, merge-gate, health-check хука) НЕ ловит этот класс: все они зелёные, потому что проверяют не тот артефакт, что уезжает в прод. ## 4. Бизнес-цель Гарантировать инвариант: **в прод никогда не промоутится образ, не собранный из провалидированного для данной задачи коммита; при невозможности это доказать — деплой fail-fast (вердикт FAILED → откат на development), а не «тихо зелёный»**. ## 5. Объём (scope) В объёме: - Привязка артефакта (staging-образ → прод-retag) к провалидированному коммиту. - Fail-fast при рассинхроне образа и кода (никаких тихих промоутов устаревшего). - Условность как ORCH-35/36/43: реально только для `orchestrator`; прочие репо — no-op / прежнее поведение. - Контракт never-raise и fail-closed (на сомнении — не деплоить). Вне объёма: - Полный авто-approve прод-деплоя (ORCH-54). - Изменение exit-code-контракта хука (0/1/2) и реестров `STAGE_TRANSITIONS` / `QG_CHECKS` как набора стадий. - Миграции схемы БД. - Деплой/рестарт **прод**-контейнера `orchestrator` (8500) в рамках задачи. ## 6. Бизнес-требования (BR) - **BR-1.** Образ, который BUILD-ONCE retag промоутит в прод, ДОЛЖЕН соответствовать коду, провалидированному стадией `deploy-staging` для данной задачи (тот же git-коммит). - **BR-2.** Если соответствие НЕ доказуемо (staging-образ собран не из провалидированного коммита, либо провенанс невозможно прочесть) — деплой ОБЯЗАН fail-fast: вердикт `FAILED`, штатный откат на `development` (контракт БАГ-8), без рестарта прода. - **BR-3.** `staging_check.py` (e2e-валидация) ДОЛЖЕН прогоняться против артефакта, соответствующего тому же провалидированному коммиту, что уедет в прод (нельзя валидировать один образ, а катить другой). - **BR-4.** Поведение условно: реально для `orchestrator`; для прочих репозиториев — no-op / без регрессий прежнего синхронного деплоя. - **BR-5.** Выбранное решение НЕ должно приводить к вечной блокировке деплоя (если механизм свежести отсутствует — нужен путь, который доводит до зелёного, а не fail-fast'ит навсегда). - **BR-6.** Контракт never-raise: сбой проверки свежести/провенанса не должен валить stage_engine; на любом сомнении — fail-closed (трактуем как несоответствие). - **BR-7.** Документация-голден-сорс: INFRA / DEPLOY_HOOK / STAGING / architecture README + CHANGELOG обновляются в том же PR; решение оформляется ADR. ## 7. Связанные материалы - `docs/history/LESSONS_ORCH-036-selfdeploy.md` (п.4 — корень) - `docs/architecture/adr/adr-0007-executable-self-deploy.md`, `docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md` - `src/self_deploy.py`, `scripts/orchestrator-deploy-hook.sh`, `src/config.py` - `docs/operations/STAGING.md`, `docs/operations/DEPLOY_HOOK.md`, `docs/operations/INFRA.md`