# 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 `). 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).