Files
orchestrator/docs/architecture/llm-usage-policy.md
claude-bot 80aa2409f7
All checks were successful
CI / test (push) Successful in 1m9s
CI / test (pull_request) Successful in 1m7s
feat(testing): deterministic test-runner replacing LLM tester on the testing stage (ORCH-116)
Second realised slice of the determinization-roadmap (ORCH-118 A5,
needs-hybrid-fallback): on the `testing` stage for the self-hosting
`orchestrator` repo the LLM `tester` agent is replaced by a deterministic
test-runner (src/test_runner.py), intercepted in launch_job BEFORE _spawn
(deploy-finalizer / post-deploy-monitor / staging-runner precedent).

It runs the regression `python -m pytest <target>` in the task worktree via
proc_group (tree-kill) + an optional read-only smoke (/health, /status, /queue
+ serial_gate), maps the exit-code -> result: PASS|FAIL via the existing
self_deploy.map_exit_code_to_status contract, writes 13-test-report.md and
initiates the EXISTING check_tests_passed gate exactly as a finished LLM-tester.

Invariant (NFR-1): only the *producer* changes — the artifact contract
(13-test-report.md / result:), the gate check_tests_passed / _parse_tests_verdict,
STAGE_TRANSITIONS and the DB schema are byte-for-byte UNCHANGED. Additive, under
a kill-switch (test_runner_enabled), never-raise, fail-closed, self-hosting scope,
two-level outcome (tool-error DEFER, anti ORCH-110), hybrid (LLM strictly
off-control-path). 52c-`status:` is aligned with the verdict (D6.1) so the
three-field _parse_tests_verdict never false-negatives a PASS.

Docs (ORCH-118 NFR-6, atomic with code): llm-call-sites.md (A5 implemented),
llm-determinization-roadmap.md (rank 2 implemented), llm-usage-policy.md,
README/internals/overview, tester.md, CLAUDE.md, CHANGELOG.md. Coverage:
tests/test_orch116_test_runner.py (TC-01..TC-14); LLM anti-drift tests green.
Full suite: 2137 passed.

Refs: ORCH-116
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 02:59:51 +03:00

10 KiB
Raw Blame History

LLM usage policy (ORCH-118)

Нормативный durable-документ. Формулирует принцип использования LLM в оркестраторе, критерии «keep vs replace» через control-path-ось, и нормативное определение «avoidable LLM control path». Применяется ко всем будущим правкам control-path'ов. Сопутствующие артефакты — карта llm-call-sites.md и roadmap llm-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 — код)

  1. consultation ≠ transport/slot. «LLM консультируется» ⇔ решение/артефакт конвейера потребляет суждение LLM. Существование транспорта (_spawn) или слота агента (job-роли с перехватом до _spawn) — это capability, не консультация.
  2. control-path (C) ≠ artifact-producer (P) — определяется кодом-потребителем вывода роли:
    • (C) LLM эмитит machine-verdict, на котором ветвится check_*-гейт → суждение входит в поток управления.
    • (P) LLM производит артефакт, а продвижение решает детерминированный гейт независимо (наличие файлов / CI) → суждение в control flow не входит.
  3. деривируемость вердикта — вердикт 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-code pytest+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'а.
  • Реализованный срез (ORCH-116). ORCH-116 снял A5/tester тем же паттерном: детерминированный src/test_runner.py производит result: на testing для in-scope репо без _spawn (перехват до него, как D1/D2) — exit-код pytest в worktree + read-only smoke. Это needs-hybrid-fallback (детерминированное ядро вынесено; LLM-фолбэк на не-деривируемое суждение — триаж падений / маппинг TC↔критерии — остаётся off-control-path и не производит result:). Раннер LLM не зовёт и второй транспорт не вводит → инвариант «единственный транспорт S0» соблюдён (TC-12 зелёный). Будущий off-control-path триаж — не новый транспорт control-path-консультации (он вне control-path).