16 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-115 | analysis | analyst | ready-for-review | 2026-06-16 | claude-opus-4-8 |
01 — BRD (бизнес-требования): ORCH-115 — заменить LLM-деплойера детерминированным staging-раннером
Work Item: ORCH-115 · Repo: orchestrator · Стадия: analysis
1. Бизнес-контекст и проблема
Стадия deploy-staging сейчас исполняется LLM-агентом deployer (src/stages.py:18,
get_agent_for_stage("testing") = "deployer"). Фактическая работа агента на этой стадии —
чисто детерминированная: запустить staging-сюиту (docker exec orchestrator-staging python3 scripts/staging_check.py --base-url http://localhost:8501 --mode stub), смаппить exit-код
в вердикт (0 → SUCCESS, ≠0 → FAILED), записать 15-staging-log.md с frontmatter
staging_status: и смержить лог в main (.openclaw/agents/deployer.md, шаги 1–4).
Это avoidable LLM control path по нормативной политике (docs/architecture/llm-usage-policy.md
§3): (i) это C-консультация — её вердикт staging_status: потребляется гейтом
check_staging_status (src/qg/checks.py:599), и (ii) вердикт полностью деривируем из
exit-кода staging_check.py. Карта вызовов (docs/architecture/llm-call-sites.md, строка A6)
классифицирует deployer как replace-deterministic-now, а roadmap
(docs/architecture/llm-determinization-roadmap.md, машинный блок) ставит его rank 1 с
first_slice = yes, hybrid_needed = no. Эта задача — первый срез реализации того roadmap.
Боль / риск, который закрываем:
- Недетерминизм в потоке управления. Решение «advance или rollback» на
deploy-stagingзависит от LLM-сессии (стоимость, латентность, риск галлюцинации команд), хотя сводится к одному exit-коду. - Стоимость и латентность. Каждый прогон deployer'а на staging тратит токены/время opus-агента
(оценка по
agent_runs: deployer-строки ~40–120k токенов / 3–15 мин на прогон; точное число —GET /metrics) ради действия, которое выполняется тремя shell-строками. - Класс инцидентов «LLM принял решение, которое есть исполнение фиксированных команд + маппинг результата» — тот же RCA-трек, что ORCH-110/111/112/113/114/117.
Установленные факты (не изобретать):
- Пьюр-логика вердикта уже существует и юнит-тестируема:
src/staging_verdict.py::compute_staging_verdict(ORCH-061) считает infra-tolerant вердикт внутриstaging_check.py; раннеру остаётся доверять exit-коду (как уже делает LLM-deployer —deployer.mdstep 2). - Детерминированный прецедент замены агента уже работает:
launch_jobперехватывает зарезервированные ролиdeploy-finalizer(D1,src/agents/launcher.py:389) иpost-deploy-monitor(D2,:394) до_spawnи исполняет их как no-LLM-джобы. - Прод-ребро
deployдля self-hosting уже детерминировано (src/self_deploy.pyPhase A/B/C, ORCH-036) — LLM в критическом self-restart-пути нет. Срез не трогает критический прод-путь.
2. Объём (scope)
В объёме (Phase 1)
- Детерминированный staging-раннер для
deploy-stagingрепоorchestrator(self-hosting): исполняет staging-сюиту, маппит exit-код вstaging_status:, пишет15-staging-log.md, мержит вmain— без запуска LLM-агентаdeployer. - Раннер активируется через перехват в
launch_jobдо_spawn(прецедент D1/D2), без правкиsrc/stages.py/STAGE_TRANSITIONS(рольdeployerв словаре остаётся; меняется лишь кто обрабатывает джоб на стадииdeploy-stagingдля in-scope репо). - После выпуска вердикта раннер инициирует существующую оценку exit-гейта
check_staging_statusровно так, как это делал завершившийся LLM-deployer (_try_advance_stage→advance_stage( finished_agent="deployer")) — все нижестоящие под-гейты (security → merge → coverage → image-freshness, ORCH-022/043/027/058) и Phase A (ORCH-036) ведут себя идентично. - Kill-switch + скоуп-CSV (паттерн ORCH-022/027/043/089/090):
*_enabled(откат к LLM-пути) и*_repos(пусто → self-hosting only). - Наблюдаемость: read-only блок в
GET /queue+ структурный лог вердикта.
Вне объёма (явно НЕ делаем в ORCH-115)
- Phase 2 — «project deploy contract» для не-self репо (например
enduro-trails): конфигурируемый контракт deploy/rollback/healthcheck для произвольных репо. Описан как forward-looking follow-up (см. §6 и02-trz.md§8); в приёмку ORCH-115 не входит. Для не-self репоdeploy-stagingсейчас — мгновенный pass (check_staging_status→ N/A,src/qg/checks.py:620), поэтому Phase 1 их не затрагивает. - Прод-ребро
deploy(Phase A/B/C self-deploy, ORCH-036) — уже детерминировано; не трогаем. - LLM debug/triage-аналитик после детерминированного FAILED —
replace-deterministic-nowбез гибрида (roadmaphybrid_needed = no). В этом срезе LLM наdeploy-stagingотсутствует и в happy-path, и в fail-path; опциональный off-control-path debug-аналитик оставлен как будущее улучшение и требованиями не запрещён (см. NFR-7). - Любая правка
STAGE_TRANSITIONS/ реестра и имёнQG_CHECKS/ семантикиcheck_*/ machine-verdict-ключей / схемы БД (см. NFR-1). - ORCH-112 (checkout hygiene) и ORCH-114 (transition lease) — по явной границе задачи не
смешиваем: раннер вызывает
advance_stage, который уже владеет lease ORCH-114; сам lease/гигиену не модифицируем.
3. Заинтересованные стороны
- Заказчик / Owner (
homenet542@gmail.com) — инициатор детерминизации LLM-control-path'ов. - Платформа orchestrator (self-hosting) — прямой потребитель: дешевле/быстрее/детерминированнее
собственный
deploy-staging. - Другие проекты на общем инстансе (enduro-trails) — НЕ затронуты в Phase 1 (скоуп self-hosting), выигрывают позже от Phase 2.
- Reviewer / Tester / Deployer-роли конвейера — принимают результат через неизменные гейты.
4. Бизнес-требования (BR)
- BR-1 — Детерминированный staging без LLM. На
deploy-stagingдля in-scope репо вердиктstaging_status:производится детерминированным кодом (исполнениеstaging_check.py+ маппинг exit-кода), без консультации LLM. Happy-pathdeploy-stagingне вызывает_spawn. - BR-2 — Контракт артефакта неизменен. Раннер пишет тот же
15-staging-log.mdс тем же frontmatter-ключомstaging_status: SUCCESS|FAILED, который читаетcheck_staging_status/_parse_staging_status. Гейт байт-в-байт не меняется. - BR-3 — Эквивалентность маршрутизации. SUCCESS → продвижение на
deployчерез те же под-гейты и Phase A; FAILED → существующий откатdeploy-staging → development(тот же путь, что у FAILED-вердикта LLM-deployer'а,src/stage_engine.py:932). Никаких новых рёбер/исходов. - BR-4 — Переиспользование существующей пьюр-логики. Раннер использует уже существующий
exit-code→verdict маппинг (тривиальный
0→SUCCESS/иначеFAILED, зеркалоself_deploy.map_exit_code_to_status); infra-tolerance (ORCH-061) остаётся внутриstaging_check.py— раннер ему доверяет, повторно не судит. - BR-5 — Обратимость одним флагом. Глобальный kill-switch возвращает прежний LLM-deployer-путь
на
deploy-stagingбайт-в-байт; скоуп-CSV ограничивает раннер in-scope репо (пусто → толькоorchestrator). - BR-6 — Наблюдаемость. Исход раннера (запущен / SUCCESS / FAILED / ошибка инструмента) виден в
GET /queueи в структурном логе; деградации (например staging-инстанс недоступен) различимы от «код упал». - BR-7 — Self-hosting safety. Раннер на
deploy-stagingникогда не рестартит прод-контейнер 8500, не трогаетmainforce-push'ем, не правит.env/docker-compose.yml. Он лишь читает, исполняет staging-сюиту (порт 8501), пишет лог и мержит лог штатным PR/artifact-merge-путём.
5. Нефункциональные требования (NFR)
- NFR-1 — Скоуп-инвариант (анти-дрейф).
STAGE_TRANSITIONS(src/stages.py), реестр и именаQG_CHECKS/check_*/_parse_*(src/qg/checks.py), machine-verdict-ключи (staging_status:/deploy_status:/verdict:/result:/security_status:/coverage_status:), схема БД — байт-в-байт не тронуты. Это замена продюсера артефакта, не гейта. - NFR-2 — never-raise / fail-safe. Любая ошибка раннера (docker недоступен, таймаут, I/O) →
безопасный детерминированный исход без падения воркера: либо
FAILED(fail-closed, никогда ложный green), либо штатный requeue/defer — не «тихий advance». Сбой раннера не клинит очередь всех проектов. - NFR-3 — Изоляция процесса / таймаут. Спавненный subprocess (
docker exec …) имеет ограниченный таймаут и чистое завершение дерева процессов (согласовано с прецедентом ORCH-110proc_group/tree-kill); сирот pytest/docker не оставляет. - NFR-4 — Сквозные бюджеты времени. Таймаут раннера согласован со сквозным инвариантом
ORCH-065/109/110 (
reaper_max_running_s> Σ(работ на ребре deploy-staging) + grace) — без правкиreaper_max_running_s. - NFR-5 — Совместимость с не-self репо. Для репо вне скоупа
deploy-stagingведёт себя 1:1 как до ORCH-115 (LLM-deployer либо мгновенный N/A-pass). enduro-trails не затронут. - NFR-6 — Соответствие политике LLM. Изменение снимает LLM-консультацию A6; карта
docs/architecture/llm-call-sites.mdи политика/roadmap обновляются в том же PR (норматив сопровождения ORCH-118): строка deployer переходит из «consults_llm: yes» в реализованное детерминированное состояние. - NFR-7 — Не запрещать будущий debug-fallback. Архитектура раннера не должна архитектурно исключать опциональный off-control-path LLM debug-аналитик после FAILED (будущее улучшение); но в ORCH-115 он не реализуется.
6. Допущения и ограничения
- Допущение А1. staging-инстанс
orchestrator-staging(8501) поднят и доступен на хосте; его недоступность раннер трактует детерминированно (fail-closedFAILEDили defer — решает архитектор, AC-7). - Допущение А2.
scripts/staging_check.pyостаётся источником истины набора проверок и exit-кода (включая infra-tolerance ORCH-061). ORCH-115 его логику не меняет. - Допущение А3. Перехват «до
_spawn» по имени джоб-роли + стадии задачи — достаточный механизм диспетчеризации (как D1/D2); конкретный механизм финализирует архитектор (06-adr). - Ограничение О1. Граница задачи: не смешивать с ORCH-112/ORCH-114 (их код не модифицируется).
- Ограничение О2. Phase 2 (project deploy contract) — отдельный follow-up; ORCH-115 закрывает только Phase 1.
7. Критерии успеха
deploy-staging для orchestrator проходит без запуска LLM-агента deployer: детерминированный
раннер исполняет staging-сюиту, пишет корректный 15-staging-log.md (staging_status:),
мержит его в main, и конвейер продвигается/откатывается ровно как раньше — при неизменных
STAGE_TRANSITIONS/QG_CHECKS/гейтах/схеме БД, под kill-switch с откатом к прежнему поведению.
Детальные PASS/FAIL — 03-acceptance-criteria.md.
8. Риски
Краткий перечень (детали — 10-tech-risks.md, заполняет архитектор):
- R-1 — точка диспетчеризации «до
_spawn» должна корректно отличать staging-deployer от прод-deployer (по стадии задачи), иначе можно перехватить не тот джоб. - R-2 — после выпуска вердикта нужно надёжно инициировать
advance_stage, иначе задача зависнет наdeploy-staging(нет «финиша агента», который раньше триггерил гейт). - R-3 — таймаут/изоляция docker-subprocess; утечка процессов (ср. инцидент ORCH-110).
- R-4 — взаимодействие с transition-lease (ORCH-114) и serial-gate (ORCH-088) на side-effectful ребре — не сломать владение.
- R-5 — корректность отката FAILED (developer-retry cap) — должна совпасть с LLM-путём.