architect(ET): auto-commit from architect run_id=394
This commit is contained in:
145
docs/work-items/ORCH-074/06-adr/ADR-001-model-name-validation.md
Normal file
145
docs/work-items/ORCH-074/06-adr/ADR-001-model-name-validation.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# ADR-001: Убрать мёртвый frontmatter `model:` + валидация имени модели через формат-чек `claude-*`
|
||||
|
||||
Work Item ID: ORCH-074
|
||||
Эпик: ORCH-052 (слой 3), под-задача ORCH-52a
|
||||
Связан с: ORCH-041 (каркас `resolve_agent_model`/`resolve_agent_effort`), `src/config.py`, `src/agents/launcher.py`
|
||||
|
||||
## Статус
|
||||
Accepted
|
||||
|
||||
## Контекст
|
||||
|
||||
Каркас выбора модели агентов (ORCH-041) работает корректно: `launcher.resolve_agent_model(agent, project_id)`
|
||||
резолвит модель по приоритету project-override → `ORCH_AGENT_MODEL_<AGENT>` → `agent_model_default`
|
||||
→ CLI-дефолт. Все 6 агентов резолвятся в `claude-opus-4-8` (через `agent_model_default`).
|
||||
|
||||
Аудит кода (08.06) выявил два дефекта **данных/валидации** (не дефект механизма):
|
||||
|
||||
- **P1 — лживый/мёртвый `model:` во frontmatter.** Все 6 промптов `.openclaw/agents/*.md`
|
||||
содержат `model:` (`claude-sonnet-4-6` у analyst/developer/tester/deployer, `claude-opus-4-7`
|
||||
у architect/reviewer). launcher **не читает** frontmatter `model:` — это мёртвая декларация,
|
||||
которая лжёт о реально используемой модели и нарушает принцип «документация = golden source».
|
||||
Мина: если кто-то «починит» launcher читать frontmatter → все агенты молча уедут на устаревшие
|
||||
модели.
|
||||
- **P2 — нет валидации имени модели.** В отличие от effort (`VALID_EFFORTS`-гард в
|
||||
`resolve_agent_effort`), имя модели не валидируется. Опечатка в `agent_model_*` / project-override
|
||||
→ `--model <мусор>` → CLI падает или тихо деградирует. Нарушение принципа never-break.
|
||||
|
||||
Скоуп зафиксирован стейкхолдером (Слава, 08.06): **G1 + G2 + опц. G4. G3 routing НЕ включаем
|
||||
(все 6 агентов остаются `claude-opus-4-8`). Эффорт не трогаем.** rev.2 BRD подтвердила скоуп
|
||||
без изменений. Код-факт (TRZ §4): `agent_fallback_model` читается напрямую на `launcher.py:374`,
|
||||
минуя `resolve_agent_model`.
|
||||
|
||||
Архитектор должен зафиксировать три решения: (а) форма G1, (б) предикат валидации G2,
|
||||
(в) судьба G4 (fallback) и G3 (routing).
|
||||
|
||||
## Решение
|
||||
|
||||
### Решение 1 (G1): убрать `model:` из frontmatter, НЕ учить launcher его читать
|
||||
|
||||
Из YAML-frontmatter всех 6 файлов `.openclaw/agents/*.md` удаляется **только** строка `model: …`.
|
||||
Ключи `name`/`description`/`tools` сохраняются; frontmatter остаётся валидным YAML и **описательным**.
|
||||
config (`agent_model_*` / `agent_model_default`) остаётся **единственным источником правды** о модели.
|
||||
|
||||
Отвергнутая альтернатива — научить launcher читать frontmatter `model:` — отвергнута: она вводит
|
||||
второй источник правды (frontmatter ⊕ config), усложняет резолв, и моментально активировала бы
|
||||
устаревшие значения (sonnet-4-6 / opus-4-7) для всех агентов. «Убрать» проще, безопаснее и
|
||||
устраняет мину раз и навсегда.
|
||||
|
||||
### Решение 2 (G2): предикат валидации — формат-чек `claude-*`, оформленный отдельным helper
|
||||
|
||||
Добавляется **чистый helper** `is_valid_model(name: str) -> bool` рядом с `VALID_EFFORTS` в
|
||||
`src/agents/launcher.py`. Предикат — **формат-чек**, а не allowlist имён:
|
||||
|
||||
```
|
||||
strip → непустая строка → соответствует ^claude-[a-z0-9.-]+$
|
||||
```
|
||||
|
||||
То есть: имя после `strip()` непусто, начинается с `claude-` и состоит только из строчных
|
||||
букв/цифр/точек/дефисов. Регэксп оформляется модульной константой (напр. `_MODEL_NAME_RE`).
|
||||
|
||||
**Почему формат-чек, а не allowlist `VALID_MODELS`:**
|
||||
allowlist (по образцу `VALID_EFFORTS`) воссоздаёт ровно ту мину, которую мы убиваем в G1 — статичный
|
||||
список имён, который **врёт при устаревании**. Когда Anthropic выпустит `claude-opus-4-9`, оператор,
|
||||
корректно прописавший новую модель, получит её молчаливый дроп на устаревший default (never-break
|
||||
сработает против пользователя). Это хуже, чем пропустить структурно-корректное, но опечатанное имя:
|
||||
финальный авторитет о существовании модели — сам Claude CLI, а не наш код. Формат-чек
|
||||
**forward-compatible** (новые версии проходят без правки кода) и ловит реальные классы отказов:
|
||||
чужой провайдер (`gpt-4`), пустая строка/пробелы, мусор с недопустимыми символами, неверный префикс
|
||||
(`claud-opus-typo`). Признанное ограничение: формат-чек НЕ ловит опечатку, которая всё ещё выглядит
|
||||
как валидное claude-имя (`claude-opus-typo`) — такие отсекает CLI на запуске (контракт never-break
|
||||
+ exit-code обработка в `_monitor_agent` это покрывают). Задача валидатора — не быть реестром моделей,
|
||||
а не дать **структурному мусору** уехать в `--model`.
|
||||
|
||||
**Применение (контракт never-break):**
|
||||
- В `resolve_agent_model`: резолвенное имя валидируется **перед возвратом**. Невалидное →
|
||||
`logger.warning(...)` + откат на следующий валидный уровень. Реализация: helper применяется внутри
|
||||
каскада приоритетов так, что невалидный уровень пропускается (project-override невалиден → пробуем
|
||||
env → default), а если итог всё равно невалиден → возврат `""` (без флага `--model`, CLI-дефолт).
|
||||
**Никогда** не возвращается мусор и **никогда** не бросается исключение.
|
||||
- Контракт уровней резолва ORCH-041 сохраняется: валидация добавляется **поверх**, порядок приоритетов
|
||||
и сигнатуры не меняются. Все ныне используемые валидные имена (`claude-opus-4-8`, валидный enduro
|
||||
per-project override) проходят без изменения поведения.
|
||||
- Поведенческая аналогия с `resolve_agent_effort` (`VALID_EFFORTS`): валидный → как есть, невалидный →
|
||||
лог + дроп. Разница только в форме предиката (формат-чек vs множество) по причинам выше.
|
||||
|
||||
### Решение 3 (G4): fallback НЕ включаем; но валидатор применяем к точке чтения fallback
|
||||
|
||||
`agent_fallback_model` остаётся `""` (флаг `--fallback-model` не прокидывается). **AC-5 помечается
|
||||
N/A.** Обоснование отказа:
|
||||
- G3 выключен ради **детерминизма**: все агенты на `claude-opus-4-8`. Fallback вернул бы скрытую
|
||||
вариативность модели под нагрузкой (агент молча отработал бы на другой модели) — это противоречит
|
||||
духу зафиксированного скоупа.
|
||||
- Нет наблюдаемой проблемы доступности, мотивирующей fallback. Принцип минимального изменения.
|
||||
- Self-hosting: новое рантайм-поведение под нагрузкой трудно наблюдать; не вводим без нужды.
|
||||
|
||||
**При этом** helper `is_valid_model` применяется ТАКЖЕ на месте чтения fallback (`launcher.py:374`,
|
||||
`fb = settings.agent_fallback_model`) — **независимо** от того, что значение сейчас пустое. Причина —
|
||||
код-факт TRZ §4: fallback читается напрямую, мимо `resolve_agent_model`, поэтому валидация только
|
||||
внутри резолва его НЕ покрывает. Защитный гард на месте чтения навсегда закрывает дыру never-break:
|
||||
если кто-то позже задаст `ORCH_AGENT_FALLBACK_MODEL` с опечаткой, мусор будет залогирован и
|
||||
сброшен (`fb_flag = ""`), а не уедет в `--fallback-model`. Для текущего пустого значения регрессии нет:
|
||||
`is_valid_model("") == False` → `fb_flag = ""` — то же поведение, что и сейчас (`if fb`). Это делает
|
||||
**TC-11** проверяемым (мусорный fallback дропается) при выключенном G4.
|
||||
|
||||
### Решение 4 (G3): routing НЕ включаем
|
||||
|
||||
Подтверждается отказ от model-routing как осознанное решение стейкхолдера (Слава, 08.06). Все 6
|
||||
агентов резолвятся в `claude-opus-4-8`. **AC-4 = N/A.**
|
||||
|
||||
## Размещение и форма (для разработчика)
|
||||
|
||||
- `is_valid_model(name)` + `_MODEL_NAME_RE` — в `src/agents/launcher.py` рядом с `VALID_EFFORTS`
|
||||
(один валидатор, два места вызова: резолв модели и чтение fallback — оба в этом модуле, без
|
||||
кросс-модульного импорта).
|
||||
- Префикс `claude-` хардкодится в launcher: оркестратор привязан к Claude CLI (`CLAUDE_BIN`),
|
||||
конфигурировать предикат не нужно (не over-engineering). Каноничная версия модели по-прежнему
|
||||
живёт ТОЛЬКО в `config.py::agent_model_default` — в launcher версия не хардкодится.
|
||||
- frontmatter: удалить только `model:`-строку; не вносить генератор, возвращающий её обратно.
|
||||
|
||||
## Последствия
|
||||
|
||||
**Плюсы:**
|
||||
- frontmatter перестаёт лгать; config — единственный источник правды о модели (golden source цел).
|
||||
- Опечатка/чужой провайдер/мусор в имени модели больше не роняет и не деградирует запуск агента
|
||||
(never-break соблюдён в обеих точках: резолв и fallback).
|
||||
- Forward-compatible: будущие модели Claude не требуют правки кода (в отличие от allowlist).
|
||||
- Минимальное изменение: механизм ORCH-041, API, схема БД, структура CLI-команды не меняются.
|
||||
|
||||
**Минусы / ограничения:**
|
||||
- Формат-чек пропускает структурно-валидную опечатку вида `claude-opus-typo` (отсекается CLI на
|
||||
запуске + never-break обработкой exit-code). Принятый компромисс ради forward-compat.
|
||||
- Префикс `claude-` зашит — при гипотетической смене CLI-провайдера потребуется правка (приемлемо:
|
||||
оркестратор Claude-специфичен по дизайну).
|
||||
|
||||
**Не затрагивается:**
|
||||
- API (HTTP) — нет. Схема БД — нет миграций. Стадии/QG — без изменений (это runtime-гард в launcher,
|
||||
не quality-gate). Топология/инфра — без изменений (07/08 артефакты не требуются).
|
||||
- Эффорт (`agent_effort_*`) и `VALID_EFFORTS`-гард — не трогаются (регрессия покрыта TC-10).
|
||||
- enduro per-project override — валидные имена проходят без изменения поведения (AC-8 / TC-08).
|
||||
|
||||
## Соответствие принципам
|
||||
|
||||
Всё в Docker / один сервер — да. Минимум зависимостей — новых нет. Без ORM/очередей/облака — да.
|
||||
Self-hosting: изменение применяется к БУДУЩИМ запускам агентов, прод-контейнер не перезапускается
|
||||
в рамках задачи; прод-деплой орка — только через staging-гейт (8501) и Plane-статус «Confirm Deploy».
|
||||
23
docs/work-items/ORCH-074/10-tech-risks.md
Normal file
23
docs/work-items/ORCH-074/10-tech-risks.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Технические риски — ORCH-074
|
||||
|
||||
Work Item ID: ORCH-074
|
||||
Связан с: ADR-001 (`06-adr/ADR-001-model-name-validation.md`).
|
||||
|
||||
| ID | Риск | Вероятность | Влияние | Митигация |
|
||||
|----|------|-------------|---------|-----------|
|
||||
| R-1 | **Валидация роняет запуск агента** (исключение вместо graceful-деградации) — нарушение never-break, встал бы конвейер всех проектов. | Низкая | Высокое | Helper `is_valid_model` — чистый предикат без исключений; невалидное → `logger.warning` + откат на default/`""`. Покрыто TC-03..TC-05, TC-10. |
|
||||
| R-2 | **Fallback обходит валидацию** (код-факт: `launcher.py:374` читает `agent_fallback_model` напрямую, мимо `resolve_agent_model`). | Средняя (если позже зададут fallback) | Среднее | ADR-001 решение 3: один helper применяется ТАКЖЕ на месте чтения fallback. Мусорный fallback дропается с warning. Покрыто TC-11. |
|
||||
| R-3 | **Регрессия enduro per-project override** — валидация ломает корректный не-self override (общий инстанс/БД/очередь). | Низкая | Высокое | Валидные claude-имена проходят формат-чек без изменения поведения; механизм приоритетов ORCH-041 не меняется. Покрыто TC-08. |
|
||||
| R-4 | **Формат-чек пропускает структурную опечатку** вида `claude-opus-typo` (валидный префикс, несуществующая модель). | Средняя | Низкое | Принятый компромисс (ADR-001): финальный авторитет — CLI; never-break + обработка exit-code в `_monitor_agent` покрывают отказ запуска. Allowlist отвергнут как воссоздающий мину устаревания (G1). |
|
||||
| R-5 | **frontmatter-генератор возвращает `model:` обратно** → мина P1 оживает. | Низкая | Среднее | Проверить отсутствие автогенератора, возвращающего `model:`; frontmatter остаётся описательным. Покрыто TC-01/TC-02 (CI-гард на отсутствие `^model:`). |
|
||||
| R-6 | **Хардкод версии модели в launcher** при добавлении валидации. | Низкая | Среднее | Префикс `claude-` зашит осознанно (CLI-специфика); каноничная ВЕРСИЯ остаётся только в `config.py::agent_model_default`. Регэксп версию не фиксирует. |
|
||||
| R-7 | **Self-hosting деплой** — рестарт прод-контейнера встанет конвейер всех проектов (enduro). | — | Высокое | Изменение применяется к будущим запускам; прод-деплой только через staging-гейт (8501) и Plane-статус «Confirm Deploy». Без немедленного рестарта прода. |
|
||||
|
||||
## Инварианты (должны держаться после изменения)
|
||||
|
||||
1. **never-break**: невалидная модель/эффорт/fallback НЕ роняет запуск агента — деградация на
|
||||
default/CLI-дефолт + лог.
|
||||
2. **Один источник правды о модели**: config (`agent_model_*`); frontmatter — описательный.
|
||||
3. **Обратная совместимость ORCH-041**: все валидные имена (`claude-opus-4-8`, enduro override)
|
||||
резолвятся без изменения поведения; порядок приоритетов и сигнатуры не меняются.
|
||||
4. **Детерминизм**: все 6 агентов = `claude-opus-4-8` (G3/routing выключен, G4/fallback выключен).
|
||||
Reference in New Issue
Block a user