work_item: ORCH-043 title: "merge-gate + auto-rebase + re-test — безопасная параллель в одном репо" framework: pytest notes: > Тесты на git-операции используют локальные временные репозитории (init bare "origin" + рабочая ветка), мокают сеть/Plane/Telegram (как в tests/test_qg.py: ORCH_DB_PATH/ORCH_REPOS_DIR в tmp, httpx замокан). Каталог тестов/команда pytest для re-test должны совпадать с CI-конфигом проекта. Финальные имена функций/модулей сверять с реализацией архитектора. tests: # ---- merge_gate core: ancestor / behind detection ---- - id: TC-01 type: unit description: "branch_is_behind_main → True, когда origin/main ушёл вперёд относительно ветки" module: tests/test_merge_gate.py expected: PASS - id: TC-02 type: unit description: "branch_is_behind_main → False, когда ветка уже содержит весь origin/main" module: tests/test_merge_gate.py expected: PASS - id: TC-03 type: unit description: "branch_is_behind_main never-raise: недоступный git/clone → безопасный возврат, не исключение" module: tests/test_merge_gate.py expected: PASS # ---- auto-rebase ---- - id: TC-04 type: unit description: "auto_rebase_onto_main: чистый догон → (True), ветка содержит origin/main, push выполнен через --force-with-lease" module: tests/test_merge_gate.py expected: PASS - id: TC-05 type: unit description: "auto_rebase_onto_main: текстовый конфликт → rebase отменён (worktree чист), (False, 'rebase conflict...'), main не тронут" module: tests/test_merge_gate.py expected: PASS - id: TC-06 type: unit description: "auto_rebase_onto_main НЕ пушит и не форс-пушит main ни при каком исходе (проверка вызванных git-команд)" module: tests/test_merge_gate.py expected: PASS # ---- re-test ---- - id: TC-07 type: unit description: "retest_branch: pytest rc=0 → (True, 're-test green')" module: tests/test_merge_gate.py expected: PASS - id: TC-08 type: unit description: "retest_branch: pytest rc!=0 → (False, 're-test failed...') с хвостом вывода" module: tests/test_merge_gate.py expected: PASS - id: TC-09 type: unit description: "retest_branch: превышен merge_retest_timeout_s → (False, 're-test timeout...'), без виса" module: tests/test_merge_gate.py expected: PASS # ---- merge-lock / сериализация ---- - id: TC-10 type: unit description: "merge-lock: второй захват того же repo не проходит, пока lock удержан; освобождается в finally/после ошибки" module: tests/test_merge_gate.py expected: PASS - id: TC-11 type: unit description: "merge-lock restart-safe: устаревший/осиротевший lock не блокирует навсегда (тайм-аут merge_lock_timeout_s)" module: tests/test_merge_gate.py expected: PASS # ---- QG check_branch_mergeable ---- - id: TC-12 type: unit description: "check_branch_mergeable: ветка актуальна → (True, 'up-to-date'), rebase не вызывался" module: tests/test_qg_merge_gate.py expected: PASS - id: TC-13 type: unit description: "check_branch_mergeable: отстаёт + чистый rebase + зелёный re-test → (True)" module: tests/test_qg_merge_gate.py expected: PASS - id: TC-14 type: unit description: "check_branch_mergeable: конфликт rebase → (False, 'rebase conflict...')" module: tests/test_qg_merge_gate.py expected: PASS - id: TC-15 type: unit description: "check_branch_mergeable: красный re-test после догона → (False, 're-test failed after rebase...')" module: tests/test_qg_merge_gate.py expected: PASS - id: TC-16 type: unit description: "check_branch_mergeable never-raise: внутренняя ошибка → (False, reason), не исключение; lock освобождён" module: tests/test_qg_merge_gate.py expected: PASS - id: TC-17 type: unit description: "merge_gate_enabled=False (или репо вне merge_gate_repos) → (True, 'merge-gate disabled') no-op" module: tests/test_qg_merge_gate.py expected: PASS # ---- реестр QG / стадии ---- - id: TC-18 type: unit description: "'check_branch_mergeable' присутствует в QG_CHECKS и callable" module: tests/test_qg_registry_snapshot.py expected: PASS - id: TC-19 type: unit description: "snapshot STAGE_TRANSITIONS/_EXPECTED_QGS обновлён осознанно и совпадает; порядок ключей сохранён (get_previous_stage не сломан)" module: tests/test_qg_registry_snapshot.py expected: PASS # ---- интеграция со stage_engine (откаты) ---- - id: TC-20 type: integration description: "_run_qg диспетчеризует check_branch_mergeable с сигнатурой (repo, work_item_id, branch)" module: tests/test_stage_engine.py expected: PASS - id: TC-21 type: integration description: "merge-gate FAIL → advance_stage откатывает задачу на 'development', set_issue_blocked, комментарий Plane, Telegram-алерт (моки)" module: tests/test_stage_engine.py expected: PASS - id: TC-22 type: integration description: "merge-gate FAIL уважает MAX_DEVELOPER_RETRIES — нет бесконечного цикла заворотов" module: tests/test_stage_engine.py expected: PASS - id: TC-23 type: integration description: "merge-gate PASS → задача продвигается к слиянию/деплою, рассинхрона стадий нет" module: tests/test_stage_engine.py expected: PASS # ---- сквозной сценарий гонки ---- - id: TC-24 type: integration description: > Воспроизведение бизнес-сценария: A и B от main@C0; A влита (main@C1); B проходит merge-gate → догоняется до C1 и re-test зелёный → безопасное слияние; при красном re-test B откатывается, main остаётся зелёным module: tests/test_merge_gate_race.py expected: PASS # ---- конфигурация ---- - id: TC-25 type: unit description: "Новые ORCH_* настройки (merge_gate_enabled, merge_retest_timeout_s, merge_lock_timeout_s, merge_gate_repos) читаются с дефолтами и env-override" module: tests/test_config.py expected: PASS # ---- регресс ---- - id: TC-26 type: integration description: "Полный набор pytest tests/ -q зелёный (существующие гейты/вебхуки/стадии не сломаны)" module: tests/ expected: PASS