137 lines
15 KiB
Markdown
137 lines
15 KiB
Markdown
---
|
||
work_item: ORCH-123
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-16
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-123 — staging-runner execution strategy must not depend on Docker CLI inside the app container
|
||
|
||
Work Item: **ORCH-123** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ описывает **требования и ограничения к реализации**, выведенные из BRD и фактического кода.
|
||
> Архитектурное **решение** (какую стратегию исполнения выбрать: host-side ssh / Docker SDK поверх
|
||
> сокета / docker CLI в образе / выделенный hook-режим, + security-review) — задача архитектора
|
||
> (`06-adr/`), т.к. задача эскалирована в full-cycle (`01-brd.md` → `escalate: full-cycle`).
|
||
|
||
## 1. Сводка изменения
|
||
Восстановить работоспособную стратегию исполнения staging-сюиты для self-hosting `orchestrator` на
|
||
стадии `deploy-staging`, не завися от docker CLI, **отсутствующего внутри прод-контейнера**
|
||
(`Dockerfile:11` ставит `openssh-client git curl ca-certificates`, не docker; `docker.sock`
|
||
смонтирован — `docker-compose.yml:40` — но клиента нет). Сегодня `staging_runner.build_staging_command`
|
||
(`src/staging_runner.py:154`) формирует `["docker","exec","orchestrator-staging",…]` и запускает её
|
||
**изнутри** прод-контейнера через `proc_group` → `Popen` падает `FileNotFoundError` → ветка tool-error
|
||
→ инфра-DEFER×2 → fail-closed `FAILED` → откат `deploy-staging → development`. Требуется: (а) исполнять
|
||
сюиту так, чтобы она реально запускалась в проде (паттерн host-side, уже применённый прод-деплоем
|
||
ORCH-036 / `--build-staging`); (б) **различать** постоянный environment/tool-error от настоящего
|
||
код-фейла и не делать вводящего в заблуждение код-фейл-отката; (в) prod-like preflight + регресс;
|
||
(г) документировать границу исполнения.
|
||
|
||
## 2. Задействованные модули / пути
|
||
| Путь | Действие | Примечание |
|
||
|------|----------|-----------|
|
||
| `src/staging_runner.py` | **изменить** | `build_staging_command`/`run_staging_suite` — точка, где сюита запускается «изнутри контейнера» (корень дефекта, FR-1); классификация tool-error vs environment-дефект в `run_staging_gate`/`_handle_tool_error` (FR-2) |
|
||
| `src/self_deploy.py` | возможно переиспользовать | `build_deploy_command`/`initiate_deploy` — рабочий host-side ssh+setsid механизм (ORCH-036); кандидат на общий ssh-хелпер исполнения host-side команды (решает архитектор) |
|
||
| `scripts/orchestrator-deploy-hook.sh` | возможно изменить | `--build-staging` уже делает host-side `docker exec "$STAGING_CONTAINER" python3 staging_check.py` (строки 197/261) — прецедент/возможная точка выделенного staging-режима |
|
||
| `Dockerfile` | возможно изменить | строка 11 — если выбран вариант «docker CLI в образе» (тогда + security-обоснование) |
|
||
| `docker-compose.yml` | возможно изменить | строка 40 — `docker.sock` уже смонтирован; если выбран socket/SDK-вариант, зафиксировать права (`:ro` где возможно) |
|
||
| `src/proc_group.py` | возможно изменить | `run_in_process_group` уже корректно деградирует spawn-error в `ProcResult(returncode=None)` — кандидат на preflight «исполняемое существует» (FR-4) |
|
||
| `src/config.py` | возможно изменить | существующие `staging_runner_*`; при необходимости — флаг выбора/режима стратегии (FR-5), дефолт = боевое |
|
||
| `docs/operations/INFRA.md` | **изменить** | граница исполнения staging-сюиты относительно прод-контейнера (FR-6 / BR-5) |
|
||
| `docs/architecture/README.md` | **изменить** | описать стратегию исполнения staging-runner (FR-6 / BR-5) |
|
||
| `CHANGELOG.md`, `CLAUDE.md` | изменить | docs = golden source (CLAUDE.md §2); раздел ORCH-115 дополнить фиксом исполнения |
|
||
| `tests/test_orch115_staging_runner.py` / `tests/test_orch123_staging_runner_exec.py` | **создать/расширить** | регресс «docker CLI отсутствует» + классификация + preflight (`04-test-plan.yaml`) |
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — Работоспособная стратегия исполнения staging-сюиты в проде (BR-1)
|
||
- На стадии `deploy-staging` для self-hosting `orchestrator` staging-сюита (`staging_check.py` внутри
|
||
`orchestrator-staging`) **должна реально исполняться** в боевом окружении, **не завися** от наличия
|
||
бинаря `docker` внутри прод-контейнера `orchestrator`.
|
||
- Стратегия исполнения — **выбор архитектора** (ADR), из перечня BRD §2: host-side через
|
||
существующий ssh+setsid механизм (ORCH-036, `deploy_ssh_host=127.0.0.1`, ssh-ключ смонтирован,
|
||
`openssh-client` в образе) **либо** Docker SDK/`docker.sock` (уже смонтирован, + security-review)
|
||
**либо** docker CLI в образе **либо** выделенный режим хука. ТЗ **не** прескриптивно — фиксирует
|
||
лишь требуемый инвариант «сюита исполняется».
|
||
- Команда/контракт сюиты (`python3 staging_check.py --base-url http://localhost:<staging_port>
|
||
--mode stub`, host-specifics из config — ORCH-101) сохраняются; меняется **инициатор/канал**
|
||
запуска, не сама сюита.
|
||
|
||
### FR-2 — Environment/tool-error ≠ код-фейл (BR-2, BR-3)
|
||
- Невозможность исполнить сюиту по причине **окружения** (нет исполняемого `docker`/SDK недоступен/
|
||
стратегия неработоспособна) **не должна** завершаться откатом `deploy-staging → development` как
|
||
кодовой ошибкой и **не должна** инкрементировать developer-retry. Текущий терминальный путь
|
||
`_handle_tool_error` → `write_staging_log("FAILED") + _advance` (= откат) для **постоянного**
|
||
environment-дефекта вводит в заблуждение (см. BRD §1) и должен быть заменён на **отличимый
|
||
инфра/environment-исход** (хольд на `deploy-staging` + алерт оператору, по образцу
|
||
`merge_gate` infra-tolerance ORCH-110: задача остаётся на стадии, без developer-retry).
|
||
- **Анти-over-tolerance (BR-3):** если сюита **реально исполнилась** (получен exit-код) и упала
|
||
(exit≠0), исход — **прежний** откат `deploy-staging → development` + developer-retry (контракт
|
||
`staging_runner` D5 для «сюита исполнилась» сохраняется байт-в-байт).
|
||
- Различение «сюита исполнилась / постоянный environment-дефект / транзиентная инфра» —
|
||
детерминированная классификация (по образцу `merge_gate.classify_retest_failure`, ORCH-110 D2);
|
||
никаких догадок — постоянный environment-дефект (spawn-error «исполняемое не найдено») трактуется
|
||
как НЕ-транзиентный и НЕ как код-фейл.
|
||
|
||
### FR-3 — Анти-бессмысленный-ретрай (BR-2)
|
||
- При постоянном environment-дефекте бессмысленно крутить инфра-DEFER ×N (каждая попытка падает
|
||
идентично, жжёт время/слот) и затем ложно откатывать. Допустимо: немедленный отличимый
|
||
инфра-хольд+алерт (без отката, без developer-retry) **или** preflight, не дающий задаче войти в
|
||
ложный путь. Конкретику решает архитектор; инвариант — **не** оканчиваться код-фейл-откатом.
|
||
|
||
### FR-4 — Prod-like preflight (BR-4)
|
||
- Должен существовать механизм, ловящий «стратегия исполнения неработоспособна (нет исполняемого/
|
||
канал недоступен)» **до раската** — preflight на старте сервиса и/или в `scripts/staging_check.py`/
|
||
smoke (`scripts/staging_check.py` Block …) / в `should_intercept`/`applies`. Условие, проявившееся
|
||
в инциденте (нет бинаря `docker` там, где runner его зовёт), должно детектироваться **до** того,
|
||
как реальная задача ложно откатится.
|
||
- Реализационно preflight никак не должен трогать гейты/стадии; never-raise; область — self-hosting.
|
||
|
||
### FR-5 — Условность / kill-switch (BR-1, NFR-4, NFR-6)
|
||
- Поведение под существующим `staging_runner_enabled` (выключен → прежний LLM-деплойер через `_spawn`
|
||
байт-в-байт) + `staging_runner_repos` (область). При необходимости нового флага выбора/режима
|
||
стратегии — env `ORCH_*`, **дефолт = боевое** (паттерн ORCH-101); откат = выставить флаг(и) →
|
||
поведение до ORCH-123. `applies(repo)` (локально, без сети) проверяется первым.
|
||
|
||
### FR-6 — Документирование границы исполнения (BR-5)
|
||
- `docs/operations/INFRA.md` + `docs/architecture/README.md`: явно зафиксировать, что docker-операции
|
||
для self-hosting исполняются **host-side** (а не из app-контейнера, где нет docker CLI), какие
|
||
исполняемые/сокеты доступны прод-контейнеру (`openssh-client`/`git`/`curl`; `docker.sock` смонтирован,
|
||
CLI — нет), и как именно staging-runner запускает сюиту после фикса.
|
||
|
||
## 4. Изменения API
|
||
**Нет** обязательных. Существующий read-only блок `staging_runner` в `GET /queue` (ORCH-115)
|
||
**дополняется** различием environment/tool-error vs код-фейл и (опц.) статусом preflight; новых
|
||
управляющих эндпоинтов не требуется (на усмотрение архитектора — опц. read-only preflight-поле).
|
||
|
||
## 5. Изменения схемы БД
|
||
**Нет.** Restart-safe состояние (счётчик инфра-ретраев) уже ведётся маркером в `task_content`
|
||
(`_INFRA_RETRY_MARKER`, ORCH-115) — без миграции. Новой таблицы/колонки не требуется.
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
**Нет.** Это **не** Quality Gate и **не** стадия — это стратегия исполнения продюсера артефакта
|
||
`15-staging-log.md`. `STAGE_TRANSITIONS` / реестр `QG_CHECKS` / имена и семантика `check_*`
|
||
(`check_staging_status`/`_parse_staging_status`) / machine-verdict ключи (`staging_status:`/
|
||
`deploy_status:`/…) / схема БД — **байт-в-байт не тронуты** (NFR-1; зеркало инварианта ORCH-115 NFR-1).
|
||
|
||
## 7. Совместимость / регресс
|
||
- **Обратная совместимость:** `staging_runner_enabled=False` → стадию `deploy-staging` снова ведёт
|
||
LLM-`deployer` через `_spawn` **байт-в-байт**; для прочих репо `applies==False` → no-op (нулевая
|
||
регрессия enduro).
|
||
- **Контракт артефакта:** `15-staging-log.md` (`staging_status:` + 52c-схема, `author_agent:
|
||
staging-runner`/`model_used: n/a`) сохраняется; вердикт по-прежнему читается ТОЛЬКО из frontmatter.
|
||
- **Self-hosting инварианты (NFR-2):** не рестартить прод 8500, не `docker compose up orchestrator`/
|
||
`--build` прода, не force-push, не писать в `main`, не править `.env` — сохраняются (ORCH-115 BR-7).
|
||
- **Security (NFR-3):** любой socket/CLI-в-контейнере вариант проходит security-review в ADR; host-side
|
||
ssh-вариант переиспользует доверенный ORCH-036 механизм без расширения привилегий.
|
||
- **Бюджеты/инварианты:** тайм-аут сюиты не растёт сверх `staging_runner_timeout_s` (держит сквозной
|
||
reaper-инвариант ORCH-065/109/110); proc_group tree-kill (ORCH-110) сохраняется.
|
||
- **Артефакты pipeline:** обычные docs work item (`01..04` этой задачи; `06-adr/` на стадии
|
||
architecture после эскалации; `15-staging-log.md` при прогоне). Новых pipeline-артефактов задача не
|
||
вводит.
|
||
- **Трассировка (CLAUDE.md §9 / ORCH-078):** правки маркированных блоков — ORCH-115 (staging-runner,
|
||
прямой объект), ORCH-036 (self_deploy ssh), ORCH-110 (proc_group/infra-tolerance), ORCH-058
|
||
(`--build-staging`) — сверять с их `06-adr/` перед изменением; инварианты не ломать.
|