# adr-0020: Единый frontmatter-контракт + спека handoff (reader/writer/валидатор) Статус: **Accepted** · Дата: 2026-06-09 · Источник: **ORCH-076** (ORCH-52c) Детально: [`docs/work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md`](../../work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md) ## Контекст Слой 1 эпика ORCH-52 (ORCH-075/52b) дал **описательный** стандарт документов (`docs/_standards/PIPELINE_DOCS.md`), явно отложив машинную проверку на ORCH-52c. В коде: `src/frontmatter.py` — только single-key reader (never-raise), а ~10-строчный блок парсинга YAML-frontmatter **продублирован** в 5 вердикт-парсерах (`check_reviewer_verdict`, `_parse_tests_verdict`, `_parse_deploy_status`, `_parse_staging_status`, `parse_security_status`) + в `_strip_frontmatter`/`extract_security_findings`. Единого контракта чтения, writer'а, схемы и формальной спеки handoff — нет. Эти парсеры читают вердикты **на гейтах self-hosting** инструмента, обслуживающего прод других проектов из общего инстанса → любой регресс = стоп конвейера всех проектов. ## Решение 1. **`src/frontmatter.py` → полный frontmatter-контракт** (функции в существующем leaf-модуле, контракт **never-raise**): сохранённый `read_frontmatter_value` (без изменений) + единый парс-примитив `parse_frontmatter(content) -> FrontmatterParse` (единственная точка YAML-логики, структура различает no-block / malformed / yaml-error / data) + `render_/ write_frontmatter` (writer) + `validate_schema` (обязательная схема `work_item, stage, author_agent, status, created_at, model_used`) + `strip_frontmatter`. 2. **Унифицируется механизм парсинга, НЕ семантика.** Все 5 вердикт-парсеров читают YAML через `parse_frontmatter`; token-наборы, upper-casing, приоритет негативного токена, 3-полевой контракт tester'а (ORCH-047), fallback `worktree→origin/main` — **1:1**. Сигнатуры и `tuple[bool, str]` — неизменны. Reason-строки переносятся дословно. 3. **Валидатор не hard-fail по умолчанию.** Флаг `frontmatter_validation_strict` (env `ORCH_FRONTMATTER_VALIDATION_STRICT`, дефолт `False`): default — warning/лог, **вне вердикт-пути гейтов** (нулевая регрессия); hard-fail — зарезервированный strict-режим (включение — с ORCH-52d). Иначе ORCH-52c заблокировала бы собственный деплой. 4. **Формальная спека handoff** `docs/_standards/HANDOFF_PROTOCOL.md` — «стадия → обязательный выход» (документы + frontmatter-ключи), согласована 1:1 с `PIPELINE_DOCS.md` §2–§3; источник истины — код. `PIPELINE_DOCS.md` обновляется ссылкой + отметкой о реализации машинного слоя. 5. **Без изменений** `STAGE_TRANSITIONS`, состава `QG_CHECKS`, API, схемы БД. ## Альтернативы - Общий «умный» verdict-резолвер (поле+токены для всех гейтов) — отклонён: различия token-логики → риск тонкого регресса на гейте при self-hosting. Унифицируем только парс YAML. - Класс/новый пакет — отклонён: состояния нет, лишний blast radius. - Hard-fail валидатор по умолчанию — отклонён (NFR-3: self-block собственного деплоя). - Сторонняя `python-frontmatter` — отклонена: лишняя зависимость ради ~30 строк. ## Последствия - **+** Конец дублирования/рассинхрона парсинга; writer+валидатор+схема готовы к ORCH-52d; спека handoff закрывает пробел контракта стадий. - **+** Нулевая регрессия по построению: семантика и reason-строки 1:1, валидатор инертен при дефолте, never-raise сохранён, enduro 1:1. - **−** Унификация частичная (парс, не семантика); strict-режим «спящий» до ORCH-52d. - **Обратимость:** `frontmatter_validation_strict=False` ⇒ прежнее поведение; перевод гейтов поведенчески инвариантен. - **Риск:** первый боевой `autoDeploy` орка (ORCH-089) — наблюдение за стадией `deploy` (`docs/work-items/ORCH-076/10-tech-risks.md`). ## Связи - Опирается: adr-0019 (pipeline-docs-standard, ORCH-075), ORCH-016 (reader), ORCH-047 (3-полевой tester), adr-0012 (security-гейт), adr-0018 (auto-label/`autoDeploy`). - Готовит: ORCH-52d (эмиссия полной схемы агентами; возможное включение strict).