21 KiB
work_item, stage, author_agent, status, created_at, model_used, escalate
| work_item | stage | author_agent | status | created_at | model_used | escalate |
|---|---|---|---|---|---|---|
| ORCH-123 | analysis | analyst | ready-for-review | 2026-06-16 | claude-opus-4-8 | full-cycle |
01 — BRD / Bug-report: ORCH-123 — staging-runner assumes Docker CLI inside the orchestrator container and false-fails deploy-staging
Work Item: ORCH-123 · Repo: orchestrator · Стадия: analysis · Трек: Bug → эскалация в full-cycle
⚠️
escalate: full-cycle(ADR-001 D5 ORCH-019). Метка задачи —Bug, но по сути это архитектурный + safety-critical (self-hosting) дефект: требуется решение о стратегии исполнения staging-сюиты из прод-окружения (host-side ssh / Docker SDK поверх смонтированного сокета / docker CLI в образе / выделенный hook-режим), и любой вариант с доступом кdocker.sock/CLI из контейнера требует явного security-review (доступ к сокету = root-эквивалент на хосте). Это не «однострочная» правка кода — нужен ADR. Поэтому выпускается полный analysis-пакет (а не облегчённый bug-пакет). Оператор снимает багфикс-трек:POST /bug-fast-track/escalate?work_item=ORCH-123→ задача пойдёт через стадиюarchitecture(architect выпустит ADR для стратегии исполнения staging-runner).
1. Бизнес-контекст и проблема
Симптом (наблюдаемое — установленный факт из прод-логов)
Задача ORCH-116 (поверх раската ORCH-115) дошла до стадии deploy-staging с зелёными
reviewer и tester, но детерминированный staging-runner упал ещё до запуска staging-сюиты.
Запись из прод-логов:
proc_group: spawn error for [docker, exec, orchestrator-staging, python3,
/repos/orchestrator/scripts/staging_check.py, ...]: [Errno 2] No such file or directory: docker
Далее runner отработал свой инфра-DEFER дважды, исчерпал staging_runner_infra_max_retries=2,
записал 15-staging-log.md с staging_status: FAILED и stage_engine откатил ORCH-116 на
development — как будто это дефект кода/тестов задачи, чем оно не является.
Причина симптома (установленный факт — верифицировано по коду и инфраструктуре)
ORCH-115 (src/staging_runner.py) исполняет staging-сюиту командой
docker exec orchestrator-staging python3 .../staging_check.py … изнутри прод-контейнера
orchestrator через proc_group.run_in_process_group. Прод-контейнер не содержит docker CLI:
Dockerfile:11ставитopenssh-client git curl ca-certificates— бинаряdockerнет;python:3.12-slimего тоже не содержит. Поэтомуsubprocess.Popen(["docker", …])падаетFileNotFoundError: [Errno 2] No such file or directory: docker.docker-compose.yml:40монтирует/var/run/docker.sock(rw) в сервисorchestratorи добавляетgroup_add: ${ORCH_DOCKER_GID}. То есть сокет в контейнере есть, а CLI-бинаря, который бы им воспользовался, — нет. (Это важное уточнение корня: проблема не в доступе к Docker, а в отсутствии исполняемого клиента в образе.)
Цепочка отказа в коде:
build_staging_command() (staging_runner.py:154) → ["docker","exec",…] → run_staging_suite() →
proc_group.run_in_process_group → Popen ⇒ OSError ⇒ ProcResult(returncode=None, timed_out=False)
⇒ в run_staging_gate (staging_runner.py:528) suite_ran = (returncode is not None) and (not timed_out) = False ⇒ ветка tool-error ⇒ _handle_tool_error (staging_runner.py:415):
инфра-DEFER ×2 → бюджет исчерпан → write_staging_log(..., "FAILED", tool_error=True) +
_advance(...) ⇒ существующий откат deploy-staging → development.
Корневая классификационная ошибка
Двухуровневый исход ORCH-115 (D5) различает только «сюита исполнилась» vs «сюита не исполнилась
(транзиентная инфра)». Отсутствие docker CLI — это детерминированный, постоянный дефект
окружения, а не транзиентная икота: ретраи бессмысленны (каждая попытка падает идентично), а
терминальный исход — откат на development с расходом developer-retry — вводит в заблуждение
(винит код/разработчика задачи за environment-проблему раннера). Так до фикса любая self-hosting
задача, доходящая до deploy-staging, обречена на ложный откат.
Локализация (анализ — куда смотреть архитектору/разработчику)
Установленный факт о топологии исполнения (CLAUDE.md / docs/operations/INFRA.md / код):
прод-деплой (ORCH-036, src/self_deploy.py) уже решил ровно эту проблему и НЕ запускает docker
изнутри контейнера. Он исполняет хост-хук на хосте через ssh + setsid (detached):
build_deploy_command (self_deploy.py:220) формирует ssh <user>@<host> 'setsid bash -c "… bash <hook> --deploy …"', где deploy_ssh_host=127.0.0.1, ssh-ключ смонтирован, openssh-client стоит в
образе (Dockerfile:11). Хост-хук scripts/orchestrator-deploy-hook.sh уже выполняет docker compose … / docker tag / docker exec "$STAGING_CONTAINER" python3 staging_check.py
(--build-staging, строки 197/261) — на хосте, где docker CLI есть.
Вывод: ORCH-115 при замене LLM-деплойера детерминированным раннером отклонился от уже
установленного паттерна «docker-операции исполняются host-side, не внутри app-контейнера». Дефект —
в стратегии исполнения staging_runner (где/как запускается staging-сюита), а не в гейте
check_staging_status и не в контракте 15-staging-log.md. Поэтому фикс должен восстановить
работоспособную стратегию исполнения staging-сюиты в проде, не завися от недоступного внутри
контейнера docker CLI, и перестать классифицировать постоянный environment-дефект как код-фейл.
🔎 Точка для проверки на стадии architecture (не факт, требует верификации): как staging-сюита исполнялась до ORCH-115 LLM-деплойером — действительно через
docker execиз контейнера (тогда путь всегда был сломан и LLM-гейт был «бумажным»), или иным механизмом. Это определяет, «сломал ли ORCH-115 рабочий путь» или «сделал давний дефект детерминированным и видимым». На фикс выбор стратегии это не меняет, но влияет на формулировку регресса.
2. Объём (scope)
В объёме
- Исправить стратегию исполнения staging-runner так, чтобы
deploy-stagingдля self-hostingorchestratorпроходил в проде, не завися от недоступного внутри прод-контейнера docker CLI. - Гарантировать, что tool-error / environment-дефект (в частности отсутствие исполняемого
docker/невозможность запустить сюиту по причине окружения) НЕ приводит к вводящему в заблуждение откатуdeploy-staging → developmentкак код-фейлу и не жжёт developer-retry; постоянный environment-дефект должен быть отличим от транзиентной инфры и от настоящего код-фейла. - Добавить prod-like регресс/preflight, который ловит «нет исполняемого/стратегия неработоспособна» до раската (а не постфактум, ложным откатом реальной задачи).
- Задокументировать границу исполнения (
docs/operations/INFRA.md+docs/architecture/README.md): где и как staging-сюита исполняется относительно прод-контейнера, какие исполняемые/сокеты доступны.
Вне объёма
- ❌ Изменение гейта
check_staging_status/_parse_staging_status, контракта15-staging-log.md(staging_status:),STAGE_TRANSITIONS, machine-verdict ключей, схемы БД — байт-в-байт прежние (ORCH-115 NFR-1: меняется продюсер/стратегия исполнения артефакта, не гейт). - ❌ Изменение содержимого/логики самой
scripts/staging_check.py(тулинг сюиты корректен; меняется лишь как/откуда её запускают). - ❌ Откат ORCH-115 (детерминизация staging корректна по замыслу; чиним способ исполнения).
- ❌ Выбор конкретного механизма (ssh host-side / Docker SDK поверх сокета / docker CLI в образе /
выделенный hook-режим) — это зона архитектора (
06-adr/), здесь — только требования и ограничения. - ❌ Изменение прод-деплой-пути (ORCH-036 self_deploy) сверх возможного переиспользования его ssh-механизма; happy-path прод-деплоя не трогается.
3. Заинтересованные стороны
- Заказчик/оператор (Слава) — страдает от ложных откатов и ручного разруливания залипших задач; принимает результат.
- Self-hosting конвейер
orchestrator— прямой потребитель: без фикса ни одна self-hosting задача не проходитdeploy-staging. - Все проекты на общем инстансе (enduro-trails) — косвенно: застрявшие/откатываемые self-hosting задачи занимают слоты и внимание оператора на общей очереди.
4. Бизнес-требования (BR)
- BR-1 — Для self-hosting
orchestratorстадияdeploy-staging(детерминированный staging-runner) исполняет staging-сюиту и проходит в проде без зависимости от docker CLI, отсутствующего внутри прод-контейнера. Задача уровня ORCH-116 (reviewer/tester зелёные, код корректен) доходит доdeploy, а не откатывается. - BR-2 — Tool-error / environment-дефект ≠ код-фейл. Невозможность исполнить сюиту по причине
окружения (нет исполняемого, недоступна стратегия) не должна завершаться откатом
deploy-staging → developmentкак кодовой ошибкой и не должна инкрементировать developer-retry; такой исход должен быть отдельным, отличимым (хольд/алерт оператору об инфра/environment-сбое). - BR-3 — Настоящий код-фейл сохраняется (анти-over-tolerance). Если сюита реально
исполнилась и упала (exit≠0), поведение — прежний откат
deploy-staging → development+ developer-retry (BR-2 не должно маскировать настоящие провалы; ср. ORCH-110 BR-6). - BR-4 — Prod-like preflight/регресс. Должен существовать механизм, ловящий «исполняемое отсутствует / стратегия неработоспособна» до раската (preflight на старте/в smoke-проверке) и тест, воспроизводящий «docker CLI отсутствует в контейнере» (красный до фикса, зелёный после).
- BR-5 — Граница исполнения задокументирована.
docs/operations/INFRA.md(иdocs/architecture/README.md) явно описывают, где/как исполняется staging-сюита относительно прод-контейнера, какие исполняемые/сокеты доступны, и почему docker-операции идут так, а не «изнутри app-контейнера». - BR-6 — Наблюдаемость. Различие «environment/tool-error» vs «код-фейл» видно в логе
(структурная запись), Telegram-алерте (кликабельный номер) и read-only блоке
staging_runnerвGET /queue.
5. Нефункциональные требования (NFR)
- NFR-1 (нулевая регрессия конвейера) —
STAGE_TRANSITIONS/ реестрQG_CHECKS/ имена и семантикаcheck_*(в т.ч.check_staging_status/_parse_staging_status) / machine-verdict ключи (staging_status:/deploy_status:/…) / схема БД — байт-в-байт не тронуты (фикс — стратегия исполнения продюсера, не гейт и не стадия). - NFR-2 (self-hosting safety) — путь исполнения staging-runner никогда: не рестартит прод
orchestrator(8500), не делаетdocker compose up orchestrator/--buildпрода, не force-push, не пишет вmain, не редактирует.env. Только запускает staging-сюиту (8501) и пишет лог (инвариант ORCH-115 BR-7/AC-8 сохраняется). - NFR-3 (security — если выбран socket/CLI-в-контейнере) — любой вариант с прямым использованием
docker.sock/CLI из контейнера = root-эквивалент на хосте → обязателен явный security-review в ADR (поверхность атаки, ограничение прав, :ro где возможно). Вариант host-side ssh должен переиспользовать уже существующий доверенный механизм (ORCH-036) без расширения привилегий. - NFR-4 (обратимость / kill-switch) — поведение под существующим
staging_runner_enabled(+ при необходимости — новым флагом выбора стратегии); выключенный kill-switch → прежний LLM-деплойер через_spawnбайт-в-байт (ORCH-115 fail-safe сохраняется). - NFR-5 (надёжность) — never-raise / fail-safe / restart-safe (по образцу leaf'ов
staging_runner/self_deploy/proc_group); очередь репо никогда не клинится; тайм-аут сюиты не растёт сверх бюджета, держащего сквозной инвариант reaper (ORCH-065/109/110). - NFR-6 (область) — изменение скоупится на self-hosting (
orchestrator, единственный с staging 8501); поведение для прочих репо/синхронного LLM-деплоя — не ухудшается (applies(repo)первым).
6. Допущения и ограничения
- Прод и staging контейнеры запущены на одном хосте;
/var/run/docker.sockдоступен на хосте, где docker CLI установлен; ssh на127.0.0.1под смонтированным ключом — рабочий канал (его уже использует ORCH-036 self-deploy). staging_check.pyисполняется внутри контейнераorchestrator-staging(там есть python3 и приложение 8501) — это контракт сюиты; меняется только то, кто инициируетdocker exec(хост vs прод-контейнер) или как (CLI vs SDK поверх сокета).- Источник истины «применять ли детерминированный runner» —
staging_runner.applies(repo)(ORCH-115); фикс не меняет его семантику. - Конкретный механизм исполнения (host-side ssh / Docker SDK поверх сокета / docker CLI в образе /
выделенный режим хука) — открытый вопрос для архитектуры, решается в
06-adr/с security-review.
7. Критерии успеха
Self-hosting задача уровня ORCH-116 проходит deploy-staging в проде (staging-сюита реально
исполняется, вердикт маппится из exit-кода); environment/tool-error не даёт ложного код-фейл-отката
и не жжёт developer-retry; настоящий код-фейл по-прежнему откатывает на development; существует
prod-like preflight + обязательный регресс-тест «нет docker CLI в контейнере» (красный до фикса,
зелёный после); граница исполнения задокументирована; гейт/контракт/STAGE_TRANSITIONS/схема БД — без
регресса; полный pytest tests/ -q зелёный. Детальные PASS/FAIL — 03-acceptance-criteria.md.
8. Риски
- Security (socket/CLI в контейнере): прямой доступ к
docker.sockиз app-контейнера = эскалация до root на хосте → ADR обязан взвесить поверхность атаки против host-side ssh-варианта. - Over-tolerance: слишком широкая трактовка «environment-дефекта» может замаскировать настоящие код-фейлы/реальные сбои staging-стенда (митигация — BR-3; ср. инцидент ORCH-110).
- Кросс-каттинг: ORCH-115 (staging-runner, прямой объект), ORCH-036 (self_deploy ssh-механизм —
потенциально переиспользуемый), ORCH-110 (proc_group tree-kill + infra-tolerance паттерн), ORCH-058
(
--build-staginghost-side docker exec — прецедент), ORCH-101 (host-параметризация). Правки маркированных блоков сверять с их06-adr/(CLAUDE.md §9). Детали/митигации —10-tech-risks.md(заполняет архитектор).