Replace the LLM `deployer` agent on the `deploy-staging` stage (self-hosting orchestrator) with a deterministic staging-runner intercepted in launch_job BEFORE _spawn (the deploy-finalizer / post-deploy-monitor reserved-agent precedent). The runner executes the SAME staging suite, maps the exit-code to `staging_status:` via the existing self_deploy.map_exit_code_to_status contract, writes 15-staging-log.md, and initiates the UNCHANGED check_staging_status gate exactly as a finished LLM-deployer would. Invariant (NFR-1): this replaces only the *producer* of the artifact — the artifact contract, the gate / _parse_staging_status / check_staging_status name, STAGE_TRANSITIONS, the machine-verdict key `staging_status:` and the DB schema are byte-for-byte unchanged. Additive, under a kill-switch + repo-scope CSV, never-raise, fail-safe back to the LLM path. Two-level outcome (D5, anti ORCH-110): suite executed -> verdict -> advance (FAILED -> the existing deploy-staging -> development rollback + developer-retry, same as a FAILED LLM verdict); tool-error (suite did not execute) -> bounded DEFER -> fail-closed FAILED + alert on exhaustion (infra != code fault; never a silent advance / false green). First implemented slice of the LLM determinization roadmap (ORCH-118 A6, replace-deterministic-now). - New leaf src/staging_runner.py (never-raise; proc_group tree-kill + timeout) - launch_job intercept + _run_staging_runner_job (mirror _run_deploy_finalizer_job) - config: ORCH_STAGING_RUNNER_* keys (enabled/repos/timeout/infra-retry budget) - GET /queue staging_runner observability block - docs: llm-call-sites/roadmap/usage-policy (A6 implemented; machine blocks + single-transport invariant intact), deployer.md (LLM branch -> fallback), CLAUDE.md, CHANGELOG.md, overview (tech-pipeline/tech-agents/tech-quality-security), .env.example - tests/test_orch115_staging_runner.py (TC-01..TC-13); LLM anti-drift green (TC-14) Refs: ORCH-115 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.2 KiB
LLM usage policy (ORCH-118)
Нормативный durable-документ. Формулирует принцип использования LLM в оркестраторе, критерии «keep vs replace» через control-path-ось, и нормативное определение «avoidable LLM control path». Применяется ко всем будущим правкам control-path'ов. Сопутствующие артефакты — карта
llm-call-sites.mdи roadmapllm-determinization-roadmap.md.
1. Принцип
LLM — только там, где нужно настоящее суждение. Если решение/вердикт control-path'а есть
детерминированная функция tool-сигналов, которые оркестратор уже вычисляет (exit-code pytest,
smoke, staging_check.py, статус деплоя, наличие файлов, CI-статус), — оно должно приниматься
детерминированно, а не консультацией LLM. LLM сохраняется там, где требуется суждение, не
сводимое к tool-сигналу (анализ требований, архитектурное решение, написание кода, приемлемость
ревью).
Это защищает автономность (NFR-2): меньше точек, где недетерминизм/стоимость/латентность LLM встроены в поток управления, и меньше класса инцидентов «LLM-агент принял решение, которое на деле есть исполнение фиксированных команд и маппинг результата» (RCA-трек ORCH-110/111/112/113/114/117).
2. Три оси решения (ground-truth — код)
- consultation ≠ transport/slot. «LLM консультируется» ⇔ решение/артефакт конвейера потребляет
суждение LLM. Существование транспорта (
_spawn) или слота агента (job-роли с перехватом до_spawn) — это capability, не консультация. - control-path (C) ≠ artifact-producer (P) — определяется кодом-потребителем вывода роли:
- (C) LLM эмитит machine-verdict, на котором ветвится
check_*-гейт → суждение входит в поток управления. - (P) LLM производит артефакт, а продвижение решает детерминированный гейт независимо (наличие файлов / CI) → суждение в control flow не входит.
- (C) LLM эмитит machine-verdict, на котором ветвится
- деривируемость вердикта — вердикт C-консультации либо детерминированная функция tool-сигналов, либо настоящее суждение, не сводимое к exit-коду.
3. Нормативное определение «avoidable LLM control path»
Это двухбитный проверяемый предикат над src/qg/checks.py, а не «удобство на глаз».
Call-site является avoidable LLM control path тогда и только тогда, когда выполнены оба условия:
- (i) это C (control-path) консультация — её LLM-вердикт потребляется потоком управления
(
check_*-гейт ветвится на нём: PASS → дальше / FAIL → откат); - (ii) вердикт деривируем (derivable) из tool-сигналов, которые оркестратор уже вычисляет сам —
exit-code
pytest/ smoke /staging_check.py/ статус деплоя.
Если оба условия выполнены, суждение LLM не добавляет информации → консультацию можно снять без потери смысла (заменить детерминированным раннером или гибридом с LLM-фолбэком только на не-деривируемую часть).
Поимённый целевой набор (сверен с кодом, прибит тестами TC-13/TC-14):
- avoidable LLM control paths =
{tester, deployer}— C и вердикт деривируем (result:= exit-codepytest+smoke;staging_status:= маппинг exit-кодаstaging_check.py). reviewer— C, но keep: вердикт «приемлемость кода/решения» НЕ деривируем из exit-кода (настоящее суждение). Это control-path-но-keep, не avoidable.analyst/architect/developer— не control path (P, artifact-producer): детерминированный гейт судит артефакт независимо.
4. Критерии решения: keep vs replace
| Ситуация (по осям §2) | Решение | Класс |
|---|---|---|
| P — artifact-producer (детерминированный гейт судит артефакт) | keep LLM | keep-LLM |
| C, вердикт НЕ деривируем (настоящее суждение) | keep LLM (назвать суждение) | keep-LLM |
| C, вердикт деривируем, замена безопасна сейчас | replace | replace-deterministic-now |
| C, вердикт деривируем, но замена позже / с предпосылками | replace later | replace-later/risky |
| C, ядро деривируемо, но часть требует суждения | hybrid (детерм. ядро + LLM-фолбэк) | needs-hybrid-fallback |
keep-LLM требует обоснования: любая
keep-LLM-запись обязана назвать конкретное суждение; для C-keep — явно зафиксировать не-деривируемость вердикта (почему не сводится к exit-коду).
5. Требование к новым/изменённым control-path'ам (норматив)
- Обоснование против политики. Любой новый или изменённый control-path, который консультирует
LLM, обязан в своём ADR обосновать это против настоящей политики: показать, что он P (artifact
judged independently) или C с не-деривируемым вердиктом. C-консультация с деривируемым
вердиктом — это
avoidable; её ввод без обоснования reviewer ловит как finding ≥P1. - Reviewer-ось (как ORCH-079) — требование, не реализация гейта. Политика рекомендует
reviewer'у проверять соответствие новых control-path'ов настоящей политике; ORCH-118 не вводит
новый Quality Gate (
QG_CHECKS/check_*не меняются) — это нормативное требование процесса. - Норматив сопровождения. Меняешь место вызова LLM или потребителя вердикта в
src/qg/checks.py→ обнови картуllm-call-sites.mdи эту политику в том же PR (анти-дрейф держат TC-13/TC-14). - Единственный транспорт. Единственный разрешённый транспорт LLM-консультации в
src/**— этоlauncher._spawn(S0). Ввод второго транспорта (новый_spawn, импортanthropic/openai/иного LLM-SDK, прямой HTTP Anthropic/Claude, второй model-invoking subprocess) запрещён без явного ADR; прибито тестами TC-01/TC-12. - Реализованный срез (ORCH-115). Снятие C-консультации с деривируемым вердиктом — это разрешённое
replace-deterministic-now, а не ввод новой LLM-консультации. ORCH-115 снял A6/staging-status: детерминированныйsrc/staging_runner.pyпроизводитstaging_status:без_spawn(перехват до него, какD1/D2) — раннер LLM не зовёт и второй транспорт не вводит, поэтому инвариант «единственный транспорт S0» соблюдён (TC-12 зелёный). Это образец для последующих срезов roadmap'а.