# 02 — ТЗ (TRZ): ORCH-076 — ORCH-52c: протокол handoff + frontmatter-контракт (writer/валидатор/схема)

Work Item: **ORCH-076** · Repo: **orchestrator** · Стадия: analysis

> ТЗ описывает **конкретные изменения к реализации**, выведенные из BRD и фактического кода.
> Архитектурное обоснование/решения (как именно структурировать модуль контракта вердиктов,
> точные сигнатуры) — задача архитектора (06-adr).

## 1. Сводка изменения

ORCH-52c превращает `src/frontmatter.py` из single-key reader в полный frontmatter-контракт
(**reader + writer + валидатор обязательной схемы**) и сводит **разрознённое чтение вердиктов**
гейтов к **единому frontmatter-API**, не меняя ни состав гейтов, ни семантику вердиктов.
Дополнительно создаётся **формальная спека handoff** в `docs/_standards/`, согласованная с
манифестом ORCH-52b (`PIPELINE_DOCS.md`). Всё строго обратно совместимо (старые доки читаются
как раньше), never-raise, валидатор не hard-fail по умолчанию (kill-switch).

## 2. Задействованные модули / пути

| Путь | Действие |
|------|----------|
| `src/frontmatter.py` | **изменить** — добавить writer + валидатор + чтение всего frontmatter (multi-key/dict); reader `read_frontmatter_value` сохранить (контракт неизменен) |
| `src/qg/checks.py` | **изменить** — `check_reviewer_verdict`, `_parse_tests_verdict`, `_parse_deploy_status`, `_parse_staging_status` перевести на чтение через единый frontmatter-API (поведение/токены/семантика 1:1) |
| `src/security_gate.py` | **изменить** — `parse_security_status` читает `security_status:` через единый API (семантика 1:1) |
| `src/post_deploy.py` | **изменить (по решению архитектора)** — чтение `post_deploy_status:` через единый API (информационный, не гейт) |
| `src/review_parse.py` | **возможно изменить** — `_strip_frontmatter` может использовать общий хелпер; контракт «never raise → ""» сохранить |
| `src/config.py` | **изменить** — добавить kill-switch строгой валидации (напр. `frontmatter_validation_strict: bool = False`) |
| `docs/_standards/HANDOFF_PROTOCOL.md` (имя — на усмотрение архитектора/стандарта) | **создать** — формальная спека handoff «стадия → обязательный выход» |
| `docs/_standards/PIPELINE_DOCS.md` | **изменить** — связать со спекой handoff, отметить что ORCH-52c реализовала машинный контракт |
| `tests/test_frontmatter.py` | **создать** — unit на reader/writer/валидатор/round-trip |
| `tests/` (гейты) | **изменить/создать** — анти-регресс тесты чтения вердиктов через новый API |
| `CLAUDE.md`, `docs/architecture/README.md`, `CHANGELOG.md`, ADR | **изменить/создать** — документация |

## 3. Функциональные требования

### FR-1 — Writer frontmatter (BR-1)
В `src/frontmatter.py` добавить функцию записи: принимает данные frontmatter (mapping
ключ→значение) и тело документа, возвращает/записывает строку с каноничным ведущим
YAML-блоком `---\n…\n---\n<body>`. Формат на 100% совместим с существующими парсерами
(`split("---", 2)` + `yaml.safe_load`). **never-raise** (NFR-2): ошибка сериализации/записи →
лог + безопасный результат, исключение наружу не выходит. Точная сигнатура (in-memory render
vs запись в файл, перезапись существующего frontmatter) — решение архитектора.

### FR-2 — Валидатор обязательной схемы (BR-2, NFR-3)
В `src/frontmatter.py` добавить валидатор, проверяющий наличие обязательных полей схемы:
`work_item`, `stage`, `author_agent`, `status`, `created_at`, `model_used`. Возвращает
структурированный результат (список отсутствующих/невалидных полей + признак валидности).
**Поведение по умолчанию — warning/лог, НЕ blocker** (NFR-3): отсутствие полей не роняет
конвейер и не заваливает гейт. Жёсткость (hard-fail) включается ТОЛЬКО kill-switch'ем
`frontmatter_validation_strict` (дефолт `False`). never-raise.

### FR-3 — Полночтение frontmatter / единый reader-API (BR-1, BR-4)
В `src/frontmatter.py` добавить чтение ВСЕГО frontmatter как mapping (а не только single-key),
поверх которого строится единый доступ к вердикт-полям. Существующий
`read_frontmatter_value(path, key)` сохраняется без изменения контракта (обратная
совместимость вызывающих — `notifications.build_status_comment` и т.п.). never-raise.

### FR-4 — Единый контракт чтения вердиктов (BR-4, BR-5, NFR-1)
Пять гейтов-вердиктов читают свои стандартные поля через единый frontmatter-API:

