architect(ET): auto-commit from architect run_id=727
All checks were successful
CI / test (push) Successful in 1m7s

This commit is contained in:
2026-06-15 23:56:27 +03:00
parent 70171eb1c1
commit 7597804f8c
4 changed files with 480 additions and 0 deletions

View File

@@ -416,6 +416,34 @@ ORCH-079 синхронизирует витрину с кодом и закры
- ADR: [adr-0023](adr/adr-0023-overview-docs-reviewer-axis-and-epic52-close.md); детально —
`docs/work-items/ORCH-079/06-adr/ADR-001-readme-sync-and-reviewer-overview-docs-axis.md`.
#### Карта LLM-консультаций + политика использования LLM (ORCH-118 — design)
Зонтичный follow-up RCA-трека ORCH-114/117: оркестратор не имел нормативного критерия «где LLM нужен,
а где это avoidable control path» и карты мест вызова LLM, прибитой к коду. ORCH-118 — **inventory +
карта + roadmap + политика + структурные тесты** (реализация детерминированных раннеров — follow-up'ы
**по роли**, без выдуманных Plane-ID). Это **docs + tests only**: `STAGE_TRANSITIONS` / реестр и имена
`QG_CHECKS`/`check_*` / machine-verdict-ключи / схема БД — **байт-в-байт не тронуты**; kill-switch не
нужен (нет рантайм-поведения), как ORCH-077/079/101/102/103/011.
- **Три ортогональных оси (ground-truth — код):** (1) consultation ≠ transport/slot (единственный
транспорт LLM-консультации в `src/**` — `launcher._spawn`, `launcher.py:472/610-614`; иного нет;
D1/D2 `deploy-finalizer`/`post-deploy-monitor` занимают слот, но перехватываются в `launch_job` до
`_spawn`, `launcher.py:389/394` — консультации нет); (2) **control-path (C) ≠ artifact-producer (P)**
по коду-потребителю в `src/qg/checks.py` (C: `check_*` ветвится на LLM-вердикте; P: детерминированный
гейт судит артефакт независимо — файлы/CI); (3) деривируемость вердикта из tool-сигналов.
- **Нормативное определение** «avoidable LLM control path» = двухбитный предикат: C-консультация **И**
вердикт деривируем из tool-сигналов. Целевой набор (поимённо, доказательно): **avoidable =
{tester, deployer}**; control-path-но-keep = `{reviewer}`; не-control-path (P, keep) =
`{analyst, architect, developer}`; уже детерминированы = `{deploy-finalizer, post-deploy-monitor}`.
- **Документы (durable, `docs/architecture/`):** `llm-call-sites.md` (карта + control-path-разметка +
классификация, снимок, прибитый тестами), `llm-determinization-roadmap.md` (порядок замен; первый
срез — **deployer staging-status**, чистый маппинг exit-кода `staging_check.py`; прод уже
детерминирован Phase A/B/C ORCH-036), `llm-usage-policy.md` (нормативный принцип «LLM — только где
нужно настоящее суждение»). Анти-дрейф — `tests/test_llm_call_site_inventory.py` (offline; включая
control-path-инвариант сверки с `src/qg/checks.py` и фиксацию avoidable-набора).
- **Норматив сопровождения:** менял места вызова LLM **или** потребителя вердикта в `src/qg/checks.py`
→ обнови карту/разметку и политику в том же PR.
- ADR: [adr-0047](adr/adr-0047-llm-usage-policy-and-call-site-map.md); детально —
`docs/work-items/ORCH-118/06-adr/ADR-001-llm-call-site-map-and-determinization-roadmap.md`.
### Модель и эффорт по ролям (ORCH-41, валидация ORCH-74)
Модель и `--effort` каждого агента берутся из config (`src/config.py`), резолвятся `launcher.resolve_agent_model` / `resolve_agent_effort` по приоритету **project-override (`projects_json` `agent_models`/`agent_efforts`) > `ORCH_AGENT_MODEL_<AGENT>`/`ORCH_AGENT_EFFORT_<AGENT>` > `*_default` > CLI-дефолт (без флага)**. **Эффорт (ORCH-081):** ниже `*_default` добавлен непустой **per-role floor** — class-default поля `agent_effort_<role>` из `config.py` (его пустой env перебить не может). Floor — строго последний уровень (ниже default) и срабатывает ТОЛЬКО когда все уровни пусты, поэтому пустые прод-`ORCH_AGENT_EFFORT_*=` (которые pydantic трактует как явное `''` и обнуляют дефолт) больше не приводят к запуску без `--effort`: каждая роль получает свой канонический пол (developer=`xhigh`, tester/deployer=`medium`, прочие=`high`). Непустой явный конфиг по-прежнему побеждает floor; опечатка вне `VALID_EFFORTS` дропается валидацией ДО floor (never-break, не маскируется). См. `docs/work-items/ORCH-081/06-adr/ADR-001-effort-resolution-floor.md`. frontmatter `model:` в `.openclaw/agents/*.md` **удалён** (ORCH-74 G1) — он был мёртвой/лживой декларацией (launcher его не читает); config — единственный источник правды о модели. Model-routing (G3) НЕ включён — все 6 агентов на `claude-opus-4-8`.

