117 lines
9.5 KiB
Markdown
117 lines
9.5 KiB
Markdown
# Acceptance Criteria — ORCH-053
|
||
|
||
Work Item ID: ORCH-053
|
||
Формат: каждый критерий имеет явное условие PASS/FAIL. Критерий считается выполненным,
|
||
только если соответствующие тесты из `04-test-plan.yaml` зелёные.
|
||
|
||
## AC-1 — Реконсиляция застрявшей стадии (gate-side, F-1)
|
||
- **Дано:** task на стадии `development`, без активных job'ов, `updated_at` старше grace,
|
||
гейт `check_ci_green` для её branch — зелёный (CI прошёл, но webhook потерян, как ORCH-044).
|
||
- **Когда:** срабатывает фоновый проход `reconcile_gate_once()`.
|
||
- **PASS:** задача продвинута `development → review`, заenqueuen `reviewer` (через
|
||
`advance_stage(..., finished_agent=None)`), `tasks.updated_at` обновлён.
|
||
- **FAIL:** задача осталась на `development`, либо продвижение пошло параллельной логикой
|
||
(не через `advance_stage`).
|
||
|
||
## AC-2 — Источник истины — гейт, не событие
|
||
- **PASS:** продвижение в F-1 выполняется исключительно вызовом
|
||
`stage_engine.advance_stage(...)`; в `reconciler.py` НЕТ собственного
|
||
`update_task_stage`+`enqueue_job` для advance стадии (только переиспользование).
|
||
- **FAIL:** в reconciler продублирована логика advance/rollback.
|
||
|
||
## AC-3 — Идемпотентность: sweeper не трогает задачи с активным job
|
||
- **Дано:** task с `queued` или `running` job (`has_active_job_for_task == True`).
|
||
- **PASS:** sweeper пропускает задачу — ни advance, ни enqueue, ни нотификации.
|
||
- **FAIL:** sweeper дёргает гейт / создаёт второй job для такой задачи.
|
||
|
||
## AC-4 — Идемпотентность: задержавшийся/дублированный webhook + sweeper не двоят
|
||
- **Дано:** issue в Plane = In Progress, задержавшийся Plane-webhook ещё не обработан.
|
||
- **Когда:** F-2 реконсилирует И затем (или одновременно) приходит реальный webhook.
|
||
- **PASS:** создаётся **ровно одна** задача (один task row, один branch/worktree, один
|
||
стартовый analyst-job). Повторный путь видит существующую задачу/активный job и не двоит.
|
||
- **FAIL:** созданы две задачи / два стартовых job / два worktree на один `plane_id`.
|
||
|
||
## AC-5 — Per-stage grace соблюдается
|
||
- **Дано:** task на стадии, чей `updated_at` свежее grace этой стадии (агент легитимно
|
||
работает, напр. analysis 8 мин при grace 1800с).
|
||
- **PASS:** sweeper НЕ трогает задачу (не дёргает гейт).
|
||
- **PASS (граница):** как только `age(updated_at) >= grace_for_stage(stage)` и нет активного
|
||
job — задача становится кандидатом.
|
||
- **FAIL:** sweeper дёргает гейт у задачи в пределах grace.
|
||
|
||
## AC-6 — Plane In Progress без задачи → запуск (F-2)
|
||
- **Дано:** issue в Plane = In Progress (статус сменён руками, webhook потерян), в `tasks`
|
||
задачи нет, прошёл grace.
|
||
- **PASS:** sweeper вызывает `handle_status_start`/`start_pipeline` → задача создана,
|
||
заenqueuen analyst — как если бы пришёл webhook.
|
||
- **FAIL:** задача не создана; либо создана дублирующей логикой, минуя `handle_status_start`.
|
||
|
||
## AC-7 — Plane Approved без advance → advance (F-2)
|
||
- **Дано:** issue = Approved, task существует и стадия НЕ сдвинута, нет активного job, прошёл grace.
|
||
- **PASS:** sweeper вызывает `handle_verdict(approved=True)` → штатный advance.
|
||
- **FAIL:** нет advance, либо advance вне `handle_verdict`/`advance_stage`.
|
||
|
||
## AC-8 — Plane Rejected без rollback → rollback (F-2)
|
||
- **Дано:** issue = Rejected, task существует и не откатана, нет активного job, прошёл grace.
|
||
- **PASS:** sweeper вызывает `handle_verdict(approved=False)` → штатный rollback на предыдущую стадию.
|
||
- **FAIL:** нет rollback, либо rollback вне штатного пути.
|
||
|
||
## AC-9 — Нет спама нотификаций на красном гейте
|
||
- **Дано:** застрявшая задача, у которой гейт стабильно **красный** (напр. CI failure),
|
||
нет активного job, прошёл grace.
|
||
- **Когда:** sweeper проходит несколько тиков подряд.
|
||
- **PASS:** `notify_qg_failure`/Telegram НЕ вызывается на каждом тике (≤1 раз / без
|
||
повторов); задача не продвигается.
|
||
- **FAIL:** на каждом тике летит нотификация о провале гейта.
|
||
|
||
## AC-10 — Тишина при синхронности
|
||
- **Дано:** все задачи синхронны (нет застрявших; статусы Plane совпадают с локальными).
|
||
- **PASS:** проход не выполняет действий, не пишет INFO-логов о разблокировке, не шлёт нотификаций.
|
||
- **FAIL:** sweeper генерирует шум/действия при полностью синхронном состоянии.
|
||
|
||
## AC-11 — Restart-safe фоновый поток
|
||
- **PASS:** reconciler стартует в `main.lifespan` (daemon-поток), корректно
|
||
останавливается (`stop()`), переживает рестарт сервиса без потери (нет состояния в памяти,
|
||
критичного для корректности; всё перечитывается из БД/Plane).
|
||
- **FAIL:** reconciler не стартует автоматически, висит при shutdown, или дублирует действия
|
||
после рестарта.
|
||
|
||
## AC-12 — Наблюдаемость разблокировки (F-4)
|
||
- **Дано:** sweeper разблокировал застрявшую задачу.
|
||
- **PASS:** в лог пишется явная строка вида
|
||
`reconciler: <work_item_id> <stage> разблокирована (потерян webhook)`;
|
||
при `reconcile_notify_unblock=True` — Telegram-уведомление.
|
||
- **FAIL:** разблокировка происходит молча (невозможно измерить частоту дыры).
|
||
|
||
## AC-13 — Kill-switch
|
||
- **Дано:** `reconcile_enabled=False` (env `ORCH_RECONCILE_ENABLED=false`).
|
||
- **PASS:** фоновый поток reconciler не выполняет проходов (или не стартует); система
|
||
работает как до ORCH-053. `reconcile_plane_enabled=False` гасит только F-2, F-1 работает.
|
||
- **FAIL:** sweeper активен при выключенном флаге.
|
||
|
||
## AC-14 — Усиленный sha→branch резолв (F-3)
|
||
- **Дано:** Gitea CI-status webhook без `branches` и со `sha`, не разрезолвившимся
|
||
через `git branch -r --contains`.
|
||
- **PASS:** добавленный БД-fallback однозначно находит task (по repo + активной
|
||
development-стадии) и продвигает; неоднозначность логируется на уровне INFO; существующая
|
||
success/failure-семантика гейта не изменена.
|
||
- **FAIL:** регресс существующего резолва, либо ложный матч при неоднозначности.
|
||
|
||
## AC-15 — Never-raise в тике
|
||
- **Дано:** обработка одной задачи/issue кидает исключение (битые данные, ошибка API).
|
||
- **PASS:** исключение изолировано, проход продолжает остальные задачи; поток не падает.
|
||
- **FAIL:** одно исключение роняет весь проход / поток reconciler.
|
||
|
||
## AC-16 — F-1 не продвигает analysis по локальному состоянию
|
||
- **Дано:** task на `analysis`, артефакты на диске присутствуют, но Plane НЕ в статусе
|
||
Approved (BRD не одобрен человеком), нет активного job, прошёл grace.
|
||
- **PASS:** F-1 (gate-side) НЕ продвигает analysis→architecture (advance стадии analysis
|
||
отдан F-2, которая сверяется с реальным статусом Plane Approved).
|
||
- **FAIL:** sweeper автопродвинул неодобренный BRD.
|
||
|
||
## AC-17 — Документация обновлена (golden source)
|
||
- **PASS:** в PR обновлены `docs/architecture/README.md`, заведён
|
||
`docs/work-items/ORCH-053/06-adr/ADR-001-*.md`, обновлён `CHANGELOG.md`, упомянут
|
||
kill-switch в `docs/operations/INFRA.md`.
|
||
- **FAIL:** код изменён, документация — нет (Reviewer обязан вернуть REQUEST_CHANGES).
|