# 02 — ТЗ: устранение петли deploy-staging → development при self-deploy

Work Item: **ORCH-061** · Тип: **BUG** · Приоритет: **P0** · Репо: `orchestrator`

> Это ТЗ фиксирует **требования и контракты**, которые должна удовлетворить
> реализация. Конкретный архитектурный механизм (направление (а), (б) или оба;
> где именно разместить логику) выбирает архитектор в ADR (`06-adr/`).
> ТЗ намеренно не предписывает дизайн, но задаёт инварианты и границы изменений.

---

## 1. Затронутые модули `src/` и артефакты

Прямо относящиеся к дефекту (для контекста; точечный набор правок — за архитектором):

| Файл | Роль в дефекте |
|------|----------------|
| `scripts/staging_check.py` | e2e-suite; C9a (branch) / C9b (analyst job) дают ложный FAIL в sandbox; exit-code управляет вердиктом deployer. |
| `src/qg/checks.py` → `check_staging_status`, `_parse_staging_status` | гейт ребра `deploy-staging→deploy`; читает `staging_status:` из `15-staging-log.md`. |
| `src/stage_engine.py` → `advance_stage`, `_handle_qg_failure_rollbacks` | откат `deploy-staging→development` при FAILED (ветка `agent=="deployer" and qg=="check_staging_status"`). |
| `src/agents/launcher.py` → `_handle_completion`/`_try_advance_stage` | пост-ран git-commit; лог «no changes to commit»; обработка deployer-стадий. |
| `src/self_deploy.py` | Phase A/B/C исполняемого self-deploy (контекст продвижения `deploy`). |
| `src/config.py` | место для kill-switch/настроек нового поведения (если потребуется). |
| `.openclaw/agents/deployer.md` | инструкция deployer о написании вердикта; обновить при смене контракта. |
| `docs/operations/STAGING_CHECK.md`, `docs/architecture/README.md`, `CHANGELOG.md` | golden-source документация (обновить в том же PR). |

## 2. Функциональные требования

### FR-1 — Нет петли на корректном self-deploy
Для self-hosting `orchestrator`, при корректном состоянии (реальный pipeline в
порядке, staging-стенд здоров), задача проходит `deploy-staging → deploy → done`
**без отката** `deploy-staging → development` и **без ручного вмешательства**.

### FR-2 — Ложный (инфраструктурный) FAIL не вызывает откат
Ложное падение `staging_check` в sandbox, вызванное **исключительно** отсутствием
sandbox-настроек (например, C9a/C9b при ненастроенных bot-токенах SANDBOX), не
приводит к `staging_status: FAILED` → откату. Должно быть реализовано одним из
способов (выбор — ADR):
- **(а)** sandbox-инфраструктура приведена в состояние, при котором C9a/C9b
  проходят честно (10/10); и/или
- **(б)** вердикт staging-гейта перестаёт зависеть от заведомо инфраструктурных
  (не пайплайновых) проверок — например, осознанный allowlist/threshold
  «известных sandbox-инфра» проверок, отделённый от реальных pipeline-проверок.

> Любой механизм по FR-2 **обязан** сохранить FR-4 (реальный провал ловится).

### FR-3 — «no changes» на action-стадии не есть недовыполнение
На стадиях деплоя (`deploy-staging`, `deploy`) для self-deploy отсутствие
git-изменений (`no changes to commit`) **не** трактуется как недовыполнение и
**не** приводит к откату/блокировке. Критерий успеха action-стадии = успешный
exit агента/хука + доказанный health/staging-вердикт, а **не** наличие нового
коммита.

### FR-4 — Реальный регресс по-прежнему откатывается (страховка цела)
- Настоящий провал реальных pipeline-проверок staging → `staging_status: FAILED`
  → откат `deploy-staging → development` (как сейчас).
- Настоящий провал прод-деплоя (`deploy_status: FAILED`, БАГ-8) → откат
  `deploy → development` (как сейчас).
- Ослабления страховки быть не должно: «зелёный по умолчанию» при недоступности
  проверок запрещён (fail-closed для реальных проверок сохраняется).

### FR-5 — Условность self-hosting сохранена
Изменения активны **только** для self-hosting пути
(`is_self_hosting_repo` / `self_deploy_applies`). Для прочих репозиториев
поведение `check_staging_status` (no-op N/A) и стадии деплоя — **без изменений**.

### FR-6 — Управляемость (kill-switch)
Любое новое поведение (толерантность к инфра-FAIL и/или отвязка от git-changes)
закрыто отдельным флагом конфигурации (по образцу `merge_gate_enabled`,
`image_freshness_enabled`, `self_deploy_enabled`), с безопасным дефолтом и
возможностью мгновенно вернуть прежнее поведение без передеплоя кода-логики.