View File

@@ -0,0 +1,114 @@
---
work_item: ORCH-118
stage: architecture
author_agent: architect
status: accepted
created_at: 2026-06-15
model_used: claude-opus-4-8
---
# adr-0047: Нормативная политика использования LLM + карта call-site'ов (control-path-ось «avoidable»)
> **Сквозной (cross-cutting) ADR.** Агрегирует решение ORCH-118, влияющее на **весь** оркестратор:
> нормативная политика использования LLM, три ортогональных оси, определение «avoidable LLM control
> path» и снимок-карта LLM-консультаций, прибитая к коду структурными тестами. Локальная детализация —
> `docs/work-items/ORCH-118/06-adr/ADR-001-llm-call-site-map-and-determinization-roadmap.md`.
## Статус
Accepted
## Контекст
RCA-цепочка ORCH-114/117 (и 110/111/112/113) показала корневой класс: у side-effectful и решающих
control-path'ов не было единого детерминированного владения; местами решение брал LLM-агент «потому
что удобно», хотя по сути это исполнение фиксированных команд + маппинг результата — лишний
недетерминизм, задержка и расход токенов в точке ветвления.
Оркестратор не имел **нормативного критерия** «где LLM нужен, а где это avoidable control path» и
**карты** мест вызова LLM, прибитой к коду. Без них любая будущая правка control-path'а могла снова
ввести LLM «на удобстве», а «вслепую» убирать LLM нельзя — часть путей несёт настоящее суждение
(анализ, архитектура, написание кода, ревью).
**Ground-truth кода (ORCH-118, сверено):** единственный транспорт LLM-консультации в `src/**`
`launcher._spawn` (`launcher.py:472`, CLI `610-614`); иного LLM-транспорта нет (нет SDK-импортов /
прямого HTTP Anthropic / второго сборщика). 6 ролей-агентов консультируют через него; D1/D2
(`deploy-finalizer`/`post-deploy-monitor`) перехватываются в `launch_job` **до** `_spawn`
(`launcher.py:389/394`) — слот есть, консультации нет. Потребитель вывода каждой роли — конкретный
`check_*`/`_parse_*` в `src/qg/checks.py`.
## Решение
### D1 — Три ортогональных оси (нормативно для всего оркестратора)
1. **consultation ≠ transport/slot** — «потребляет суждение LLM» ≠ «спавнит процесс / занимает слот
агента» (capability ≠ consultation).
2. **control-path (C) ≠ artifact-producer (P)** — определяется кодом-потребителем: C — `check_*`
ветвится на machine-verdict, написанном LLM; P — детерминированный гейт судит артефакт независимо
(файлы/CI).
3. **деривируемость вердикта** — вердикт C-консультации либо детерминированная функция tool-сигналов
(exit-code `pytest`/smoke/`staging_check.py`/деплоя), либо настоящее суждение.
### D2 — Нормативное определение «avoidable LLM control path»
> Call-site — **avoidable LLM control path** ⟺ **(i)** C-консультация (LLM-вердикт потребляется
> потоком управления) **И (ii)** вердикт деривируем из tool-сигналов, которые оркестратор уже
> вычисляет → LLM не добавляет информации.
Целевой набор (доказательно из `src/qg/checks.py`): **avoidable = {tester, deployer}**;
control-path-но-keep = `{reviewer}`; не-control-path (P, keep) = `{analyst, architect, developer}`;
уже детерминированы (вне консультаций) = `{deploy-finalizer, post-deploy-monitor}`.
### D3 — Нормативная политика использования LLM (`docs/architecture/llm-usage-policy.md`)
Принцип: **«LLM — только там, где требуется настоящее суждение».** Критерий keep vs replace —
через оси D1 (является ли путь control path; деривируем ли вердикт; обратимость; влияние на
автономность NFR-2). **Требование:** любая новая/изменённая control-path-консультация обязана
обосновать использование LLM против этой политики; reviewer контролирует это как обзорную ось
(в духе ORCH-079) — **как требование, не как новый машинный гейт**.
### D4 — Карта как снимок, прибитый к коду
`docs/architecture/llm-call-sites.md` — инвентарь + control-path-разметка + классификация со
схемой полей и машинным блоком (детали — work-item ADR-001 D2/D4). Структурные тесты
`tests/test_llm_call_site_inventory.py` (offline) держат инварианты: транспорт-агностичный
двусторонний инвариант единственной точки, отсутствие консультации в детерминированных путях,
control-path-разметка сверена с `src/qg/checks.py`, avoidable-набор = `{tester, deployer}`.
### D5 — Roadmap детерминизации (`docs/architecture/llm-determinization-roadmap.md`)
Рекомендованный первый срез — **deployer (staging-status)** (`replace-deterministic-now`: чистый
маппинг exit-кода `staging_check.py`; прод уже детерминирован Phase A/B/C ORCH-036; опора на
прецедент D1/D2). Затем — **tester-гибрид** (`needs-hybrid-fallback`). Кандидаты — **по роли**,
без конкретных Plane-ID (NFR-6).
### D6 — Скоуп и инварианты (нормативно)
ORCH-118 — **docs + tests only**: `STAGE_TRANSITIONS` / реестр и имена `QG_CHECKS`/`check_*` /
machine-verdict-ключи / схема БД — **байт-в-байт не тронуты**; раннеры замен не реализуются;
follow-up Plane-ID не фиксируются. Self-hosting-безопасно (только чтение кода + запись docs/tests).
**Норматив сопровождения (durable):** менял места вызова LLM **или** потребителя вердикта в
`src/qg/checks.py` → обнови карту/разметку и политику в **том же PR** (иначе тесты D4 красные).
## Альтернативы
- **Машинный гейт-enforcement политики (новый QG)** — отвергнуто: политика нормативно-описательная,
как ось трассировки ORCH-078; новый QG увеличил бы поверхность риска без необходимости (FR-6 §QG).
- **Реализация раннеров в этой же задаче** — отвергнуто: inventory-first по требованию заказчика;
«вслепую» убирать LLM рискованно без утверждённой карты.
- **Привязка к конкретным follow-up ID** — отвергнуто (NFR-6, корень отклонённой R2).
## Последствия
- **+** Единый нормативный критерий и код-привязанная карта закрывают класс «LLM на удобстве» и
делают замены предсказуемыми; автономность защищена политикой.
- **** Карта — снимок: эволюция `src/qg/checks.py` требует со-обновления карты (держится тестами).
*Митигейшн:* запланированный норматив сопровождения, тест указывает точку дрейфа.
- **Откат:** удаление/правка `docs/architecture/llm-*.md` + тест-файла + секции README; рантайм не
затронут.
## Ссылки
- Work-item ADR: `docs/work-items/ORCH-118/06-adr/ADR-001-llm-call-site-map-and-determinization-roadmap.md`
- BRD/TRZ/AC: `docs/work-items/ORCH-118/{01-brd,02-trz,03-acceptance-criteria}.md`
- Сверено по коду: `src/agents/launcher.py`, `src/qg/checks.py`, `.openclaw/agents/*.md`
- Связанные: ORCH-036 (детерминированный self-deploy), ORCH-061 (`staging_verdict`),
ORCH-077/079 (docs/prompts-only прецедент + reviewer-ось обзорных доков), ORCH-114/117 (RCA-трек)
</content>