144 lines
14 KiB
Markdown
144 lines
14 KiB
Markdown
# 02 — Техническое задание (ТЗ)
|
||
|
||
**Work Item:** ORCH-044
|
||
**Основано на:** 01-brd.md
|
||
|
||
> Примечание: ТЗ фиксирует **что** должно измениться и **наблюдаемое поведение**.
|
||
> Выбор конкретной реализации (например, формат проверки `.credentials.json` vs парсинг
|
||
> маркера в логе) — за архитектором (стадия architecture, ADR). Где описаны варианты —
|
||
> это границы допустимого решения, а не предписание.
|
||
|
||
> ## ⛔ КОРРЕКЦИЯ SCOPE ВЛАДЕЛЬЦЕМ (Слава, 06.06) — ЧИТАТЬ ПЕРВЫМ
|
||
>
|
||
> **P2 (`--effort`) ПОЛНОСТЬЮ ИСКЛЮЧЁН из этой задачи.** Решение владельца:
|
||
> - effort **НУЖЕН и работает** — его **НЕЛЬЗЯ** убирать как unsupported. **Вариант B запрещён.**
|
||
> - В ORCH-044 **НЕ трогать** `--effort`: ни `_spawn` effort_flag, ни `resolve_agent_effort`, ни дефолты `agent_effort_*` в `config.py`, ни ORCH-41 effort-доки.
|
||
> - Текущий прод-хотфикс `ORCH_AGENT_EFFORT_*=""` в `.env` **оставить как есть** — не снимать, не менять.
|
||
> - Полноценный возврат effort (расследование флагов + json) вынесен в **ОТДЕЛЬНУЮ задачу ORCH-50** («Эффорт агентов: заставить --effort работать с --print/json»). Туда же — любое расследование причины пустого stdout.
|
||
>
|
||
> **Архитектор/дев игнорируют все TR-2.x и AC-7/AC-8/AC-9, относящиеся к effort.** Реализуем ТОЛЬКО:
|
||
> - **P1** — preflight ловит auth (ОБА подхода: проактивно cred-файл `expiresAt` + постфактум маркер `Not logged in`);
|
||
> - **P3** — пустой лог / нет result-JSON ⇒ job `failed` (не `done`, не вечный `running`).
|
||
>
|
||
> Заголовок задачи содержит «--effort фикс» по историческим причинам — это НЕ часть scope. Effort = ORCH-50.
|
||
|
||
## 1. Задействованные модули `src/`
|
||
| Модуль | Текущее место | Изменение |
|
||
|--------|---------------|-----------|
|
||
| `src/preflight.py` | `_run_version`, `_compute`, `check` | Добавить дешёвую token-free проверку авторизации (P1) |
|
||
| `src/config.py` | блок ORCH-41 effort (стр. 98–108), новый блок настроек preflight-auth | Настройки auth-проверки; решение по effort-дефолтам (P2) |
|
||
| `src/agents/launcher.py` | `_spawn` (effort_flag, стр. 290–292, 303–311), `_monitor_agent` (стр. 460–615), `_finalize_job` (стр. 630–667) | Решение по `--effort` (P2); детекция пустого лога / отсутствия result-JSON (P3) |
|
||
| `src/queue_worker.py` | `_drain_once` claim-gating (стр. 158–165) | Учесть новый auth-fail preflight в гейтинге клейма (P1) — при необходимости |
|
||
| `src/db.py` | `mark_job` | Использование существующего перевода job → `failed` (P3); новых колонок не требуется |
|
||
|
||
Новых файлов модулей не предполагается обязательно; допускается выделение хелпера
|
||
(например, `_check_auth()` в `preflight.py`) — на усмотрение архитектора.
|
||
|
||
## 2. Требования по проблемам
|
||
|
||
### P1 — Preflight ловит авторизацию (token-free)
|
||
- **TR-1.1.** Preflight ДОЛЖЕН, помимо `os.path.exists(bin)` и `claude --version`, выполнять
|
||
**дешёвую проверку авторизации без обращения к API модели и без prompt-ping**.
|
||
- **TR-1.2.** Допустимые подходы (выбор — за архитектором, ADR):
|
||
- (a) Проверка существования и читаемости файла учётных данных
|
||
`~/.claude/.credentials.json` (HOME агента — `/home/slin`, см. launcher env, стр. 326)
|
||
и валидности OAuth-токена по дате истечения внутри
|
||
(`claudeAiOauth.expiresAt`, epoch ms) — `expiresAt <= now` ⇒ протух ⇒ fail;
|
||
- (b) Парсинг реального run-вывода на маркер `Not logged in` (и подобные) с переводом
|
||
job в провал и размыканием/учётом circuit breaker.
|
||
- Подход (a) предпочтителен как **проактивный** (ловит ДО клейма); (b) — как защитная
|
||
сетка постфактум. Допускается комбинация.
|
||
- **TR-1.3.** Путь к файлу учётных данных ДОЛЖЕН резолвиться согласованно с тем HOME,
|
||
под которым launcher реально спавнит claude (`/home/slin`), а не из окружения процесса
|
||
оркестратора (аналогично тому, как `_claude_bin()` следует за реально исполняемым путём).
|
||
- **TR-1.4.** Результат auth-проверки кешируется тем же механизмом, что и version-check
|
||
(`preflight_cache_ttl`), чтобы не читать файл на каждый тик воркера.
|
||
- **TR-1.5.** При `auth=fail`: `check()` возвращает `(False, reason)` с **информативным
|
||
reason** (например, `claude not logged in: credentials missing` / `OAuth token expired at
|
||
<iso>`). Job НЕ клеймится (поведение `_drain_once` уже корректно при `ok=False`).
|
||
- **TR-1.6.** Граница ответственности: preflight остаётся **локальным** (BR-1). Сетевая
|
||
валидация токена у провайдера — вне объёма.
|
||
- **TR-1.7.** Поведение при «всё хорошо» не меняется: залогинен + валидный токен ⇒ `ok=True`.
|
||
|
||
### P2 — Решение по `--effort`
|
||
- **TR-2.1.** Провести расследование (стадия architecture/development): причина пустого
|
||
stdout при `--effort` + `--print --output-format json` в CLI 2.1.142 — несовместимость
|
||
с json-форматом, иной синтаксис флага, или баг CLI. Зафиксировать вывод в ADR/`10-tech-risks.md`.
|
||
- **TR-2.2.** По итогам выбрать **ровно один** исход и привести к нему код+доки+дефолты:
|
||
- **Вариант A (вернуть effort):** найден корректный способ (например, иной синтаксис или
|
||
несовместимость только с конкретным output-format) — `--effort` снова формируется в
|
||
`_spawn` корректно; прод-хотфикс `ORCH_AGENT_EFFORT_*=""` снимается; добавить
|
||
регресс-тест, что вывод не пустой.
|
||
- **Вариант B (unsupported):** effort несовместим — **убрать `--effort` из активного пути
|
||
запуска** (`_spawn` не формирует `effort_flag`), убрать/нейтрализовать дефолты effort в
|
||
`config.py`, обновить ORCH-41-доки (INFRA.md, internals.md) пометив фичу как unsupported
|
||
на данной версии CLI. `resolve_agent_effort` либо удаляется, либо документированно
|
||
оставляется заглушкой (решение — ADR).
|
||
- **TR-2.3.** Независимо от A/B: **не должно остаться «мёртвого» флага**, который тихо гасит
|
||
вывод. После задачи запуск с дефолтной конфигурацией прода ДОЛЖЕН давать непустой
|
||
result-JSON.
|
||
- **TR-2.4.** Изменение дефолтов/удаление флага не должно ломать `resolve_agent_model`
|
||
(модель — независимая фича ORCH-41) и существующие тесты `test_resolve_agent_effort.py`
|
||
(их допустимо обновить под новый контракт).
|
||
|
||
### P3 — Пустой лог / нет result-JSON ⇒ провал
|
||
- **TR-3.1.** В `_monitor_agent`/`_finalize_job`: при `exit_code == 0` ДОЛЖНА выполняться
|
||
**проверка валидности результата** перед тем как считать job успешным:
|
||
- run-лог **непустой** (размер > 0 и/или содержит непустой текст), И
|
||
- из него извлекается **валидный result-JSON** (тот же контракт, что использует
|
||
`usage._extract_last_json_object` / `parse_usage_from_log`).
|
||
- **TR-3.2.** Если результат невалиден (пустой лог ИЛИ нет валидного JSON) при `exit_code==0`,
|
||
job ДОЛЖЕН трактоваться как **провал**:
|
||
- НЕ переводиться в `done`;
|
||
- попасть в путь ретрая/провала (`attempts < max_attempts` ⇒ requeue, иначе `failed`),
|
||
аналогично permanent-ветке `_finalize_permanent`, с информативным `error`
|
||
(например, `empty run log / no result JSON (run_id=...)`);
|
||
- сгенерировать алерт (Telegram), как прочие провалы;
|
||
- НЕ выполнять авто-advance стадии (`_try_advance_stage`) и НЕ постить «успешный»
|
||
status-коммент.
|
||
- **TR-3.3.** Классификация такого провала: по умолчанию — **permanent** (это не 429/overload).
|
||
Если в логе присутствует transient-маркер (через `error_classifier`) — допускается
|
||
transient-путь. Auth-провал (`Not logged in`) — на усмотрение архитектора: может
|
||
маршрутизироваться как сигнал брейкеру (P1/TR-1.2b).
|
||
- **TR-3.4.** Никогда не оставлять job в `running` навечно из-за пустого результата: либо
|
||
`done` (валидно), либо `failed`/`queued`(retry). (Watchdog ORCH-7 продолжает закрывать
|
||
случай таймаута; здесь закрывается случай «быстрая смерть с exit 0».)
|
||
- **TR-3.5.** Защитность: вся проверка обёрнута так, что её собственная ошибка не роняет
|
||
монитор (как и прочий код `_monitor_agent`); при сомнении — fail-safe в сторону провала job.
|
||
|
||
## 3. Изменения API
|
||
Нет новых/изменённых HTTP-endpoint'ов. Допускается обогащение поля `preflight_reason` в
|
||
`/queue` (через существующий `worker.status()` / `QueueWorker.last_preflight_reason`) более
|
||
информативным auth-сообщением — без изменения схемы ответа.
|
||
|
||
## 4. Изменения схемы БД
|
||
Нет. Используются существующие колонки `jobs` (`status`, `error`, `attempts`,
|
||
`max_attempts`, `transient_attempts`) и `agent_runs`. Новых таблиц/колонок не требуется.
|
||
|
||
## 5. Требования к новым QG checks
|
||
Новых Quality Gate проверок не требуется — изменения в слое запуска/preflight, не в гейтах
|
||
стадий. Реестр `QG_CHECKS` не меняется.
|
||
|
||
## 6. Конфигурация (env / config.py)
|
||
- Возможные новые настройки preflight-auth (имена — на усмотрение архитектора), например:
|
||
- `ORCH_PREFLIGHT_CHECK_AUTH` (bool, default true) — включение auth-проверки;
|
||
- путь к credentials, если не выводится из HOME автоматически.
|
||
- Решение по effort-дефолтам (`agent_effort_*`) согласно TR-2.2 (нейтрализовать при варианте B).
|
||
- Все новые настройки документируются в `config.py` docstring и в INFRA.md (env-карта).
|
||
|
||
## 7. Артефакты pipeline (обязательны к созданию/обновлению)
|
||
- `06-adr/ADR-NNN-*.md` — решение по подходу preflight-auth (a/b/комбо) и по effort (A/B).
|
||
- `10-tech-risks.md` — риск ложноположительной auth-проверки, риск регрессии effort, риск
|
||
fail-safe-провала на легитимных пустых выводах.
|
||
- `12-review.md`, `13-test-report.md` — по стадиям.
|
||
- Обновить `docs/operations/INFRA.md` и `docs/architecture/internals.md` (effort-секции),
|
||
`CHANGELOG.md`. Документация = golden source (правило агентов №2).
|
||
|
||
## 8. Ограничения и запреты
|
||
- ❌ Prompt-ping в preflight (жжёт rate limit) — запрещено (BR-1, комментарий в preflight.py).
|
||
- ❌ Сетевые вызовы к API модели в preflight.
|
||
- ❌ Оставлять job в `running` без таймаута при пустом результате.
|
||
- ❌ `--no-verify`/обход хуков без одобрения Owner.
|
||
- ⚠️ Self-hosting: не ронять прод-контейнер `orchestrator`; проверка изменений — через
|
||
staging (8501) перед прод-деплоем (см. CLAUDE.md, INFRA.md).
|