work_item: ORCH-073
title: "CRIT: эрозия main — системный фикс merge-verify + восстановление кода 067/069"
notes: >
  Покрытие FR-1..FR-5 / AC-1..AC-11. Все верификаторы — never-raise (INV-1):
  при ошибке git/HTTP → False (fail-closed), не падение. Gitea/git вызовы мокаются
  (monkeypatch httpx + subprocess), как в существующих тестах merge_gate/stage_engine.
  Тесты регресс-гарда и .gitattributes используют временный git-репозиторий (tmp_path).

tests:
  # ---- FR-1: verify_merged_to_main — SHA-в-main как единственный критерий ----
  - id: TC-01
    type: unit
    description: "verify_merged_to_main: sha — предок origin/main → True (happy-path, AC-6)."
    module: tests/test_orch073_merge_verify.py
    expected: PASS
  - id: TC-02
    type: unit
    description: "verify_merged_to_main: sha НЕ предок main И существует merged docs-PR ветки → False (баг 067/069, AC-2)."
    module: tests/test_orch073_merge_verify.py
    expected: PASS
  - id: TC-03
    type: unit
    description: "verify_merged_to_main: пустой sha → False (неопределённо, fail-closed)."
    module: tests/test_orch073_merge_verify.py
    expected: PASS
  - id: TC-04
    type: unit
    description: "verify_merged_to_main: git fetch/merge-base бросает исключение → False (never-raise, INV-1)."
    module: tests/test_orch073_merge_verify.py
    expected: PASS

  # ---- FR-2: pr_already_merged различает code-PR / docs-PR ----
  - id: TC-05
    type: unit
    description: "pr_already_merged/идентификация PR: merged docs-PR (head=docs/*, base=main) НЕ засчитывается как merge кода ветки."
    module: tests/test_orch073_pr_classify.py
    expected: PASS
  - id: TC-06
    type: unit
    description: "merged code-PR (head=<feature-branch>, base=main) корректно распознаётся как code-merge."
    module: tests/test_orch073_pr_classify.py
    expected: PASS
  - id: TC-07
    type: unit
    description: "pr_already_merged: HTTP-ошибка/не-200 → False (never-raise, консервативно)."
    module: tests/test_orch073_pr_classify.py
    expected: PASS

  # ---- FR-3: merge_pr сливает именно code-ветку ----
  - id: TC-08
    type: unit
    description: "merge_pr выбирает open PR с head==<feature-branch> и base==main (не docs/*), вызывает Gitea POST merge."
    module: tests/test_orch073_merge_pr.py
    expected: PASS
  - id: TC-09
    type: unit
    description: "merge_pr: нет open code-PR → (False, 'no open PR'); никогда не push/force-push main (INV-2)."
    module: tests/test_orch073_merge_pr.py
    expected: PASS
  - id: TC-10
    type: unit
    description: "merge_pr идемпотентен: уже-слитый code-PR (SHA в main) → no-op, без второго POST merge (AC-7/INV-4)."
    module: tests/test_orch073_merge_pr.py
    expected: PASS

  # ---- FR-4: .gitattributes CHANGELOG.md merge=union ----
  - id: TC-11
    type: integration
    description: ".gitattributes в корне репо содержит 'CHANGELOG.md merge=union'; git check-attr подтверждает driver=union (AC-4)."
    module: tests/test_orch073_gitattributes.py
    expected: PASS
  - id: TC-12
    type: integration
    description: "Во временном git-репо два ребейза/слияния с правкой '## [Unreleased]' НЕ дают конфликта; обе записи в CHANGELOG сохранены (AC-4)."
    module: tests/test_orch073_gitattributes.py
    expected: PASS

  # ---- FR-5: регресс-гард целостности main + интеграция в _handle_merge_verify ----
  - id: TC-13
    type: unit
    description: "_handle_merge_verify: SHA в main И маркеры на месте → return False (advance к done, happy-path AC-6)."
    module: tests/test_orch073_regression_guard.py
    expected: PASS
  - id: TC-14
    type: unit
    description: "_handle_merge_verify: SHA НЕ в main (docs-only merge) → return True (HOLD), alert + set_issue_blocked, НЕ done (AC-3)."
    module: tests/test_orch073_regression_guard.py
    expected: PASS
  - id: TC-15
    type: unit
    description: "Регресс-гард: deployed SHA есть, но набор маркеров ранее-merged задач уменьшился → HOLD + alert 'main regressed', НЕ done (AC-5)."
    module: tests/test_orch073_regression_guard.py
    expected: PASS
  - id: TC-16
    type: unit
    description: "_handle_merge_verify: внутренняя ошибка верификатора → HOLD + alert, без проброса исключения в advance_stage (never-raise, INV-1)."
    module: tests/test_orch073_regression_guard.py
    expected: PASS

  # ---- Условность / обратная совместимость ----
  - id: TC-17
    type: unit
    description: "merge_verify_applies: non-self репо (enduro) или kill-switch off → под-гейт no-op, поведение merge/verify без изменений (AC-6/INV-5)."
    module: tests/test_orch073_conditionality.py
    expected: PASS
  - id: TC-18
    type: unit
    description: "Регресс-гард уважает kill-switch (merge_verify_enabled=False) → no-op; для non-self → no-op (INV-5)."
    module: tests/test_orch073_conditionality.py
    expected: PASS

  # ---- Регресс существующего поведения ----
  - id: TC-19
    type: integration
    description: "Существующие тесты merge_gate/stage_engine (ORCH-065/071) остаются зелёными; полный pytest tests/ -q green (AC-8)."
    module: tests/
    expected: PASS
