work_item: ORCH-053 description: > Тесты sweeper/reconciler потерянных webhook. Вся сеть (Plane API, Gitea API, QG) мокируется (monkeypatch), как в существующих tests/. Telegram заглушён autouse-фикстурой conftest. Используется временная SQLite БД (ORCH_DB_PATH / фикстура setup_db по образцу test_webhooks.py / test_queue.py). Реальные агенты/CLI не запускаются. tests: # ---- F-1: gate-side sweeper ------------------------------------------------- - id: TC-01 type: unit description: > reconcile_gate_once продвигает застрявшую development-задачу: нет активных job, updated_at старше grace, check_ci_green замокан в (True, "CI green") → advance_stage вызван, стадия стала review, заenqueuen reviewer. module: tests/test_reconciler.py expected: PASS - id: TC-02 type: unit description: > Источник истины — гейт: reconciler НЕ содержит собственного update_task_stage/ enqueue_job для advance — продвижение идёт строго через stage_engine.advance_stage (проверка через мок/spy advance_stage, вызван с finished_agent=None). module: tests/test_reconciler.py expected: PASS - id: TC-03 type: unit description: > Задача с активным job (has_active_job_for_task=True) пропускается: гейт не дёргается, advance_stage не вызывается, нотификаций нет. module: tests/test_reconciler.py expected: PASS - id: TC-04 type: unit description: > Per-stage grace: задача с updated_at свежее grace своей стадии не трогается; ровно на границе age>=grace и без активного job — становится кандидатом. module: tests/test_reconciler.py expected: PASS - id: TC-05 type: unit description: > grace_for_stage читает reconcile_grace_overrides_json (per-stage), при отсутствии ключа — reconcile_grace_default_s; невалидный JSON → дефолт, не падает. module: tests/test_reconciler.py expected: PASS - id: TC-06 type: unit description: > Нет спама: при стабильно красном гейте (check_ci_green=(False,...)) несколько проходов подряд НЕ вызывают notify_qg_failure повторно на каждом тике; задача не продвигается. module: tests/test_reconciler.py expected: PASS - id: TC-07 type: unit description: > Тишина при синхронности: когда все задачи done / имеют активный job / в пределах grace — проход не вызывает advance_stage и не пишет INFO-логов о разблокировке. module: tests/test_reconciler.py expected: PASS - id: TC-08 type: unit description: > AC-16: задача на analysis с артефактами на диске, но Plane НЕ Approved — F-1 (reconcile_gate_once) НЕ продвигает analysis→architecture. module: tests/test_reconciler.py expected: PASS - id: TC-09 type: unit description: > Never-raise: если обработка одной задачи кидает исключение (advance_stage замокан на raise), проход ловит его и продолжает обрабатывать остальные задачи; поток не падает. module: tests/test_reconciler.py expected: PASS - id: TC-10 type: unit description: > Kill-switch: при reconcile_enabled=False reconcile_gate_once/plane_once не выполняют действий (no-op); при reconcile_plane_enabled=False гасится только F-2. module: tests/test_reconciler.py expected: PASS # ---- F-2: plane-side reconciler -------------------------------------------- - id: TC-11 type: unit description: > In Progress без задачи: list_issues_by_state возвращает issue в In Progress, в БД задачи нет → reconcile_plane_once вызывает handle_status_start (мок) ровно один раз с корректным issue_data (id/state/project). module: tests/test_reconciler_plane.py expected: PASS - id: TC-12 type: unit description: > Approved без advance: issue=Approved, task существует, нет активного job → вызван handle_verdict(approved=True) (мок) один раз. module: tests/test_reconciler_plane.py expected: PASS - id: TC-13 type: unit description: > Rejected без rollback: issue=Rejected, task существует, нет активного job → вызван handle_verdict(approved=False) (мок) один раз. module: tests/test_reconciler_plane.py expected: PASS - id: TC-14 type: unit description: > Идемпотентность F-2: issue в требующем-действия статусе, но у task есть активный job → handle_status_start/handle_verdict НЕ вызываются (живой webhook в работе). module: tests/test_reconciler_plane.py expected: PASS - id: TC-15 type: integration description: > AC-4 анти-дубль на создании: одновременная реконсиляция + обработка реального In Progress webhook для одного plane_id создают ровно ОДИН task row и один стартовый analyst-job (реальная временная БД, мок Gitea/Plane сетевых вызовов). module: tests/test_reconciler_plane.py expected: PASS - id: TC-16 type: unit description: > list_issues_by_state never-raise: при ошибке Plane API (httpx бросает / non-2xx) → возвращает [], тик не падает; при успехе — обходит пагинацию и фильтрует по state. module: tests/test_reconciler_plane.py expected: PASS - id: TC-17 type: unit description: > F-2 опрашивает все проекты реестра projects.PROJECTS и резолвит state-uuid через get_project_states per-project (enduro + orchestrator), не хардкодит uuid. module: tests/test_reconciler_plane.py expected: PASS # ---- F-3: sha→branch резолв ------------------------------------------------- - id: TC-18 type: unit description: > handle_ci_status: при отсутствии branches и неразрезолвленном sha срабатывает БД-fallback и однозначно находит единственную development-задачу repo; продвижение идёт штатно. module: tests/test_gitea_sha_resolve.py expected: PASS - id: TC-19 type: unit description: > handle_ci_status: при неоднозначности (несколько development-задач repo) БД-fallback не делает ложный матч (branch остаётся неразрезолвленным, лог INFO), success/failure-семантика гейта не изменена. module: tests/test_gitea_sha_resolve.py expected: PASS # ---- F-4 / интеграция фонового потока -------------------------------------- - id: TC-20 type: unit description: > Наблюдаемость: при разблокировке reconciler пишет явную лог-строку с work_item_id и stage; при reconcile_notify_unblock=True вызывается send_telegram (замокан). module: tests/test_reconciler.py expected: PASS - id: TC-21 type: integration description: > Restart-safe поток: Reconciler.start() поднимает daemon-поток, stop() завершает его в пределах таймаута; повторный start идемпотентен (не плодит второй поток). module: tests/test_reconciler.py expected: PASS - id: TC-22 type: unit description: > Конфиг: новые поля reconcile_* присутствуют в Settings с заявленными дефолтами и читаются из env с префиксом ORCH_ (по образцу tests/test_config.py). module: tests/test_config.py expected: PASS - id: TC-23 type: unit description: > Регресс реестров: STAGE_TRANSITIONS и QG_CHECKS не изменены ORCH-053 (snapshot-тест проходит как раньше). module: tests/test_qg_registry_snapshot.py expected: PASS