docs(llm): LLM call-site map, control-path axis, roadmap & usage policy + anti-drift tests
All checks were successful
CI / test (push) Successful in 1m8s
CI / test (pull_request) Successful in 1m10s

ORCH-118 (inventory-first, docs+tests only): publish an evidence-based map of
every place the orchestrator's control flow consumes (or can consume) an LLM
judgment, mark the control-path axis (C control-path vs P artifact-producer),
define "avoidable LLM control path" as a checkable two-bit predicate, classify
each call-site, and order the deterministic-replacement roadmap. Pin the map to
code with offline structural anti-drift tests.

- docs/architecture/llm-call-sites.md   — map + machine-readable inventory block
  + control-path axis + classification + keep-LLM justifications + deterministic
  non-agent paths (FR-1/FR-2/FR-3/FR-8).
- docs/architecture/llm-determinization-roadmap.md — ordered candidates BY ROLE,
  savings sourced from agent_runs, recommended first slice = deployer staging
  (FR-4). No fabricated follow-up Plane-IDs (R3/NFR-6).
- docs/architecture/llm-usage-policy.md — normative principle, keep/replace
  criteria via the axis, definition of "avoidable LLM control path" (FR-5/FR-8).
- tests/test_llm_call_site_inventory.py — TC-01/02/03/04/05/06/09/12/13/14.
- tests/test_llm_determinization_docs.py — TC-07/08/11.
- CHANGELOG.md + docs/overview/tech-quality-security.md — golden-source sync (AC-8).

Avoidable LLM control paths = {tester, deployer}; control-path-keep = {reviewer};
not-control-path (P) = {analyst, architect, developer}. Single LLM transport =
launcher._spawn (S0); no alternative transport (TC-12). Runtime untouched:
STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict keys / DB schema are
byte-for-byte; no replacement runners implemented (FR-7). Full suite: 2081 passed.

