work_item: ORCH-076
title: "ORCH-52c — handoff-протокол + frontmatter writer/валидатор/единый контракт вердиктов"
framework: pytest
scope: >
  Покрывается: writer/валидатор/единое чтение frontmatter (src/frontmatter.py);
  чтение пяти гейтов-вердиктов через единый API при семантике 1:1; обратная
  совместимость старых доков без новой схемы; never-raise; kill-switch строгой
  валидации. Вне покрытия: правка промптов агентов (ORCH-52d), ретро-фит старых
  документов, изменение STAGE_TRANSITIONS/состава QG_CHECKS.
notes: >
  Полный регресс tests/ должен оставаться зелёным (анти-регресс гейтов, AC-4/AC-6).
  Регресс = любой существующий тест гейтов (review/tester/deploy/staging/security),
  ставший красным, или изменение вердикта при том же входном значении.
  Тесты не должны требовать сети (frontmatter — файловый/in-memory контракт).

tests:
  # --- frontmatter.py: writer / валидатор / reader (AC-1, AC-5) ---
  - id: TC-01
    type: unit
    description: "Writer сериализует mapping в каноничный ведущий YAML-frontmatter (--- ... ---), читаемый существующими парсерами"
    module: tests/test_frontmatter.py
    expected: PASS

  - id: TC-02
    type: unit
    description: "Round-trip: writer записал frontmatter -> reader read_frontmatter_value возвращает те же значения по ключам"
    module: tests/test_frontmatter.py
    expected: PASS

  - id: TC-03
    type: unit
    description: "Валидатор: полная схема (work_item/stage/author_agent/status/created_at/model_used) -> valid=True, нет отсутствующих полей"
    module: tests/test_frontmatter.py
    expected: PASS

  - id: TC-04
    type: unit
    description: "Валидатор: отсутствие части обязательных полей -> valid=False со списком отсутствующих, но БЕЗ исключения (warning-only по умолчанию)"
    module: tests/test_frontmatter.py
    expected: PASS

  - id: TC-05
    type: unit
    description: "never-raise: writer и валидатор на битом вводе (None/не-mapping/нечитаемый путь/битый YAML) не выбрасывают исключение, возвращают безопасное значение + лог"
    module: tests/test_frontmatter.py
    expected: PASS

  - id: TC-06
    type: unit
    description: "reader read_frontmatter_value сохраняет прежний контракт (single-key, None на ошибку/отсутствие, strip, регистр сохранён)"
    module: tests/test_frontmatter.py
    expected: PASS

  - id: TC-07
    type: unit
    description: "kill-switch frontmatter_validation_strict: False -> отсутствие полей не блокирует; True -> строгий режим сигнализирует невалидность"
    module: tests/test_frontmatter.py
    expected: PASS

  # --- единый контракт вердиктов: чтение через общий API, семантика 1:1 (AC-3, AC-6) ---
  - id: TC-08
    type: unit
    description: "check_reviewer_verdict через единый API: verdict: APPROVED -> (True); REQUEST_CHANGES -> (False); отсутствие -> (False) — как до задачи"
    module: tests/test_qg_verdicts.py
    expected: PASS

  - id: TC-09
    type: unit
    description: "_parse_tests_verdict через единый API: ORCH-047 три равноранговых поля (result/verdict/status), приоритет негативного токена (BLOCKED/FAILED) сохранён"
    module: tests/test_qg_verdicts.py
    expected: PASS

  - id: TC-10
    type: unit
    description: "_parse_deploy_status через единый API: deploy_status SUCCESS -> (True); FAILED -> (False); missing/bad YAML -> (False) — семантика БАГ-8 неизменна"
    module: tests/test_qg_verdicts.py
    expected: PASS

  - id: TC-11
    type: unit
    description: "_parse_staging_status через единый API: SUCCESS/FAILED семантика и условность ORCH-35 (non-self -> N/A pass) сохранены"
    module: tests/test_qg_verdicts.py
    expected: PASS

  - id: TC-12
    type: unit
    description: "parse_security_status через единый API: security_status PASS -> (True); FAIL -> (False) — семантика неизменна"
    module: tests/test_security_gate.py
    expected: PASS

  # --- обратная совместимость / анти-регресс (AC-4) ---
  - id: TC-13
    type: unit
    description: "Старый док-вердикт БЕЗ новой схемы (только verdict/result/deploy_status/staging_status/security_status) читается каждым из пяти гейтов как до задачи"
    module: tests/test_qg_verdicts.py
    expected: PASS

  - id: TC-14
    type: unit
    description: "Док С новой полной схемой + вердикт-ключом читается гейтом с тем же вердиктом, что и без схемы (схема аддитивна, не влияет на вердикт)"
    module: tests/test_qg_verdicts.py
    expected: PASS

  - id: TC-15
    type: integration
    description: "fallback worktree -> origin/main для check_deploy_status/check_staging_status сохранён при чтении через единый API"
    module: tests/test_qg_verdicts.py
    expected: PASS

  # --- инварианты конвейера (AC-6) ---
  - id: TC-16
    type: unit
    description: "Состав QG_CHECKS и STAGE_TRANSITIONS не изменён (тот же набор ключей/стадий, что эталон)"
    module: tests/test_stages_invariants.py
    expected: PASS

  # --- полный регресс ---
  - id: TC-17
    type: integration
    description: "Полный прогон tests/ зелёный (нет регресса существующих тестов гейтов и конвейера)"
    module: tests/
    expected: PASS