### FR-7 — Наблюдаемость
Срабатывание нового поведения (например, «staging_check: проигнорирован
инфра-FAIL C9a/C9b» или «action-стадия: no-changes ожидаемо») логируется явной
строкой и при необходимости отражается в Plane-комментарии/Telegram, чтобы
оператор отличал «реальный зелёный» от «зелёного с допущением».

## 3. Изменения API

API эндпоинты (`/health`, `/status`, `/queue`, `/webhook/*`) — **без изменений**.
Допускается расширение снапшота `GET /queue` диагностическим полем (опционально,
по решению архитектора) — без удаления/переименования существующих ключей.

## 4. Изменения схемы БД

**Нет.** Схема (`events`, `tasks`, `agent_runs`, `jobs`) не меняется. Любое
restart-safe состояние (если потребуется) — через существующие паттерны
(sentinel-файлы / поля `jobs.task_content`), без миграций.

## 5. Контракты, которые НЕЛЬЗЯ менять

- `STAGE_TRANSITIONS` (порядок и состав стадий) и `get_previous_stage`.
- Состав/семантика `QG_CHECKS` как реестра; frontmatter-контракты
  `staging_status:` (`15-staging-log.md`) и `deploy_status:` (`14-deploy-log.md`) —
  читаются ТОЛЬКО из YAML-frontmatter, значения `SUCCESS|FAILED`.
- Откатные контракты БАГ-8 (`deploy→development`) и ORCH-35
  (`deploy-staging→development`) для **реальных** провалов.
- Контракт exit-code хука деплоя (`0/1/2`) и `map_exit_code_to_status`.
- Поведение для не-self-hosting репозиториев.

## 6. Требования к новым/изменённым QG checks

- Если выбран механизм толерантности (FR-2 вариант б), он реализуется **внутри**
  существующего пути `check_staging_status` / staging-вердикта (не новая стадия),
  по образцу условности ORCH-35; контракт «never-raise» сохраняется.
- Любая новая проверка/под-чек регистрируется в `QG_CHECKS` и покрывается
  снапшот-тестом реестра (`tests/test_qg_registry_snapshot.py`).

## 7. Требования к staging_check.py (если затрагивается)

- Если выбран механизм классификации проверок (FR-2 вариант б через suite),
  e2e-проверки, заведомо зависящие от sandbox-инфраструктуры (C9a/C9b и связанные),
  должны быть **отличимы** (по метке/категории) от реальных pipeline-проверок,
  чтобы вердикт и/или exit-code мог их учитывать осознанно. Прежний дефолтный
  режим (`stub`/`full-real`) и существующие проверки A/B сохраняются.
- Никакого «всегда 0»: реальный провал реальных проверок обязан давать ненулевой
  exit-code / FAIL-категорию.

## 8. Требования к pipeline-артефактам

- Стадия деплоя по-прежнему производит машинный вердикт-артефакт
  (`15-staging-log.md` / `14-deploy-log.md`) с корректным frontmatter.
- Артефакты, обновляемые по pipeline в этом PR: `docs/architecture/README.md`
  (раздел про staging-гейт/self-deploy — отметить ORCH-061),
  `docs/operations/STAGING_CHECK.md` (поведение C9a/C9b и/или sandbox-настройка),
  `CHANGELOG.md`, при изменении контракта — `.openclaw/agents/deployer.md`.
- ADR: `docs/work-items/ORCH-061/06-adr/ADR-001-*.md` (решение по направлению/механизму).

## 9. Нефункциональные требования

- **Безопасность self-hosting:** реализация НЕ перезапускает/не роняет прод 8500
  в рамках задачи; сборки/recreate — только staging (8501).
- **Идемпотентность / restart-safe:** новое поведение переживает рестарт инстанса.
- **never-raise:** дефект-исправляющая логика не должна пробрасывать исключения в
  `advance_stage` (по образцу merge-gate / image-freshness).
- **Обратная совместимость:** при выключенном флаге (FR-6) — прежнее поведение 1:1.
- **Тестируемость:** «чистая» вердикт-логика выделяется так, чтобы покрываться
  unit-тестами без live staging/docker.

## 10. Зависимости и связанные задачи

- ORCH-35 (условный staging-гейт, ADR-0003), ORCH-36 (исполняемый self-deploy,
  ADR-0007), ORCH-58 (провенанс staging-образа), ORCH-60 (skip escalated/Blocked).
- Блокирует: ORCH-54 (автономное внедрение).
