analyst(ET): auto-commit from analyst run_id=157
All checks were successful
CI / test (push) Successful in 13s
All checks were successful
CI / test (push) Successful in 13s
This commit is contained in:
90
docs/work-items/ORCH-044/01-brd.md
Normal file
90
docs/work-items/ORCH-044/01-brd.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 01 — Business Requirements Document (BRD)
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
**Title:** Надёжность запуска агента: preflight ловит auth+битый флаг, --effort фикс
|
||||
**Приоритет:** Высокий (надёжность конвейера)
|
||||
**Автор запроса:** Слава, 05.06 («почему перед стартом аналитика не прошла проверка?»)
|
||||
|
||||
## 1. Контекст и инцидент (05.06)
|
||||
Задача **ORCH-17** застряла на стадии Analysis ~30 минут. Аналитик-агент стартовал и
|
||||
мгновенно «умирал»: run-лог — **пустой файл (0 байт)**, а job в очереди оставался в
|
||||
состоянии `running` (вечное зависание без сигнала).
|
||||
|
||||
Корневые причины (две, наложились):
|
||||
1. **`claude` Not logged in** после ребилда контейнера — токен/сессия не поднялись.
|
||||
2. **Флаг `--effort`** в связке с `--print --output-format json` (CLI 2.1.142) **гасил весь
|
||||
stdout** — claude завершался с пустым выводом.
|
||||
|
||||
**Главная системная проблема:** preflight-проверка пропустила обе битые задачи в работу —
|
||||
она слепа к авторизации и не ловит «битый флаг → пустой вывод».
|
||||
|
||||
## 2. Проблема (как есть)
|
||||
- **P1. Дыра в preflight (главное).** `src/preflight.py` сознательно проверяет только
|
||||
(a) `os.path.exists(CLAUDE_BIN)` и (b) `claude --version` (timeout 5s, без токенов).
|
||||
Но `--version` отвечает успешно **даже когда claude НЕ залогинен** (версия — локальная
|
||||
информация). Итог: `preflight=ok`, а реальный запуск падает `Not logged in`. Preflight
|
||||
слеп к авторизации и пропускает заведомо нерабочие задачи в очередь.
|
||||
- **P2. `--effort` ломает вывод.** Флаг `--effort <low..max>` совместно с
|
||||
`--print`/`--output-format json` в CLI 2.1.142 даёт **пустой stdout** — агент молча
|
||||
умирает. Сейчас effort **отключён в проде** хотфиксом (`.env`: `ORCH_AGENT_EFFORT_*=""`),
|
||||
но дефолты в `src/config.py` всё ещё `high`/`medium`, а документация (INFRA.md,
|
||||
internals.md, ORCH-41) описывает effort как рабочую фичу. Несоответствие кода/доков/прода.
|
||||
- **P3. Пустой лог ≠ провал.** Агент с пустым run-логом (0 байт) и `exit 0` трактуется как
|
||||
**успех** (`_finalize_job` → `done`, авто-advance стадии) либо вечно висит `running`.
|
||||
Ни watchdog, ни ретрай не срабатывают. Нет сигнала об инциденте.
|
||||
|
||||
## 3. Бизнес-последствия
|
||||
- Любой сбой авторизации или несовместимости флага → **тихое зависание** задачи без алерта.
|
||||
- Блокируется конвейер **всех** проектов (общий инстанс/очередь, self-hosting) — как было с
|
||||
ORCH-17 (30 мин простоя, ручное вмешательство).
|
||||
- Деградация доверия к автономности оркестратора: «проверка перед стартом» не работает.
|
||||
|
||||
## 4. Цель
|
||||
Сделать запуск агента **отказоустойчивым по входу и по выходу**:
|
||||
1. Preflight ловит отсутствие/протухание авторизации **дёшево и без траты токенов** до того,
|
||||
как job будет заклеймлен.
|
||||
2. Разобраться с `--effort` и привести код/доки/прод к одному непротиворечивому состоянию.
|
||||
3. Пустой/невалидный результат запуска трактуется как **провал** (job → `failed`), чтобы
|
||||
сработали watchdog/ретрай и алерт, а не вечное зависание.
|
||||
|
||||
## 5. Заинтересованные стороны
|
||||
- **Owner/Слава** — инициатор, требует «проверки перед стартом».
|
||||
- **Все проекты на инстансе** (enduro-trails и self-hosting ORCH) — страдают от простоя.
|
||||
- **Агенты конвейера** — analyst/architect/... — все запускаются через единый launcher.
|
||||
|
||||
## 6. Объём (Scope)
|
||||
**В объёме:**
|
||||
- Дешёвая token-free проверка авторизации в preflight.
|
||||
- Расследование и решение по `--effort` (вернуть корректно ИЛИ задокументировать как
|
||||
unsupported и убрать из кода/дефолтов/доков).
|
||||
- Детекция «пустой лог / нет валидного result-JSON» как провала job с корректным
|
||||
переводом в `failed` и срабатыванием ретрая/алерта.
|
||||
- Обновление документации (INFRA.md / internals.md / CHANGELOG) в том же PR.
|
||||
|
||||
**Вне объёма:**
|
||||
- Prompt-ping (ping→pong) — **запрещено** (жжёт rate limit). Только локальные/дешёвые проверки.
|
||||
- Реформа circuit breaker / backoff-логики (используем существующие механизмы).
|
||||
- Изменение схемы стадий/конвейера.
|
||||
- Автоматический re-login claude (восстановление авторизации) — отдельная задача.
|
||||
|
||||
## 7. Бизнес-правила
|
||||
- BR-1: Preflight **не тратит токены** и не делает сетевых вызовов к API модели.
|
||||
- BR-2: Протухшая/нечитаемая авторизация → `preflight=fail` → job **не клеймится** (остаётся
|
||||
`queued`), пишется warning, при необходимости — алерт/брейкер.
|
||||
- BR-3: Пустой run-лог ИЛИ отсутствие валидного result-JSON при `exit 0` → job `failed`
|
||||
(никогда не `done` и не вечный `running`).
|
||||
- BR-4: Никаких `--no-verify`/обхода хуков без явного одобрения Owner.
|
||||
- BR-5: Код, дефолты `config.py`, прод `.env` и документация по `--effort` должны быть
|
||||
взаимно непротиворечивы после задачи.
|
||||
|
||||
## 8. Критерии успеха (бизнес-уровень)
|
||||
- Симуляция «не залогинен» → preflight ловит до клейма, job не стартует впустую.
|
||||
- Симуляция «пустой лог + exit 0» → job становится `failed`, срабатывает ретрай/алерт.
|
||||
- Состояние `--effort` однозначно: либо работает с json-форматом, либо удалён из активного
|
||||
пути и доков (без «мёртвого» флага в дефолтах).
|
||||
- Инцидент класса ORCH-17 больше не приводит к тихому 30-минутному зависанию.
|
||||
|
||||
## 9. Связанные материалы
|
||||
- `src/preflight.py`, `src/queue_worker.py`, `src/agents/launcher.py`, `src/config.py`
|
||||
- `docs/history/LESSONS_ORCH-017.md`, `docs/history/LESSONS_2026-06-05.md`
|
||||
- ORCH-41 (effort/model resolver), ORCH-1 (очередь/resilience), ORCH-7 (watchdog)
|
||||
129
docs/work-items/ORCH-044/02-trz.md
Normal file
129
docs/work-items/ORCH-044/02-trz.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# 02 — Техническое задание (ТЗ)
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
**Основано на:** 01-brd.md
|
||||
|
||||
> Примечание: ТЗ фиксирует **что** должно измениться и **наблюдаемое поведение**.
|
||||
> Выбор конкретной реализации (например, формат проверки `.credentials.json` vs парсинг
|
||||
> маркера в логе) — за архитектором (стадия architecture, ADR). Где описаны варианты —
|
||||
> это границы допустимого решения, а не предписание.
|
||||
|
||||
## 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).
|
||||
119
docs/work-items/ORCH-044/03-acceptance-criteria.md
Normal file
119
docs/work-items/ORCH-044/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# 03 — Критерии приёмки (Acceptance Criteria)
|
||||
|
||||
**Work Item:** ORCH-044
|
||||
Каждый критерий — однозначное PASS/FAIL. Привязка к TR из `02-trz.md`.
|
||||
|
||||
## P1 — Preflight ловит авторизацию
|
||||
|
||||
### AC-1 — Не залогинен ⇒ preflight FAIL (TR-1.1, TR-1.2, TR-1.5)
|
||||
- **Дано:** бинарь claude существует, `claude --version` отвечает успешно, НО учётные
|
||||
данные отсутствуют/нечитаемы (нет `.credentials.json`).
|
||||
- **Когда:** вызывается `preflight.check(force=True)`.
|
||||
- **Тогда:** возвращается `(False, reason)`, где `reason` упоминает авторизацию
|
||||
(например, «not logged in» / «credentials»).
|
||||
- **FAIL если:** возвращается `(True, ...)` (как сейчас — слепота к auth).
|
||||
|
||||
### AC-2 — Протухший OAuth-токен ⇒ preflight FAIL (TR-1.2a)
|
||||
- **Дано:** `.credentials.json` существует и читаем, но `claudeAiOauth.expiresAt` в прошлом.
|
||||
- **Когда:** `preflight.check(force=True)`.
|
||||
- **Тогда:** `(False, reason)` с указанием на истечение токена.
|
||||
- *(N/A, если архитектор выбрал чистый вариант (b) без чтения файла — тогда покрывается AC-9.)*
|
||||
|
||||
### AC-3 — Валидный логин ⇒ preflight OK без регрессии (TR-1.7)
|
||||
- **Дано:** bin есть, `--version` ок, `.credentials.json` читаем, `expiresAt` в будущем.
|
||||
- **Когда:** `preflight.check(force=True)`.
|
||||
- **Тогда:** `(True, ...)`.
|
||||
- **FAIL если:** залогиненный валидный кейс даёт FAIL (ложное срабатывание).
|
||||
|
||||
### AC-4 — Auth-fail блокирует клейм job (TR-1.5, BR-2)
|
||||
- **Дано:** preflight возвращает `(False, ...)` из-за auth; в очереди есть `queued` job.
|
||||
- **Когда:** `QueueWorker._drain_once()` выполняет тик.
|
||||
- **Тогда:** job **не клеймится** (остаётся `queued`), в `worker.last_preflight_ok=False`,
|
||||
пишется лог-warning; claude не спавнится.
|
||||
- **FAIL если:** job переходит в `running` / спавнится агент.
|
||||
|
||||
### AC-5 — Token-free и локально (BR-1, TR-1.6)
|
||||
- **Дано:** auth-проверка.
|
||||
- **Тогда:** она НЕ делает prompt-ping и НЕ обращается к API модели (никаких httpx/сетевых
|
||||
вызовов к провайдеру в пути проверки; проверяется по коду/моку — сетевой вызов не
|
||||
происходит).
|
||||
- **FAIL если:** проверка отправляет запрос к модели/жжёт токены.
|
||||
|
||||
### AC-6 — Кеширование auth-проверки (TR-1.4)
|
||||
- **Дано:** `preflight_cache_ttl` > 0, первый `check()` выполнен.
|
||||
- **Когда:** повторные `check()` в пределах TTL.
|
||||
- **Тогда:** дорогая часть (чтение файла/процесс) не повторяется чаще TTL (как у version-check).
|
||||
- **FAIL если:** файл/процесс дёргается на каждый тик внутри TTL.
|
||||
|
||||
## P2 — Решение по `--effort`
|
||||
|
||||
### AC-7 — Расследование задокументировано (TR-2.1)
|
||||
- **Тогда:** в ADR (`06-adr/`) и/или `10-tech-risks.md` зафиксирована причина пустого stdout
|
||||
при `--effort` + `--print --output-format json` (несовместимость/синтаксис/баг CLI).
|
||||
- **FAIL если:** изменения внесены без объяснения первопричины.
|
||||
|
||||
### AC-8 — Однозначный исход A или B, без «мёртвого» флага (TR-2.2, TR-2.3)
|
||||
- **Тогда:** реализован ровно один из вариантов:
|
||||
- **A:** `--effort` формируется и запуск с ним даёт **непустой** result-JSON; прод-хотфикс
|
||||
`ORCH_AGENT_EFFORT_*=""` более не требуется; есть регресс-тест на непустой вывод; ИЛИ
|
||||
- **B:** `--effort` **не формируется** в активном пути `_spawn`; дефолты `agent_effort_*`
|
||||
нейтрализованы; ORCH-41-доки помечают effort как unsupported на текущем CLI.
|
||||
- **FAIL если:** в коде остаётся путь, где дефолтная конфигурация добавляет `--effort` и
|
||||
гасит вывод; ИЛИ код/доки/дефолты противоречат друг другу.
|
||||
|
||||
### AC-9 — Дефолтный запуск даёт непустой результат (TR-2.3, перекликается с P3)
|
||||
- **Дано:** конфигурация по умолчанию после задачи (без ручного хотфикса в `.env`).
|
||||
- **Когда:** агент запускается стандартным путём `_spawn`.
|
||||
- **Тогда:** результат запуска — непустой run-лог с валидным result-JSON (проверяемо
|
||||
модульно через построение cmd и/или интеграционно на моке claude).
|
||||
- **FAIL если:** дефолтный путь воспроизводит пустой stdout инцидента.
|
||||
|
||||
## P3 — Пустой лог / нет result-JSON ⇒ провал
|
||||
|
||||
### AC-10 — Пустой лог + exit 0 ⇒ job НЕ done (TR-3.1, TR-3.2)
|
||||
- **Дано:** агент завершился `exit_code=0`, но run-лог пустой (0 байт).
|
||||
- **Когда:** отрабатывает `_monitor_agent`/`_finalize_job`.
|
||||
- **Тогда:** job НЕ переходит в `done`; переходит в `failed` (или `queued` при наличии
|
||||
retry-бюджета) с информативным `error`; шлётся алерт.
|
||||
- **FAIL если:** job становится `done`, либо остаётся `running` навсегда.
|
||||
|
||||
### AC-11 — Нет валидного result-JSON + exit 0 ⇒ job НЕ done (TR-3.1, TR-3.2)
|
||||
- **Дано:** run-лог непустой, но не содержит валидного result-JSON (мусор/обрезок).
|
||||
- **Когда:** финализация job.
|
||||
- **Тогда:** job трактуется как провал (как AC-10).
|
||||
- **FAIL если:** job становится `done`.
|
||||
|
||||
### AC-12 — Нет авто-advance и нет «успешного» коммента при провале результата (TR-3.2)
|
||||
- **Дано:** кейс AC-10/AC-11.
|
||||
- **Тогда:** `_try_advance_stage` НЕ вызывается (стадия не двигается), «успешный»
|
||||
status-коммент агента НЕ постится.
|
||||
- **FAIL если:** стадия продвинулась/запостился успех при пустом результате.
|
||||
|
||||
### AC-13 — Валидный результат не регрессирует (TR-3.1)
|
||||
- **Дано:** `exit_code=0` и непустой run-лог с валидным result-JSON.
|
||||
- **Когда:** финализация job.
|
||||
- **Тогда:** job → `done`, авто-advance и usage-коммент работают как раньше.
|
||||
- **FAIL если:** легитимный успешный запуск теперь ошибочно помечается провалом.
|
||||
|
||||
### AC-14 — Никогда не вечный `running` (TR-3.4, BR-3)
|
||||
- **Тогда:** для любого завершившегося процесса (любой exit_code, включая 0 с пустым логом)
|
||||
job завершается в терминальном/ретраябельном состоянии (`done`/`failed`/`queued`), не
|
||||
остаётся `running`.
|
||||
- **FAIL если:** существует путь, оставляющий job `running` после выхода процесса.
|
||||
|
||||
## Сквозные
|
||||
|
||||
### AC-15 — Документация обновлена в том же PR (правило агентов №2, №6)
|
||||
- **Тогда:** обновлены `docs/operations/INFRA.md` (env-карта preflight-auth и/или effort),
|
||||
`docs/architecture/internals.md` (effort-секция), `CHANGELOG.md`; заведён ADR.
|
||||
- **FAIL если:** функционал изменён, доки/CHANGELOG/ADR не обновлены (reviewer → REQUEST_CHANGES).
|
||||
|
||||
### AC-16 — Тесты зелёные (test-plan)
|
||||
- **Тогда:** все тесты из `04-test-plan.yaml` проходят; `pytest tests/ -q` зелёный.
|
||||
- **FAIL если:** хотя бы один тест плана FAIL или существующие тесты сломаны без обоснованного
|
||||
обновления контракта.
|
||||
|
||||
### AC-17 — Self-hosting безопасность (CLAUDE.md)
|
||||
- **Тогда:** изменения не требуют рестарта/падения прод-контейнера `orchestrator` в рамках
|
||||
задачи; проверка прошла через staging (8501).
|
||||
- **FAIL если:** задача ломает/рестартует прод-инстанс, останавливая конвейер других проектов.
|
||||
145
docs/work-items/ORCH-044/04-test-plan.yaml
Normal file
145
docs/work-items/ORCH-044/04-test-plan.yaml
Normal file
@@ -0,0 +1,145 @@
|
||||
work_item: ORCH-044
|
||||
title: "Надёжность запуска агента: preflight auth + --effort фикс + пустой лог = провал"
|
||||
notes: >
|
||||
Реальный claude/Popen НЕ спавнится: subprocess и launcher мокаются (паттерн
|
||||
tests/test_resilience.py). БД — свежий per-test sqlite (fixture fresh_db).
|
||||
Файлы учётных данных создаются во временном каталоге (tmp_path) и путь
|
||||
мокается. Сетевые вызовы запрещены — проверяются моками/отсутствием httpx.
|
||||
|
||||
tests:
|
||||
# ---------------- P1: preflight ловит авторизацию ----------------
|
||||
- id: TC-01
|
||||
type: unit
|
||||
description: "Нет .credentials.json при рабочем --version -> preflight.check() = (False, reason про auth)"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-1, TR-1.1, TR-1.2]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-02
|
||||
type: unit
|
||||
description: "Протухший OAuth (claudeAiOauth.expiresAt в прошлом) -> preflight FAIL про истечение токена"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-2, TR-1.2a]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-03
|
||||
type: unit
|
||||
description: "Валидный логин (credentials читаемы, expiresAt в будущем) -> preflight OK, без регрессии"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-3, TR-1.7]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-04
|
||||
type: unit
|
||||
description: "Нечитаемый/битый .credentials.json (невалидный JSON) -> preflight FAIL, не падает исключением"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-1, TR-1.2a, TR-3.5]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-05
|
||||
type: unit
|
||||
description: "Auth-проверка token-free: при check() не происходит сетевого вызова к API модели (мок httpx/urlopen не вызван)"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-5, BR-1, TR-1.6]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-06
|
||||
type: unit
|
||||
description: "Auth-результат кешируется: повторные check() в пределах preflight_cache_ttl не перечитывают credentials"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-6, TR-1.4]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-07
|
||||
type: unit
|
||||
description: "Путь к credentials резолвится от HOME агента (/home/slin), а не от окружения процесса оркестратора"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [TR-1.3]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-08
|
||||
type: integration
|
||||
description: "QueueWorker._drain_once при preflight auth-fail не клеймит job: job остаётся queued, claude не спавнится, last_preflight_ok=False"
|
||||
module: tests/test_preflight_auth.py
|
||||
covers: [AC-4, BR-2, TR-1.5]
|
||||
expected: PASS
|
||||
|
||||
# ---------------- P2: решение по --effort ----------------
|
||||
- id: TC-09
|
||||
type: unit
|
||||
description: "Вариант B: при дефолтной конфигурации построенная cmd в _spawn НЕ содержит '--effort' (флаг не гасит вывод). При варианте A — тест адаптируется на корректное формирование effort"
|
||||
module: tests/test_effort_flag.py
|
||||
covers: [AC-8, TR-2.2, TR-2.3]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-10
|
||||
type: unit
|
||||
description: "resolve_agent_effort согласован с принятым решением (B: нейтрализован/пусто по дефолту; A: валидное значение). Существующий test_resolve_agent_effort обновлён под новый контракт и зелёный"
|
||||
module: tests/test_resolve_agent_effort.py
|
||||
covers: [AC-8, TR-2.4]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-11
|
||||
type: integration
|
||||
description: "Дефолтный путь запуска (мок claude, отдающий валидный result-JSON) даёт непустой лог с валидным JSON — воспроизведение инцидента (пустой stdout) не происходит"
|
||||
module: tests/test_effort_flag.py
|
||||
covers: [AC-9, TR-2.3]
|
||||
expected: PASS
|
||||
|
||||
# ---------------- P3: пустой лог / нет result-JSON = провал ----------------
|
||||
- id: TC-12
|
||||
type: integration
|
||||
description: "exit_code=0 + пустой run-лог (0 байт) -> job НЕ done; помечается failed (или queued при retry-бюджете) с информативным error; алерт вызван"
|
||||
module: tests/test_empty_log_failure.py
|
||||
covers: [AC-10, TR-3.1, TR-3.2]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-13
|
||||
type: integration
|
||||
description: "exit_code=0 + лог без валидного result-JSON (мусор) -> job трактуется как провал, не done"
|
||||
module: tests/test_empty_log_failure.py
|
||||
covers: [AC-11, TR-3.1]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-14
|
||||
type: integration
|
||||
description: "При провале по пустому результату _try_advance_stage НЕ вызывается и успешный usage-коммент НЕ постится"
|
||||
module: tests/test_empty_log_failure.py
|
||||
covers: [AC-12, TR-3.2]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-15
|
||||
type: integration
|
||||
description: "exit_code=0 + непустой лог с валидным result-JSON -> job done, авто-advance и usage-коммент работают (нет регрессии)"
|
||||
module: tests/test_empty_log_failure.py
|
||||
covers: [AC-13, TR-3.1]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-16
|
||||
type: integration
|
||||
description: "Любой выход процесса не оставляет job в 'running': пустой лог+exit0 завершается терминально (done/failed/queued)"
|
||||
module: tests/test_empty_log_failure.py
|
||||
covers: [AC-14, BR-3, TR-3.4]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-17
|
||||
type: unit
|
||||
description: "Классификация пустого результата по умолчанию permanent; transient-маркер в логе уводит в transient-путь (error_classifier)"
|
||||
module: tests/test_empty_log_failure.py
|
||||
covers: [TR-3.3]
|
||||
expected: PASS
|
||||
|
||||
# ---------------- Регрессия / сквозное ----------------
|
||||
- id: TC-18
|
||||
type: unit
|
||||
description: "Регресс: существующие preflight-кейсы (bin missing, --version ok) из test_resilience.py остаются зелёными после добавления auth-слоя"
|
||||
module: tests/test_resilience.py
|
||||
covers: [AC-3, TR-1.7]
|
||||
expected: PASS
|
||||
|
||||
- id: TC-19
|
||||
type: integration
|
||||
description: "Полный прогон 'pytest tests/ -q' зелёный — ни один существующий тест не сломан без обоснованного обновления контракта"
|
||||
module: tests/
|
||||
covers: [AC-16]
|
||||
expected: PASS
|
||||
Reference in New Issue
Block a user