Files
orchestrator/docs/work-items/ORCH-123/01-brd.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

21 KiB
Raw Blame History

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_groupPopenOSErrorProcResult(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-hosting orchestrator проходил в проде, не завися от недоступного внутри прод-контейнера 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-2Tool-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-4Prod-like preflight/регресс. Должен существовать механизм, ловящий «исполняемое отсутствует / стратегия неработоспособна» до раската (preflight на старте/в smoke-проверке) и тест, воспроизводящий «docker CLI отсутствует в контейнере» (красный до фикса, зелёный после).
  • BR-5Граница исполнения задокументирована. docs/operations/INFRA.mddocs/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-staging host-side docker exec — прецедент), ORCH-101 (host-параметризация). Правки маркированных блоков сверять с их 06-adr/ (CLAUDE.md §9). Детали/митигации — 10-tech-risks.md (заполняет архитектор).