Files
orchestrator/docs/architecture/llm-determinization-roadmap.md
claude-bot 9d16ee473a 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 09:37:40 +03:00

8.5 KiB
Raw Blame History

LLM determinization roadmap (ORCH-118)

Что это. Упорядоченный план детерминированных замен avoidable LLM control paths ({tester, deployer} — см. llm-call-sites.md). Это транзиентный план: он обновляется по мере закрытия follow-up'ов. ORCH-118 раннеры не реализует (FR-7); старт каждого кандидата гейтится утверждением карты.

Кандидаты названы ПО РОЛИ. Конкретные follow-up Plane-ID не фиксируются ни в одном артефакте (R3 / NFR-6): этих work item нет в подтверждённом backlog; ID присваивается при заведении задачи. Анти-фабрикация прибита тестом TC-11 (tests/test_llm_determinization_docs.py).

Оценки экономии — «оценка до фактического замера» (NFR-5). Источник — существующие колонки agent_runs (model/effort/токены/стоимость/время); точные числа снимаются при заведении follow-up'а через GET /metrics / запрос к agent_runs. Здесь — порядок величины, а не контракт.


1. Рекомендованный первый срез — deployer (staging-status) РЕАЛИЗОВАН (ORCH-115)

Статус: реализовано. Срез выполнен в ORCH-115src/staging_runner.py (перехват в launch_job до _spawn, как D1/D2): на стадии deploy-staging для self-hosting orchestrator вердикт staging_status: производит детерминированный код (маппинг exit-кода staging_check.py через self_deploy.map_exit_code_to_status), а не LLM. Под kill-switch staging_runner_enabled + скоуп staging_runner_repos (пусто → self-hosting only); LLM-ветвь остаётся fallback'ом. Контракт артефакта/гейта check_staging_status/STAGE_TRANSITIONS/схема БД — не тронуты. Детали — docs/work-items/ORCH-115/06-adr/ADR-001-deterministic-staging-runner.md, сквозной docs/architecture/adr/adr-0048-deterministic-staging-runner.md. Запись rank 1 в машинном блоке §4 сохраняется (first_slice = yes, инвариант карты) как историческая фиксация первого среза.

Обоснование (самый низкорисковый «чисто деривируемый» control path):

  1. Деривируемость максимальна. Вердикт staging_status:чистый маппинг exit-кода scripts/staging_check.py; готовый leaf src/staging_verdict.py::compute_staging_verdict (ORCH-061) уже считает этот вердикт детерминированно.
  2. Прод уже детерминирован. Ребро deploy (deploy_status:) для self-hosting orchestrator производит детерминированный finalizer (Phase A/B/C, ORCH-036) — LLM в критическом self-restart-пути нет. Срез не трогает критический путь → минимальная поверхность риска.
  3. Опирается на прецедент D1/D2 (launch_job-перехват до _spawn) — архитектурный риск замены агента уже снят рабочим паттерном.
  4. replace-deterministic-now, без hybrid-fallback (в отличие от tester).

2. Второй кандидат — tester (гибрид) РЕАЛИЗОВАН (ORCH-116)

Статус: реализовано. Срез выполнен в ORCH-116src/test_runner.py (перехват в launch_job до _spawn, как D1/D2/ORCH-115): на стадии testing для self-hosting orchestrator вердикт result: производит детерминированный код (exit-код pytest tests/ в worktree ветки + read-only smoke /health//status//queue+serial_gate, маппинг через self_deploy.map_exit_code_to_status в токенах PASS/FAIL), а не LLM. Под kill-switch test_runner_enabled + скоуп test_runner_repos (пусто → self-hosting only) + резолв тест-контракта (репо без контракта → LLM-tester, fail-safe). Контракт артефакта/гейта check_tests_passed/ STAGE_TRANSITIONS/схема БД — не тронуты. Запись rank 2 в машинном блоке §4 сохраняется (first_slice = no, hybrid_needed = yes — инвариант карты) как фиксация второго среза.

Детерминированное ядро (pytest + smoke даёт PASS/FAIL по exit-коду) покрывает основной вердикт; LLM-фолбэк сохраняется только на суждение, не сводимое к exit-коду: триаж падений и маппинг TC ↔ критерии приёмки. Поэтому needs-hybrid-fallback, а не replace-deterministic-now: поверхность суждения шире и объём работы больше. В Phase 1 (ORCH-116) детерминированное ядро вынесено в раннер; off-control-path LLM-триаж (он не выносит и не переопределяет result:, не добавляет ребро в STAGE_TRANSITIONS) зафиксирован как Phase 2 follow-up по роли и в этом срезе не реализуется.

3. Общие требования к каждому follow-up'у

Каждый кандидат при заведении задачи несёт: kill-switch + обратимость (паттерн ORCH-022/027/043/089/090 — флаг *_enabled, пустой CSV *_repos → self-hosting only), скоуп-гард (не трогать STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict/схему БД), а замена-агента — перехват в launch_job до _spawn (как D1/D2). Свежесть прецедента — инцидент-трек ORCH-110/111/112/113/114/117 (единое детерминированное владение side-effectful путями).


4. Машинно-читаемый блок roadmap

Заголовок (rank | role | dependencies | savings_estimate_source | security_risk | hybrid_needed | followup_type | first_slice) парсится tests/test_llm_determinization_docs.py::test_tc07_*. followup_typeпо роли, без Plane-ID. Ровно один first_slice = yes.

rank role dependencies savings_estimate_source security_risk hybrid_needed followup_type first_slice
1 deployer staging_verdict.compute_staging_verdict (ORCH-061) + launch_job pre-spawn precedent (D1/D2) agent_runs (deployer rows; estimate pending GET /metrics) low (prod already deterministic via Phase A/B/C ORCH-036) no deployer-replacement (staging-status mapping) yes
2 tester deterministic pytest+smoke core; LLM fallback for failure triage / TC-to-criteria mapping agent_runs (tester rows; estimate pending GET /metrics) medium (failure-triage judgment must stay correct) yes tester-hybrid (deterministic core + LLM fallback) no

5. Вне scope

  • reviewer — C, но keep (вердикт «приемлемость кода/решения» не деривируем): не в roadmap'е.
  • analyst/architect/developer — P (artifact-producer, не control path): не в roadmap'е.
  • Реализация раннеров — отдельные follow-up задачи (по роли), стартуют после утверждения карты.

Связанные документы: llm-call-sites.md, llm-usage-policy.md.