Files
orchestrator/docs/work-items/ORCH-123/02-trz.md
claude-bot 3865b14a1c
All checks were successful
CI / test (push) Successful in 1m12s
analyst(ET): auto-commit from analyst run_id=747
2026-06-16 07:55:43 +03:00

137 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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/` перед изменением; инварианты не ломать.