| Гейт / парсер | Документ | Стандартное поле | Семантика (НЕИЗМЕННА) |
|---------------|----------|------------------|------------------------|
| `check_reviewer_verdict` | `12-review.md` | `verdict:` | `APPROVED`→дальше; `REQUEST_CHANGES`→откат на development |
| `_parse_tests_verdict` | `13-test-report.md` | `result:` / `verdict:` / `status:` (3 равноранговых, ORCH-047) | `PASS`→дальше; `FAIL`/`BLOCKED`→откат; негативный токен авторитетен |
| `_parse_deploy_status` | `14-deploy-log.md` | `deploy_status:` | `SUCCESS`→done; `FAILED`→откат (БАГ-8) |
| `_parse_staging_status` | `15-staging-log.md` | `staging_status:` | `SUCCESS`→дальше; `FAILED`→откат (self-hosting; иначе N/A) |
| `parse_security_status` | `17-security-report.md` | `security_status:` | `PASS`→дальше; `FAIL`→откат |

Требование: **только механизм чтения** унифицируется (одна точка парсинга YAML-frontmatter);
наборы токенов (`_TESTS_NEGATIVE_TOKENS`/`_TESTS_POSITIVE_TOKENS`), приведение к верхнему
регистру, обработка «no frontmatter / bad YAML / missing key», fallback `worktree → origin/main`
для deploy/staging — сохраняются 1:1. Возврат каждого `check_*` — прежний `tuple[bool, str]`.

### FR-5 — Обратная совместимость старых доков (NFR-1, критично)
Документ-вердикт БЕЗ новых полей схемы (`work_item/stage/author_agent/status/created_at/
model_used`), но с вердикт-ключом (`verdict:`/`result:`/`deploy_status:`/…) ДОЛЖЕН читаться
гейтом ровно как сейчас. Новая схема — аддитивна; её отсутствие не влияет на чтение вердикта.

### FR-6 — Спека handoff (BR-3)
Создать в `docs/_standards/` формальную спеку «стадия → обязательный выход»: для каждой стадии
(`created`→`analysis`→`architecture`→`development`→`review`→`testing`→`deploy-staging`→`deploy`
→`done`) перечислить обязательные документы и обязательные frontmatter-ключи на выходе.
Согласовать с таблицей §2 `PIPELINE_DOCS.md` (тот же набор документов/ключей/гейтов), явно
указать «источник истины — код». Различать machine-verdict доки и информационные (как в
`PIPELINE_DOCS.md` §3).

## 4. Изменения API

Нет. HTTP-эндпоинты не добавляются/не меняются. (Опционально архитектор может предложить блок
наблюдаемости в `GET /queue` для счётчика валидации — НЕ требование данной задачи.)

## 5. Изменения схемы БД

Нет. Таблицы/миграции/индексы не затрагиваются. Контракт работает на файлах
(YAML-frontmatter) и in-memory.

## 6. Требования к новым/изменённым QG checks

- **Состав `QG_CHECKS` НЕ изменяется** (никаких новых/удалённых зарегистрированных гейтов) —
  AC-6 / правило CLAUDE.md.
- Изменяется только **внутренняя реализация чтения вердикта** существующих `check_*`/`_parse_*`
  (делегирование единому frontmatter-API). Сигнатуры и возвращаемые значения (`tuple[bool,str]`)
  — неизменны.
- Новый kill-switch `frontmatter_validation_strict` (config) управляет жёсткостью валидатора
  схемы; дефолт `False` (warning-only) → нулевая поведенческая регрессия.

## 7. Совместимость / регресс

- **Обратная совместимость (NFR-1):** старые доки-вердикты без новой схемы читаются как
  раньше; контракт `read_frontmatter_value` неизменен; формат writer'а совместим с
  существующими парсерами.
- **never-raise (NFR-2):** writer/валидатор/единый reader не выбрасывают исключений в
  конвейер (паттерн текущего `frontmatter.py`).
- **kill-switch / обратимость (NFR-3, NFR-5):** `frontmatter_validation_strict=False` (дефолт)
  → валидация только логирует; `True` → строгий режим (на будущее). Поведение деградирует к
  прежнему при дефолтном флаге.
- **Неизменность контрактов (AC-6):** `STAGE_TRANSITIONS`, состав `QG_CHECKS`, семантика
  вердиктов, fallback `worktree→origin/main`, трёх-полевой контракт tester (ORCH-047),
  токен-логика BLOCKED/FAILED — без изменений.
- **Нулевая регрессия enduro (NFR-4):** для не-self-hosting репо поведение 1:1; условные гейты
  (ORCH-35/43/58) не затрагиваются по существу.
- **Полный регресс `tests/` зелёный** перед мержем.
- **self-hosting:** не перезапускать прод-контейнер вручную; деплой через штатный путь;
  первый боевой `autoDeploy` (наблюдение — за стадией deploy).
