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
