# HANDOFF_PROTOCOL — формальный контракт handoff «стадия → обязательный выход» > **Назначение.** Нормативная спека: что КАЖДАЯ стадия конвейера обязана оставить на выходе — > какие документы и какие frontmatter-ключи. Дополняет [`PIPELINE_DOCS.md`](PIPELINE_DOCS.md) > (карта «документ → агент → стадия → гейт → machine-key») «вертикальным» срезом по стадиям и > вводит **обязательную frontmatter-схему** для машинной проверки. > > **Статус истины (важно).** Источник истины поведения — **код**: `src/stages.py` > (`STAGE_TRANSITIONS`), `src/qg/checks.py` (`QG_CHECKS` / `check_*` / `_parse_*`), > `src/stage_engine.py` (врезки под-гейтов). Машинный контракт чтения/записи/валидации > frontmatter — `src/frontmatter.py`. Эта спека **документирует**; при расхождении первичен код > (правило ORCH-075). Введено задачей **ORCH-076** (ORCH-52c — слой 2 эпика ORCH-52: машинный контракт). Слой 1 (ORCH-075/52b) дал описательный стандарт документов; ORCH-52c реализовала единый машинный frontmatter-контракт (reader + writer + валидатор) и свела чтение пяти вердиктов к одной точке парсинга. Сквозной ADR: [`adr-0020-frontmatter-contract.md`](../architecture/adr/adr-0020-frontmatter-contract.md); детально — [`ORCH-076/06-adr/ADR-001-frontmatter-contract.md`](../work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md). --- ## 1. Обязательная frontmatter-схема (машинный источник: `frontmatter.REQUIRED_FIELDS`) Forward-looking аддитивная схема: набор полей, которые handoff-документ стадии **должен** нести в ведущем YAML-frontmatter. Машинный источник истины — кортеж [`src/frontmatter.py`](../../src/frontmatter.py) `REQUIRED_FIELDS`: | Поле | Смысл | |------|-------| | `work_item` | ID задачи (`ORCH-NNN` / `ET-NNN`) — к какой задаче относится выход | | `stage` | стадия, на выходе которой написан документ (`analysis` … `deploy`) | | `author_agent` | роль-автор (`analyst` / `architect` / `developer` / `reviewer` / `tester` / `deployer`) | | `status` | человеко/машинно-читаемый статус выхода стадии | | `created_at` | дата создания артефакта (YYYY-MM-DD) | | `model_used` | модель агента, сгенерировавшего артефакт (`claude-…`) | **Режим проверки (ORCH-52c, критично для self-hosting).** Валидатор схемы `frontmatter.validate_schema` / `maybe_warn_schema` по умолчанию **warning-only** и **никогда не влияет на boolean-вердикт ни одного гейта**: отсутствие полей логируется (`logger.warning`), но не роняет конвейер и не заваливает гейт. Жёсткий режим (hard-fail) зарезервирован на будущее (ORCH-52d) и включается ТОЛЬКО kill-switch'ем `frontmatter_validation_strict` (env `ORCH_FRONTMATTER_VALIDATION_STRICT`, дефолт `False`). Схема **аддитивна**: старый документ-вердикт без этих полей читается гейтом ровно как раньше (см. §3). --- ## 2. Контракт handoff по стадиям Категории документов — как в `PIPELINE_DOCS.md` §2: **required** (всегда), **when-applicable** (при наличии предмета: инфра / данные / security / post-deploy — отсутствие не нарушение). «Machine-verdict ключ» — поле, которое exit-гейт/под-гейт ребра читает ТОЛЬКО из frontmatter (никогда из прозы). Набор документов/ключей/гейтов **согласован 1:1 с `PIPELINE_DOCS.md` §2–§3**. | Стадия (выход) | Агент | Обязательные документы на выходе | Machine-verdict ключ (читает гейт ребра) | Гейт ребра | |----------------|-------|----------------------------------|------------------------------------------|------------| | `created` | система (`_create_initial_docs`) / заказчик | `00-business-request.md` | — (вход, не гейтится) | — | | `analysis` | analyst | `01-brd.md`, `02-trz.md`, `03-acceptance-criteria.md`, `04-test-plan.yaml` | — (гейт проверяет наличие файлов + Approved) | `check_analysis_approved` | | `architecture` | architect | `06-adr/ADR-NNN-.md` (≥1); `07-infra-requirements.md`, `08-data-requirements.md`, `10-tech-risks.md` (when-applicable/required-info) | — (гейт проверяет наличие `06-adr/` ≥1 ИЛИ `07-…`) | `check_architecture_done` | | `development` | developer | код + тесты в ветке (артефакт-док не пишется; гейт — зелёный CI) | — (гейт читает CI-статус Gitea) | `check_ci_green` | | `review` | reviewer | `12-review.md` | `verdict:` (`APPROVED` \| `REQUEST_CHANGES`) | `check_reviewer_verdict` | | `testing` | tester | `13-test-report.md` | `result:` / `verdict:` / `status:` (`PASS` \| `FAIL` \| `BLOCKED`; три равноранговых, ORCH-047) | `check_tests_passed` | | `deploy-staging` | deployer | `15-staging-log.md` (required для self-hosting); `17-security-report.md` (security-под-гейт, when-applicable) | `staging_status:` (`SUCCESS` \| `FAILED`); `security_status:` (`PASS` \| `FAIL`) | `check_staging_status` (ребро); под-гейты ребра `deploy-staging→deploy`: `check_security_gate` → `check_branch_mergeable` → `check_staging_image_fresh` | | `deploy` | deployer / deploy-finalizer | `14-deploy-log.md` | `deploy_status:` (`SUCCESS` \| `FAILED`) | `check_deploy_status` | | `done` | — | — (терминал) | — | — | | пост-`done` наблюдение | post-deploy-monitor | `16-post-deploy-log.md` (when-applicable, ORCH-021) | `post_deploy_status:` (`HEALTHY` \| `DEGRADED`) — **информационный, не гейт** | — (телеметрия петли уроков / наблюдаемость) | ### Примечания (нормативные) - **Под-гейты ребра `deploy-staging → deploy`** (`check_security_gate` → `check_branch_mergeable` → `check_staging_image_fresh`) — это **врезки в `advance_stage`**, а НЕ строки `STAGE_TRANSITIONS`. Их порядок и условность раската не меняются этой спекой. - **`15-staging-log.md`** обязателен только для self-hosting репо (`orchestrator`); для прочих репо staging-гейт — N/A (ORCH-35), документ не требуется. - **`16-post-deploy-log.md`** несёт `post_deploy_status:`, но это **информационный** ключ (телеметрия ORCH-8 / наблюдаемость), гейтом он НЕ парсится. - **`09-…` / `05-…` / `11-…`** — зарезервированные/legacy номера; канон reviewer'а — `12-review.md`. --- ## 3. Machine-verdict доки vs информационные (честный механизм проверки) Полностью согласовано с `PIPELINE_DOCS.md` §3. Machine-verdict док — гейт читает ТОЛЬКО YAML-frontmatter (через единый `frontmatter.parse_frontmatter`), маппит ключ в вердикт; имя ключа чувствительно к регистру, значение парсер приводит к верхнему регистру. | Документ | Machine-key | Парсер | Эффект вердикта | |----------|-------------|--------|-----------------| | `12-review.md` | `verdict:` | `check_reviewer_verdict` | `APPROVED` → дальше; `REQUEST_CHANGES` → откат на `development` | | `13-test-report.md` | `result:` / `verdict:` / `status:` | `_parse_tests_verdict` | `PASS` → дальше; `FAIL`/`BLOCKED` → откат (негативный токен авторитетен) | | `14-deploy-log.md` | `deploy_status:` | `_parse_deploy_status` | `SUCCESS` → `done`; `FAILED` → откат (БАГ-8) | | `15-staging-log.md` | `staging_status:` | `_parse_staging_status` | `SUCCESS` → дальше; `FAILED` → откат (self-hosting; иначе N/A) | | `17-security-report.md` | `security_status:` | `check_security_gate` → `parse_security_status` | `PASS` → дальше; `FAIL` → откат | **Информационные доки** (гейтом НЕ парсятся): `00-business-request.md`, `08-data-requirements.md`, `10-tech-risks.md`, `16-post-deploy-log.md`. **Аддитивность схемы (§1).** Документ-вердикт БЕЗ полей схемы из §1, но с вердикт-ключом, читается гейтом РОВНО как раньше: схема не участвует в вычислении вердикта при дефолтном `frontmatter_validation_strict=False`. --- ## 4. Единый машинный контракт — `src/frontmatter.py` Все операции с frontmatter сведены в один leaf-модуль (never-raise): - `read_frontmatter_value(path, key) -> str | None` — single-key reader (контракт неизменен, BC). - `parse_frontmatter(content) -> FrontmatterParse` — **единственная точка** парсинга YAML-frontmatter (`data` / `has_block` / `malformed` / `yaml_error`); пять вердикт-парсеров делегируют сюда. - `parse_frontmatter_dict` / `read_frontmatter` — ярлыки к распарсенному mapping. - `render_frontmatter` / `write_frontmatter` — writer (формат совместим с существующими парсерами). - `validate_schema` / `REQUIRED_FIELDS` / `maybe_warn_schema` — схема §1 (warning-only по умолчанию). - `strip_frontmatter` — общий хелпер тела (заменил дубли). - Kill-switch жёсткой валидации: `config.frontmatter_validation_strict` (env `ORCH_FRONTMATTER_VALIDATION_STRICT`, дефолт `False`). > Перед написанием номерного дока бери скелет из [`docs/_templates/`](../_templates/) и **не меняй > имя machine-key frontmatter** (регистр чувствителен — иначе гейт упадёт ложно).