Refs: ORCH-118
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 00:13:07 +03:00
parent 7597804f8c
commit 9710d5f80d
7 changed files with 943 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
# LLM call-site map — inventory, control-path axis & classification (ORCH-118)
> **Что это.** Доказательная карта **каждого места**, где control-path оркестратора
> потребляет (или способен потребить) суждение LLM. Единица инвентаря — **LLM-консультация**
> (control-path потребляет суждение LLM), **не** «спавн процесса / существование Claude CLI»
> (capability ≠ consultation, BRD §0 / R4).
>
> **Снимок, прибитый к коду.** Карта — *снимок*; её инварианты держат структурные тесты
> `tests/test_llm_call_site_inventory.py` (анти-дрейф). Меняешь место вызова LLM или потребителя
> вердикта в `src/qg/checks.py` → **обнови эту карту и политику в том же PR** (норматив сопровождения).
>
> **Источник истины** содержательной классификации — ADR
> `docs/work-items/ORCH-118/06-adr/ADR-001-llm-call-site-map-and-determinization-roadmap.md`
> (D2/D3/D4) и сквозной `docs/architecture/adr/adr-0047-llm-usage-policy-and-call-site-map.md`.
> Нормативное определение «avoidable LLM control path» и критерии keep/replace — в
> [`llm-usage-policy.md`](llm-usage-policy.md); порядок замен — в
> [`llm-determinization-roadmap.md`](llm-determinization-roadmap.md).
---
## 0. Три ортогональных факта (как читать карту)
Карта **явно** разводит три раздельных факта — их смешение было корнем блокеров R3→R5:
1. **Ось 1 — consultation ≠ transport/slot.** «LLM-консультация» = точка, где решение/артефакт
конвейера **потребляет суждение LLM**. Транспорт (`_spawn`) — реализация, не определение. Слот
агента (job-роли `D1`/`D2`) делает site LLM-**capable**, но консультация гейтится потоком
управления (перехват **до** `_spawn`) → **capability ≠ consultation**.
2. **Ось 2 — control-path (C) ≠ artifact-producer (P).** Определяется **кодом-потребителем** вывода
роли в `src/qg/checks.py`:
- **(C) control-path** — LLM эмитит machine-verdict, на котором **ветвится `check_*`-гейт**
(PASS → дальше / FAIL → откат). Суждение LLM **входит** в поток управления.
- **(P) artifact-producer** — LLM производит артефакт, а продвижение решает **детерминированный
гейт**, судящий артефакт **независимо** (наличие файлов / CI-статус). Суждение LLM в control
flow **не входит**.
3. **Ось 3 — деривируемость вердикта.** Вердикт C-консультации либо есть **детерминированная функция
tool-сигналов** (exit-code `pytest`/smoke/`staging_check.py`/деплоя), которые оркестратор **уже
вычисляет сам**, либо требует **настоящего суждения**, не сводимого к exit-коду.
> **Avoidable LLM control path** (нормативное определение — [`llm-usage-policy.md`](llm-usage-policy.md)):
> call-site, для которого выполнены **оба** условия — **(i)** это C-консультация (её LLM-вердикт
> потребляется потоком управления) **и** **(ii)** вердикт **деривируем** из tool-сигналов. Тогда
> суждение LLM не добавляет информации → консультацию можно снять без потери смысла.
---
## 1. Инвентарь LLM-консультаций (полный, привязан к коду)
Каждая запись несёт: `id` · `location (file:line)` · `trigger` · `stage/owner` · `output artifact` ·
`machine-verdict key` · `output consumer` (кто потребляет вывод роли) · `est. tokens/runtime`
(оценка из `agent_runs`) · `consults-LLM` · `axis` (C/P) · `classification` · `rationale`.
| id | location (file:line) | trigger | stage / owner | output artifact | machine-verdict key | output consumer (`src/qg/checks.py`) | est. tokens/runtime (оценка) | consults-LLM | axis | classification | rationale |
|----|----------------------|---------|---------------|-----------------|---------------------|--------------------------------------|------------------------------|--------------|------|----------------|-----------|
| **S0** | `src/agents/launcher.py:472` (`_spawn`; CLI-сборка `610-614`; парс токенов `_monitor_agent:838`) | `launch_job``_spawn` для любой из 6 ролей | — (транспорт) | — | — | — | — | **транспорт** (capability) | — | — | Единственный транспорт LLM-консультации в `src/**`; не call-site решения |
| **A1** | `.openclaw/agents/analyst.md` | стадия `analysis` | analyst | `01-brd``04-test-plan` | — | `check_analysis_complete:33` (наличие файлов) | ~80200k / 520 мин | да (через S0) | **P** | `keep-LLM` | анализ требований / BRD/ТЗ — настоящее суждение; гейт судит лишь наличие артефактов |
| **A2** | `.openclaw/agents/architect.md` | стадия `architecture` | architect | `06-adr/`, `07` | — | `check_architecture_done:62` (наличие 06-adr/07) | ~80200k / 520 мин | да (через S0) | **P** | `keep-LLM` | архитектурное решение / ADR — настоящее суждение |
| **A3** | `.openclaw/agents/developer.md` | стадия `development` | developer | код + PR | — | `check_ci_green:82` (+ `check_branch_mergeable:657`) — CI/merge | ~150400k / 1040 мин | да (через S0) | **P** | `keep-LLM` | написание кода — настоящее суждение; гейт судит CI/merge, не самоотчёт |
| **A4** | `.openclaw/agents/reviewer.md` | стадия `review` | reviewer | `12-review.md` | `verdict:` | `check_reviewer_verdict:336` (`verdict:`) | ~100300k / 525 мин | да (через S0) | **C** | `keep-LLM` | control path, но вердикт «приемлемость кода/решения» **НЕ деривируем** из exit-кода — настоящее суждение |
| **A5** | `.openclaw/agents/tester.md` | стадия `testing` | tester | `13-test-report.md` | `result:` | `check_tests_passed:182``_parse_tests_verdict:226` (`result:`) | ~60150k / 520 мин | да (через S0) | **C** | `needs-hybrid-fallback` | **avoidable**: PASS/FAIL = exit-code `pytest`+smoke (деривируем); LLM нужен лишь на триаж падений / маппинг TC↔критерии |
| **A6** | `.openclaw/agents/deployer.md` | стадии `deploy-staging` / `deploy` | deployer | `15-staging-log.md` / `14-deploy-log.md` | `staging_status:` / `deploy_status:` | `check_staging_status:599``_parse_staging_status:538` (`staging_status:`); `check_deploy_status:473``_parse_deploy_status:413` (`deploy_status:`) | ~40120k / 315 мин | да (через S0) | **C** | `replace-deterministic-now` | **avoidable**: staging-вердикт = маппинг exit-кода `staging_check.py`; прод уже детерминирован Phase A/B/C (ORCH-036) |
| **D1** | `src/agents/launcher.py:389` (перехват в `launch_job` **до** `_spawn`; «Not an LLM spawn» `407`) | post-deploy edge | deploy-finalizer | jobs-row | — | — | — (детерминированный) | **нет** (слот, перехват до `_spawn`) | — | `already-deterministic` (эталон) | Занимает слот агента, но LLM не консультируется — рабочий прецедент замены |
| **D2** | `src/agents/launcher.py:394` (перехват в `launch_job` **до** `_spawn`; «Not an LLM spawn» `428`) | post-deploy observation | post-deploy-monitor | jobs-row | — | — | — (детерминированный) | **нет** (слот, перехват до `_spawn`) | — | `already-deterministic` (эталон) | Тик наблюдения; LLM не консультируется |
> **Итог (поимённо).** `avoidable LLM control paths = {tester, deployer}`; control-path-но-keep =
> `{reviewer}`; не-control-path (P) = `{analyst, architect, developer}`; already-deterministic-эталон =
> `{deploy-finalizer, post-deploy-monitor}`.
### 1.1 Машинно-читаемый блок инвентаря
> Стабильный заголовок таблицы (`id | role | location | output_consumer | consults_llm | axis |
> avoidable | classification`) парсится `tests/test_llm_call_site_inventory.py` (split по `|`, без
> новых зависимостей) и сверяется с кодом (TC-03/04/05/13/14). **Не менять заголовок и значения без
> синхронной правки кода/тестов.**
<!-- ORCH-118-INVENTORY-BLOCK:START -->
| id | role | location | output_consumer | consults_llm | axis | avoidable | classification |
|----|------|----------|-----------------|--------------|------|-----------|----------------|
| S0 | _spawn | src/agents/launcher.py:472 | - | transport | - | - | - |
| A1 | analyst | .openclaw/agents/analyst.md | check_analysis_complete | yes | P | no | keep-LLM |
| A2 | architect | .openclaw/agents/architect.md | check_architecture_done | yes | P | no | keep-LLM |
| A3 | developer | .openclaw/agents/developer.md | check_ci_green | yes | P | no | keep-LLM |
| A4 | reviewer | .openclaw/agents/reviewer.md | check_reviewer_verdict | yes | C | no | keep-LLM |
| A5 | tester | .openclaw/agents/tester.md | _parse_tests_verdict | yes | C | yes | needs-hybrid-fallback |
| A6 | deployer | .openclaw/agents/deployer.md | _parse_staging_status | yes | C | yes | replace-deterministic-now |
| D1 | deploy-finalizer | src/agents/launcher.py:389 | - | no | - | - | already-deterministic |
| D2 | post-deploy-monitor | src/agents/launcher.py:394 | - | no | - | - | already-deterministic |
<!-- ORCH-118-INVENTORY-BLOCK:END -->
### 1.2 keep-LLM — названное суждение (обоснование)
> Для каждой `keep-LLM`-записи назван **конкретный** вид суждения, ради которого LLM сохраняется.
> Для C-keep (`reviewer`) обоснование явно фиксирует **НЕ-деривируемость** вердикта (почему не сводится
> к exit-коду). Парсится TC-05 (`- role: текст`).
<!-- ORCH-118-KEEP-JUSTIFICATION-BLOCK:START -->
- analyst: анализ требований и написание BRD/ТЗ — настоящее суждение; детерминированный гейт `check_analysis_complete` судит лишь наличие файлов, не их содержательное качество.
- architect: архитектурное решение и ADR — настоящее суждение о компромиссах/инвариантах; `check_architecture_done` судит лишь наличие 06-adr/07.
- developer: написание кода — настоящее суждение; гейт `check_ci_green` судит CI/merge, а не самоотчёт агента.
- reviewer: «приемлемость кода/решения» — настоящее суждение, которое НЕ деривируемо (not derivable) из exit-кода `pytest`/smoke/деплоя; в отличие от tester/deployer вердикт reviewer'а не сводится к tool-сигналу, поэтому это control-path-но-keep, а не avoidable.
<!-- ORCH-118-KEEP-JUSTIFICATION-BLOCK:END -->
---
## 2. Таксономия классификации (4 класса, выведена из осей)
Четыре взаимоисключающих класса; класс **выводится** из осей §0 (а не постулируется):
- **`keep-LLM`** — нужно настоящее суждение (обязательно **назвать** конкретное суждение, §1.2).
- **`replace-deterministic-now`** — безопасная детерминированная замена сейчас.
- **`replace-later/risky`** — замена возможна позже / с предпосылками.
- **`needs-hybrid-fallback`** — детерминированное ядро + LLM-фолбэк только на суждение.
**Правило вывода:**
`P → keep-LLM`; `C + не-деривируемый вердикт → keep-LLM`;
`C + деривируемый вердикт → replace-* / needs-hybrid-fallback` (**= avoidable**).
`deploy-finalizer`/`post-deploy-monitor` помечены `already-deterministic` — вне таксономии замен
(эталон: LLM не консультируется, перехват до `_spawn`).
---
## 3. Детерминизм не-агентских control-path'ов (доказательство, FR-3 / AC-3)
Эти пути **не консультируют LLM** (ни через `_spawn`-транспорт, ни альтернативным транспортом). ⚠️ Они
**спавнят subprocess'ы** (`git`/`pytest`/`docker`/`ssh`/сканеры/`staging_check.py`) — это
детерминированные **инструменты**, не LLM: доказательство детерминизма — **отсутствие *LLM*-транспорта**,
а не отсутствие *subprocess* (дискриминатор §0). Проверяется TC-02.
| Путь / модуль | `file:line` (якорь) | Природа |
|---------------|---------------------|---------|
| Маршрутизация стадий | `src/stages.py::STAGE_TRANSITIONS:12`, `advance_stage` в `src/stage_engine.py` | статический словарь + детерминированная функция |
| Реестр Quality Gate | `src/qg/checks.py::QG_CHECKS:812` (14 имён) | словарь имя→функция |
| Все `check_*` | `src/qg/checks.py` (`33/62/82/182/336/473/599/657`, …) | файловые/HTTP/exit-code проверки |
| Парсеры вердиктов `_parse_*` | `src/qg/checks.py:226/413/538` через `src/frontmatter.py::parse_frontmatter` | YAML-frontmatter парс (читают, **не производят** вердикт) |
| Классификатор ошибок | `src/error_classifier.py` | regex по строкам |
| Под-гейты | `src/{security_gate,merge_gate,coverage_gate,staging_verdict}.py` | сканеры/`pytest --cov`/git/маппинг exit-кодов |
| Self-deploy Phase A/B/C | `src/self_deploy.py` | детерминированный detached-деплой (ORCH-036) |
| Сериализация / владение | `src/{serial_gate,transition_lease,reconciler,job_reaper}.py` | FIFO-гейт / durable-lease / CAS / reaper |
Любая найденная **неожиданная** LLM-консультация в этих путях добавляется в инвентарь §1 и
классифицируется §2 (тогда TC-02 станет красным — точка дрейфа).
---
## 4. Скоуп и анти-дрейф
- **Docs + tests only (ORCH-118).** `STAGE_TRANSITIONS` / реестр и имена `QG_CHECKS`/`check_*` /
machine-verdict-ключи (`verdict:`/`result:`/`staging_status:`/`deploy_status:`/`security_status:`/
`coverage_status:`) / схема БД — **байт-в-байт не тронуты**. Раннеры замен **не** реализованы
(см. roadmap — follow-up'ы по роли, без Plane-ID).
- **Анти-дрейф тесты:** `tests/test_llm_call_site_inventory.py` (TC-01…TC-06, TC-09, TC-12, TC-13,
TC-14) и `tests/test_llm_determinization_docs.py` (TC-07/08/11). Дискриминатор всех проверок —
**«консультирует LLM», а не «спавнит subprocess»**.
- **Связанные документы:** [`llm-usage-policy.md`](llm-usage-policy.md),
[`llm-determinization-roadmap.md`](llm-determinization-roadmap.md).

View File

@@ -0,0 +1,70 @@
# LLM determinization roadmap (ORCH-118)
> **Что это.** Упорядоченный план детерминированных замен **avoidable LLM control paths**
> (`{tester, deployer}` — см. [`llm-call-sites.md`](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)**
Обоснование (самый низкорисковый «чисто деривируемый» 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 (гибрид)**
Детерминированное ядро (`pytest` + smoke даёт PASS/FAIL по exit-коду) покрывает основной вердикт;
LLM-фолбэк сохраняется **только** на суждение, не сводимое к exit-коду: **триаж падений** и **маппинг
TC ↔ критерии приёмки**. Поэтому `needs-hybrid-fallback`, а не `replace-deterministic-now`: поверхность
суждения шире и объём работы больше.
## 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`.
<!-- ORCH-118-ROADMAP-BLOCK:START -->
| 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 |
<!-- ORCH-118-ROADMAP-BLOCK:END -->
---
## 5. Вне scope
- `reviewer` — C, но **keep** (вердикт «приемлемость кода/решения» не деривируем): **не** в roadmap'е.
- `analyst`/`architect`/`developer` — P (artifact-producer, не control path): **не** в roadmap'е.
- Реализация раннеров — отдельные follow-up задачи (по роли), стартуют после утверждения карты.
Связанные документы: [`llm-call-sites.md`](llm-call-sites.md), [`llm-usage-policy.md`](llm-usage-policy.md).

View File

@@ -0,0 +1,96 @@
# LLM usage policy (ORCH-118)
> **Нормативный durable-документ.** Формулирует принцип использования LLM в оркестраторе, критерии
> «keep vs replace» через **control-path-ось**, и нормативное **определение «avoidable LLM control
> path»**. Применяется ко **всем** будущим правкам control-path'ов. Сопутствующие артефакты —
> карта [`llm-call-sites.md`](llm-call-sites.md) и roadmap [`llm-determinization-roadmap.md`](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`**, а не «удобство на глаз».
<!-- ORCH-118-AVOIDABLE-DEFINITION-BLOCK:START -->
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-фолбэком только на не-деривируемую часть).
<!-- ORCH-118-AVOIDABLE-DEFINITION-BLOCK:END -->
**Поимённый целевой набор** (сверен с кодом, прибит тестами 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`](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.

View File

@@ -40,6 +40,22 @@
- Анти-регресс машинный: структурные тесты сканируют исполняемый код на боевые хост-литералы,
а документацию — на секретоподобные значения; находка рвёт CI.
## Где уместен LLM: карта вызовов и нормативная политика
Платформа держит **доказательную карту** всех мест, где поток управления потребляет суждение
LLM, и **нормативную политику** «LLM — только там, где нужно настоящее суждение». Карта разводит
три факта: консультация ≠ транспорт/слот; **control-path** (вердикт LLM ветвит поток управления)
**artifact-producer** (детерминированный гейт судит артефакт независимо); и деривируемость
вердикта из tool-сигналов. Путь называется **avoidable LLM control path**, когда он одновременно
control-path и его вердикт деривируем из exit-кодов (тогда консультацию можно заменить
детерминированным раннером). Поимённо: avoidable = `{tester, deployer}`; настоящее суждение
сохраняется у `{analyst, architect, developer, reviewer}`. Карта — снимок, прибитый структурными
анти-дрейф тестами; реализация замен — отдельные follow-up'ы по роли.
- Карта вызовов LLM: [`../architecture/llm-call-sites.md`](../architecture/llm-call-sites.md)
- Нормативная политика: [`../architecture/llm-usage-policy.md`](../architecture/llm-usage-policy.md)
- Порядок замен: [`../architecture/llm-determinization-roadmap.md`](../architecture/llm-determinization-roadmap.md)
## Self-hosting-страховки
Платформа дорабатывает сама себя тем же конвейером — прод-инстанс при этом обслуживает и