# 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`
