27 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-118 | analysis | analyst | ready-for-review | 2026-06-15 | claude-opus-4-8 |
02 — ТЗ (TRZ): ORCH-118 — replace avoidable LLM control paths with deterministic implementations
Work Item: ORCH-118 · Repo: orchestrator · Стадия: analysis
ТЗ описывает что должно измениться и где (артефакты/контракты/тесты), выведено из BRD и фактического кода. Как (точная структура/размещение документов карты, формат классификации, схема структурных тестов) — решает архитектор в
06-adr/. ТЗ фиксирует требования и границы.⚠️ Скоуп — inventory + классификация + roadmap + политика + структурные тесты. Реализация детерминированных раннеров — вне скоупа (FR-7). Это docs + tests only:
src/**-рантайм не меняется.📌 Follow-up'ы — по роли, без выдуманных Plane-ID (R3, NFR-6). Конкретные ID (напр.
ORCH-115/ORCH-116) в артефактах не фиксируются — этих work item нет в репо/подтверждённом backlog; ID присваивается при заведении задачи.🔁 R4 — блокер R3-ревью (закрыт). Единица инвентаря/инварианта — LLM-консультация (control-path потребляет суждение LLM), не «спавн процесса / существование Claude CLI». Claude CLI-subprocess через
_spawn— лишь текущий транспорт; «процесс существует» ≠ «LLM консультирован» (capability ≠ consultation). Развёрнуто — BRD §0; затрагивает FR-1/FR-3/FR-6.🔁 R5 — единственный оставшийся блокер R4-ревью. Артефакты разводили «консультация ≠ транспорт/ слот», но не делали явной control-path-ось — самую важную для названия задачи: среди реальных консультаций не различались (C) control-path (LLM-вердикт потребляется потоком управления —
check_*ветвится на нём) и (P) artifact-producer (детерминированный гейт судит артефакт; суждение LLM в control flow не входит); и нигде не был определён термин «avoidable LLM control path». R5 добавляет ось + определение (BRD §0-bis) и тянет их в FR-1/FR-2 + новый FR-8, в инвентарь §1 (новая колонка), в тесты FR-6 (TC-13/TC-14). Содержательная классификация не меняется — R5 выводит её изsrc/qg/checks.py.
1. Сводка изменения
Выпустить доказательную карту всех мест вызова LLM в оркестраторе с классификацией и
упорядоченным roadmap'ом детерминированных замен, а также нормативную политику использования LLM;
прибить инварианты карты к коду набором структурных тестов. Реализация замен не входит. Все
рантайм-контракты (STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict-ключи / схема БД) —
не меняются.
Опорный факт инвентаризации (ground-truth кода на момент задачи; line-привязки уточняет карта):
Единица — LLM-консультация (потребление суждения LLM), а не «спавн процесса» (R4, BRD §0). R5: дополнительно размечается ось (C) control-path / (P) artifact-producer и потребитель вывода (
check_*/_parse_*), доказывающий ось. КолонкаКонсультирует LLM?помечает транспорт/ слот vs факт консультации; колонкаControl path?помечает, входит ли LLM-вывод в поток управления.
| # | Call-site | Где | Что делает | Консультирует LLM? | Потребитель вывода (control-flow consumer) | Control path? (C/P) | Avoidable LLM control path? |
|---|---|---|---|---|---|---|---|
| S0 | Единственный транспорт LLM-консультации | src/agents/launcher.py::_spawn (CLAUDE_BIN --print … --system-prompt "$(cat …)" + Popen) |
реализует консультацию для любой из 6 ролей | транспорт (capability) | — | — | — (транспорт, не call-site решения) |
| A1 | analyst | .openclaw/agents/analyst.md, стадия analysis |
анализ → 01–04 | да (через S0) | check_analysis_complete (src/qg/checks.py:33) — наличие файлов |
P (artifact-producer) | нет (не control path) → keep-LLM |
| A2 | architect | .openclaw/agents/architect.md, стадия architecture |
архитектура → 06-adr | да (через S0) | check_architecture_done (checks.py:62) — наличие 06-adr/07 |
P | нет → keep-LLM |
| A3 | developer | .openclaw/agents/developer.md, стадия development |
реализация + PR | да (через S0) | check_ci_green (checks.py:82) + check_branch_mergeable (checks.py:657) — CI/merge |
P | нет → keep-LLM |
| A4 | reviewer | .openclaw/agents/reviewer.md, стадия review |
ревью → 12-review.md (verdict:) |
да (через S0) | check_reviewer_verdict (checks.py:336) читает verdict: → REQUEST_CHANGES-откат |
C (control path) | нет (вердикт НЕ деривируем — настоящее суждение) → keep-LLM |
| A5 | tester | .openclaw/agents/tester.md, стадия testing |
pytest+smoke → 13-test-report.md (result:) |
да (через S0) | check_tests_passed (checks.py:182) → _parse_tests_verdict (checks.py:226) читает result: |
C | ДА (вердикт = exit-code pytest/smoke) → needs-hybrid-fallback |
| A6 | deployer | .openclaw/agents/deployer.md, стадии deploy-staging/deploy |
staging_check.py/exit-code → 15/14 логи |
да (через S0) | check_staging_status (checks.py:599)→_parse_staging_status (checks.py:538) staging_status:; check_deploy_status (checks.py:473)→_parse_deploy_status (checks.py:413) deploy_status: |
C | ДА (вердикт = staging_check.py/exit-code; прод уже детерминирован Phase A/B/C) → replace-deterministic |
| D1 | deploy-finalizer | launch_job перехват до _spawn (launcher.py:389) |
детерминированный (LLM не консультируется) | нет (слот агента, перехват до _spawn) |
— | — (нет консультации) | — (уже детерминирован) |
| D2 | post-deploy-monitor | launch_job перехват до _spawn (launcher.py:394) |
детерминированный (LLM не консультируется) | нет (слот агента, перехват до _spawn) |
— | — (нет консультации) | — (уже детерминирован) |
Чтение таблицы (R5). Три ортогональных факта: (1)
Консультирует LLM?— транспорт/слот vs факт консультации (R4 §0); (2)Control path?— входит ли вывод роли в поток управления (C:check_*ветвится на LLM-вердикте; P: детерминированный гейт судит артефакт независимо); (3)Avoidable …?— двухбитный предикат BRD §0-bis (C И вердикт деривируем из tool-сигналов). Итог: avoidable LLM control paths = {tester, deployer}; control-path-но-keep ={reviewer}; не-control-path ={analyst, architect, developer}.Не-агентские control-path'ы (маршрутизация
advance_stage,QG_CHECKS/check_*/_parse_*,error_classifier,serial_gate/merge_gate/coverage_gate/security_gate/staging_verdict/review_parse/frontmatter,self_deployPhase A/B/C) — уже детерминированы (FR-3).
2. Задействованные модули / пути
| Путь | Действие |
|---|---|
src/agents/launcher.py |
читать (инвентарь S0/D1/D2; _spawn, launch_job:389/394, AGENT_CONFIGS, resolve_agent_model/effort) — не менять |
.openclaw/agents/{analyst,architect,developer,reviewer,tester,deployer}.md |
читать (инвентарь 6 ролей) — не менять |
src/qg/checks.py |
читать (доказательство control-path-оси: кто потребляет вывод каждой роли — check_analysis_complete:33/check_architecture_done:62/check_ci_green:82/check_reviewer_verdict:336/check_tests_passed:182+_parse_tests_verdict:226/check_staging_status:599+_parse_staging_status:538/check_deploy_status:473+_parse_deploy_status:413; QG_CHECKS-реестр) — не менять |
src/stages.py, src/stage_engine.py |
читать (доказать детерминизм маршрутизации) — не менять |
src/{serial_gate,merge_gate,coverage_gate,security_gate,staging_verdict,review_parse,error_classifier,frontmatter,self_deploy,post_deploy,transition_lease,reconciler,job_reaper}.py |
читать (детерминированные leaf'ы — доказательная база) — не менять |
src/usage.py, src/db.py (agent_runs) |
читать (источник оценок экономии токенов/времени) — не менять |
docs/architecture/llm-call-sites.md (имя — пример; финально решает архитектор) |
создать: карта call-site'ов + классификация + control-path-разметка (FR-1/FR-2/FR-3/FR-8) |
docs/architecture/llm-determinization-roadmap.md (имя — пример) |
создать: упорядоченный roadmap (FR-4) |
docs/architecture/llm-usage-policy.md (имя — пример) |
создать: нормативная политика + определение «avoidable LLM control path» (FR-5/FR-8) |
docs/work-items/ORCH-118/06-adr/ADR-001-*.md |
создать (архитектор): фиксация карты/таксономии/control-path-оси/первого среза как ADR |
tests/test_llm_call_site_inventory.py (имя — пример) |
создать: структурные анти-дрейф тесты (FR-6), включая control-path-инварианты |
docs/architecture/README.md, docs/overview/*, CHANGELOG.md |
обновить (ссылка на карту/политику; норматив golden-source) |
Документы карты/политики целесообразно разместить в
docs/architecture/(durable, сквозное), а не только вdocs/work-items/ORCH-118/— окончательное размещение/формат решает архитектор.
3. Функциональные требования
FR-1 — Полнота и привязка инвентаря LLM-консультаций (BR-1)
Единица — LLM-консультация (control-path потребляет суждение LLM), а не «спавн процесса» (R4, BRD
§0). Карта перечисляет каждый call-site: S0 (единственный транспорт _spawn), A1…A6 (6 ролей,
консультируют через S0), D1/D2 (job-роли, занимают слот агента, но НЕ консультируют LLM —
перехват в launch_job до _spawn; включены как доказательство паттерна). Поля записи: id,
location (file:line), trigger, stage/owner, output artifact, machine-verdict key (если
есть), output consumer (check_*/_parse_* с file:line — кто потребляет вывод роли),
est. tokens/runtime, consults-LLM (consultation vs LLM-capable transport/slot, §0.3),
axis (C control-path / P artifact-producer, §0-bis), classification, rationale, dependency,
risk. Каждый file:line обязан резолвиться в реальный код. Инвариант (транспорт-агностичный,
двусторонний): единственный транспорт LLM-консультации в src/** — S0; иного LLM-транспорта нет —
тесты FR-6(a)+(f).
FR-2 — Таксономия классификации (BR-2)
Ровно 4 взаимоисключающих класса с определениями:
keep-LLM— нужно настоящее суждение; обязательно назвать конкретное суждение.replace-deterministic-now— безопасная детерминированная замена сейчас.replace-later/risky— замена возможна, но позже / с риском (нужны предпосылки).needs-hybrid-fallback— детерминированное ядро + LLM-фолбэк только на суждение.
Каждому call-site присвоен ровно один класс, и класс выводится из control-path-оси (§0-bis, FR-8), а не постулируется:
- P (artifact-producer) →
keep-LLM— детерминированный гейт судит артефакт; авторская работа требует суждения:analyst,architect,developer. - C + НЕ-деривируемый вердикт →
keep-LLM— control path, но суждение настоящее:reviewer(назвать суждение: «приемлемость кода/решения», не сводится к exit-коду). - C + деривируемый вердикт → avoidable →
replace-*/needs-hybrid-fallback:deployer → replace-deterministic-now/replace-later/risky(staging = exit-code-маппинг; прод self-hosting уже детерминирован Phase A/B/C),tester → needs-hybrid-fallback(детерминированный прогонpytest+smoke даёт PASS/FAIL; LLM-суждение только на триаж падений / маппинг TC↔критерии). deploy-finalizer/post-deploy-monitor → already-deterministic(вне таксономии замен, эталон).
📌 Follow-up'ы — по роли, без Plane-ID (R3, NFR-6). Кандидаты обозначаются ролью (deployer-замена, tester-гибрид), а не конкретными ID.
FR-3 — Подтверждение детерминизма не-агентских путей (BR-3)
Карта отдельным разделом фиксирует, с file:line-доказательством, что НЕ консультируют LLM (не зависят
от суждения LLM ни через _spawn, ни иным транспортом): маршрутизация (advance_stage/
STAGE_TRANSITIONS), все QG_CHECKS/check_*, парсеры вердиктов (_parse_* через
frontmatter.parse_frontmatter), error_classifier (regex), под-гейты (security/merge/coverage/
image-freshness), self_deploy Phase A/B/C, reconciler/reaper/serial-gate/transition-lease. ⚠️ Эти
модули спавнят subprocess'ы (git/pytest/docker/ssh/сканеры) — это детерминированные
инструменты, не LLM-консультации (§0): доказательство детерминизма — отсутствие LLM-транспорта,
а не отсутствие subprocess. Любая найденная неожиданная LLM-консультация добавляется в инвентарь
(FR-1) и классифицируется (FR-2).
FR-4 — Упорядоченный roadmap замен (BR-4)
Ранжированный список кандидатов (названных по роли); для каждого: зависимости (предпосылки/блокеры),
оценка экономии токенов/времени (из agent_runs), риск безопасности, нужен ли hybrid-fallback,
ожидание kill-switch/обратимости, и тип будущей follow-up задачи по роли (без конкретного
Plane-ID — заводится отдельно, NFR-6). Явно: рекомендованный первый срез + обоснование (самый
низкорисковый, «чисто деривируемый» control path, опирающийся на существующий прецедент D1/D2).
FR-5 — Политика использования LLM (BR-5)
Нормативный durable-документ: принцип «LLM — только где нужно настоящее суждение»; критерии решения
keep vs replace, сформулированные через ось §0-bis (является ли путь control path — ветвится ли
check_* на LLM-выводе; деривируем ли вердикт из tool-сигналов; обратимость; влияние на автономность);
требование к новым/изменённым control-path'ам обосновывать любое использование LLM против политики.
Может включать рекомендацию reviewer-оси (как ORCH-079) — как требование, не как реализацию гейта
(новый QG не вводится, FR-6 §QG).
FR-6 — Структурные анти-дрейф тесты (BR-6)
Новый offline-тест-файл (без сети/LLM/subprocess-к-модели), проверяющий инварианты карты. ⚠️ R4 —
инвариант формулируется вокруг LLM-консультации/транспорта, а не «существования процесса Claude CLI».
Дискриминатор тестов — «консультирует LLM», а не «спавнит subprocess»; десятки прочих subprocess
(git/pytest/docker/ssh/сканеры/staging_check.py) явно исключаются из матчинга.
- (a) Единственный транспорт. В
src/**ровно одна точка сборки/запуска Claude CLI (матчинг по совокупности признаков LLM-транспорта:CLAUDE_BIN+--system-prompt+Popen/bash -c), и этоlauncher._spawn. (Необходимое, но не достаточное — дополняется (f).) - (f) Отсутствие иного LLM-транспорта (R4). В
src/**нет альтернативного транспорта LLM-консультации: ни импортаanthropic/openai/иного LLM-SDK, ни прямого HTTP-эндпоинта Anthropic/Claude (api.anthropic.com,/v1/messagesи т.п.), ни второго model-invoking subprocess-сборщика. Allowlist единственного разрешённого транспорта =S0. - (b) Нет консультации в детерминированных путях. Перечисленные детерминированные модули и
обработчики
D1/D2не консультируют LLM (ни_spawn-транспорта, ни альтернативного по (f)). - (c) Карта перечисляет ровно те промпт-файлы, что физически лежат в
.openclaw/agents/. - (d) Классификация покрывает каждый перечисленный call-site ровно один раз (тотальность).
- (e) Capability ≠ consultation.
D1/D2перехватываются вlaunch_jobдо_spawn(launcher.py:389/394) → занимают слот, но консультации нет. - (g) Control-path-разметка верна (R5, TC-13). Для каждой роли поле
axis(C/P) карты согласовано с фактическим потребителем вsrc/qg/checks.py: P-роли (analyst/architect/developer) потребляются детерминированными гейтами (check_analysis_complete/check_architecture_done/check_ci_green), C-роли (reviewer/tester/deployer) — verdict-парсерами (check_reviewer_verdict/_parse_tests_verdict/_parse_staging_status/_parse_deploy_status). Дрейф (роль переразмечена или потребитель в коде сменил природу) → красный. - (h) Avoidable-набор зафиксирован (R5, TC-14). Множество «avoidable LLM control path» в карте =
ровно
{tester, deployer};reviewerпомечен control-path-но-keep;analyst/architect/developer— не control path. Любое добавление/удаление без обновления карты → красный.
❌ Не вводить тест, прибивающий карту к конкретным follow-up Plane-ID → ✅ тесты проверяют только инварианты, резолвящиеся в код/файлы репозитория (R3, NFR-6).
FR-7 — Скоуп-гард (BR-7)
Раннеры замен не реализуются в ORCH-118. Карта/roadmap явно помечают кандидатов по роли (deployer-замена, tester-гибрид) как follow-up, старт которых гейтится утверждением карты. Тест/диф не должны содержать новых детерминированных раннеров tester/deployer. Конкретные follow-up Plane-ID не фиксируются ни в одном артефакте (NFR-6).
FR-8 (R5) — Явная control-path-ось и определение «avoidable» (BR-8/BR-9)
- Разметка оси. Карта несёт для каждой консультации поле
axis∈ {C, P} (§0-bis) + доказательство (полеoutput consumer—check_*/_parse_*сfile:line). Разметка проверяема (FR-6g/TC-13). - Определение термина. Карта/политика дают нормативное определение «avoidable LLM control
path» — двухбитный предикат: (i) C-консультация (LLM-вердикт потребляется потоком управления) И
(ii) вердикт деривируем из tool-сигналов (exit-code
pytest/smoke/staging_check.py/деплоя). - Поимённый целевой набор. Карта/roadmap явно называют
{tester, deployer}как avoidable LLM control paths, явно отделяя их отreviewer(C, но keep — суждение не деривируемо) и отanalyst/architect/developer(P — не control path). Набор проверяем (FR-6h/TC-14). - Связь с классификацией. Класс из FR-2 выводится из (C/P)-типа и деривируемости (а не наоборот).
4. Изменения API
Нет. (Опциональная read-only наблюдаемость в GET /queue/GET /metrics — вне скоупа ORCH-118;
если архитектор сочтёт полезным — отдельная аддитивная врезка, но не требуется этой задачей.)
5. Изменения схемы БД
Нет. (Источник оценок экономии — существующие колонки agent_runs: model/effort/токены/стоимость/
время; только чтение.)
6. Требования к новым/изменённым QG checks
Нет. QG_CHECKS / check_* / _parse_* / machine-verdict-ключи — байт-в-байт. Структурные тесты
FR-6 (включая control-path TC-13/TC-14) — обычные pytest-тесты, не Quality Gate и не стадия.
Политика LLM (FR-5) — нормативный документ, а не машинный гейт. ⚠️ Control-path-ось — аналитическая
разметка карты, читающая check_* как ground-truth; она ничего в src/qg/checks.py не меняет.
7. Совместимость / регресс
- Docs + tests only: рантайм
src/**не меняется → нулевая регрессия; enduro-trails не затронут; kill-switch не нужен (нет рантайм-поведения), как в ORCH-077/079/101/102/103/011. - Обратимость: артефакты — документы и тесты; откат = удаление/правка docs (рантайм-риска нет).
- Анти-дрейф: структурные тесты держат карту синхронной с кодом, включая control-path-ось
(TC-13/TC-14); норматив сопровождения — «менял места вызова LLM или потребителя вердикта в
src/qg/checks.py→ обнови карту/разметку и политику в том же PR». - Анти-фабрикация (R3): артефакты фиксируют только проверяемые ссылки; follow-up'ы — по роли, без выдуманных Plane-ID (NFR-6).
- Self-hosting: не деплоит/не рестартит прод/не трогает
main— безопасно для общего инстанса.