From 2030d1627ab2ac2212594c2f52d985589673b279 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 9 Jun 2026 13:49:25 +0300 Subject: [PATCH] architect(ET): auto-commit from architect run_id=454 --- docs/architecture/adr/README.md | 6 +- .../adr/adr-0020-frontmatter-contract.md | 63 +++++ .../06-adr/ADR-001-frontmatter-contract.md | 248 ++++++++++++++++++ .../ORCH-076/07-infra-requirements.md | 50 ++++ .../ORCH-076/08-data-requirements.md | 34 +++ docs/work-items/ORCH-076/10-tech-risks.md | 25 ++ 6 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 docs/architecture/adr/adr-0020-frontmatter-contract.md create mode 100644 docs/work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md create mode 100644 docs/work-items/ORCH-076/07-infra-requirements.md create mode 100644 docs/work-items/ORCH-076/08-data-requirements.md create mode 100644 docs/work-items/ORCH-076/10-tech-risks.md diff --git a/docs/architecture/adr/README.md b/docs/architecture/adr/README.md index 8efe640..9d79d09 100644 --- a/docs/architecture/adr/README.md +++ b/docs/architecture/adr/README.md @@ -23,13 +23,17 @@ Per-work-item решения живут в `docs/work-items//06-adr/ADR-NNN- | adr-0015 | Зависимости задач (B ждёт A) + сериализация merge внутри репо | accepted | 2026-06-08 | ORCH-026 | | adr-0016 | ensure_open_pr — гарантированный код-PR перед merge-verify | accepted | 2026-06-09 | ORCH-082 | | adr-0017 | Per-repo serial gate (пакетный автономный режим, serial e2e) | proposed | 2026-06-09 | ORCH-088 | +| adr-0018 | Авто-режим по лейблам (autoApprove + autoDeploy) | accepted | 2026-06-09 | ORCH-089 | +| adr-0019 | Стандарт документов конвейера (PIPELINE_DOCS, слой 1) | accepted | 2026-06-09 | ORCH-075 | +| adr-0020 | Единый frontmatter-контракт + спека handoff (reader/writer/валидатор) | accepted | 2026-06-09 | ORCH-076 | > ⚠️ Историческая коллизия: номер `0007` занят двумя файлами — > `adr-0007-reconciler.md` (ORCH-053) и `adr-0007-executable-self-deploy.md` > (ORCH-036). Оба accepted; для новых сквозных ADR использовать следующий -> свободный номер (текущий максимум — `0017`). +> свободный номер (текущий максимум — `0020`). > adr-0014 **amends** adr-0013 (меняет критерий merge-verify на «SHA-в-main»). > adr-0016 **amends** adr-0013/0014 (гарантирует открытый код-PR перед merge_pr, ORCH-082). +> adr-0020 реализует машинный слой к adr-0019 (ORCH-52b→52c). ## Формат **Контекст → Решение → Альтернативы → Последствия → Связи.** Статус: proposed / accepted / superseded. diff --git a/docs/architecture/adr/adr-0020-frontmatter-contract.md b/docs/architecture/adr/adr-0020-frontmatter-contract.md new file mode 100644 index 0000000..2c355bb --- /dev/null +++ b/docs/architecture/adr/adr-0020-frontmatter-contract.md @@ -0,0 +1,63 @@ +# 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). diff --git a/docs/work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md b/docs/work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md new file mode 100644 index 0000000..a20042a --- /dev/null +++ b/docs/work-items/ORCH-076/06-adr/ADR-001-frontmatter-contract.md @@ -0,0 +1,248 @@ +# ADR-001: Единый frontmatter-контракт (reader+writer+валидатор) и унификация чтения вердиктов + +Work Item: **ORCH-076** (ORCH-52c, слой 2 эпика ORCH-52) · Repo: **orchestrator** · Стадия: architecture +Дата: 2026-06-09 · Статус: **Accepted** + +> Сквозная версия — [`docs/architecture/adr/adr-0020-frontmatter-contract.md`](../../../architecture/adr/adr-0020-frontmatter-contract.md). + +--- + +## Статус +Accepted + +## Контекст + +(Подробно — `01-brd.md` §1, `02-trz.md`.) Слой 1 эпика (ORCH-075/52b) дал **описательный** +стандарт `docs/_standards/PIPELINE_DOCS.md`. ORCH-52c — **машинный** слой. Установлено в коде +на ветке задачи: + +- `src/frontmatter.py` = **только reader** (`read_frontmatter_value(path, key) -> str | None`, + never-raise → `None`). В docstring прямой коммент: *«merging into a single parser is a + follow-up task»* — это и есть данная задача. +- **Парсинг YAML-frontmatter дублируется** в 5+ местах (~10 строк + `content.startswith("---")` → `split("---", 2)` → `yaml.safe_load` → `isinstance(dict)`): + `qg/checks.py::check_reviewer_verdict`, `_parse_tests_verdict`, `_parse_deploy_status`, + `_parse_staging_status`; `security_gate.py::parse_security_status`; плюс `_strip_frontmatter` + в `review_parse.py` и `security_gate.extract_security_findings`. Каждый — своя обработка + ошибок и свои reason-строки → риск рассинхрона. +- **Нет машинно-проверяемой схемы** обязательного frontmatter и **нет формальной спеки + handoff** «что каждая стадия обязана оставить на выходе». + +**⚠️ Self-hosting (главное ограничение проектирования).** Затрагиваемый код читает вердикты +**на гейтах** в инструменте, который прямо сейчас обслуживает прод (enduro-trails) из общего +инстанса с общей БД/очередью. Любой регресс чтения вердикта = остановка конвейера ВСЕХ +проектов. Рефакторинг обязан быть **строго обратно совместимым, never-raise, нулевая +регрессия**. Плюс на задаче выставлен лейбл `autoDeploy` (ORCH-089) — это **первый боевой +автодеплой** орка (детали риска — `10-tech-risks.md`). + +## Движущие силы (требования) + +BR-1…BR-5, NFR-1…NFR-5 (`01-brd.md`), FR-1…FR-6 (`02-trz.md`), AC-1…AC-7 +(`03-acceptance-criteria.md`). Ключевые инварианты-ограничители: + +- **INV-1** `STAGE_TRANSITIONS` и **состав** `QG_CHECKS` — не меняются (AC-6). +- **INV-2** Семантика каждого вердикта (значение → переход/откат) — 1:1, включая 3-полевой + контракт tester'а (ORCH-047) и приоритет негативного токена (AC-6, FR-4). +- **INV-3** Контракт `read_frontmatter_value` — неизменен (внешние вызыватели: `usage.py`, + `notifications.build_status_comment`) (FR-3). +- **INV-4** Валидатор схемы **не hard-fail по умолчанию** — иначе ORCH-52c заблокировала бы + собственный деплой (её доки и доки соседей ещё без полной схемы) (NFR-3). +- **INV-5** Никаких изменений API и схемы БД (TRZ §4–§5). + +--- + +## Решение + +### D1. `src/frontmatter.py` становится единым frontmatter-контрактом (1 модуль, функции) + +Выбран **набор функций в существующем leaf-модуле** (не класс, не новый пакет): модуль уже +есть, не зависит ни от чего проектного (только `logging` + ленивый `yaml`), импортируем без +циклов из `qg/checks.py`, `security_gate.py`, `post_deploy.py`, `review_parse.py`. Класс/состояние +не нужны — операции чистые. Это минимизирует blast radius (требование self-hosting). + +**Публичный API (имена канонические; точные дефолты — в реализации, контракт фиксирован здесь):** + +```python +# --- константы схемы --- +REQUIRED_FIELDS = ("work_item", "stage", "author_agent", "status", "created_at", "model_used") + +# --- reader: СОХРАНЁН без изменения контракта (INV-3) --- +def read_frontmatter_value(path: str, key: str) -> str | None: ... + +# --- единый парс-примитив (единственная точка YAML-логики) --- +@dataclass(frozen=True) +class FrontmatterParse: + data: dict # {} если нет/битый/не-mapping + has_block: bool # присутствовал ведущий ---…--- блок + malformed: bool # был "---", но < 3 сегментов (незакрытый блок) + yaml_error: str | None # текст ошибки yaml.safe_load, иначе None + +def parse_frontmatter(content: str) -> FrontmatterParse: ... # never-raise +def parse_frontmatter_dict(content: str) -> dict: ... # ярлык → .data; never-raise → {} +def read_frontmatter(path: str) -> dict: ... # файл → parse; never-raise → {} + +# --- writer --- +def render_frontmatter(data: Mapping[str, object], body: str = "") -> str: ... + # → "---\n\n---\n"; формат совместим со split("---",2)+safe_load; never-raise → body +def write_frontmatter(path: str, data: Mapping, body: str = "") -> bool: ... + # персист render_frontmatter; never-raise → False (ошибка логируется) + +# --- валидатор схемы --- +@dataclass(frozen=True) +class SchemaValidation: + valid: bool + missing: list[str] # отсутствующие/пустые обязательные поля +def validate_schema(data: Mapping, *, required=REQUIRED_FIELDS) -> SchemaValidation: ... # never-raise + +# --- общий хелпер тела (заменяет дубли _strip_frontmatter) --- +def strip_frontmatter(content: str) -> str: ... # never-raise → content +``` + +**Контракт всего модуля — never-raise** (NFR-2), как у действующего reader: любая ошибка +(I/O, YAML, сериализация) → `logger.debug/warning` + безопасное значение (`{}` / `False` / +исходный текст), исключение наружу **не выходит**. + +`parse_frontmatter` возвращает **структуру** (а не голый dict), чтобы каждый гейт мог +**воспроизвести свои текущие reason-строки 1:1** (см. D2) — это и есть способ сохранить +семантику без переписывания сообщений (INV-2). + +### D2. Унифицируется МЕХАНИЗМ парсинга, а НЕ семантика вердиктов + +AC-3/FR-4 требуют «читать через единый frontmatter-API, а не дублированной ad-hoc логикой». +Унифицируется **ровно повторяющийся блок** `startswith/split/safe_load/isinstance` → +замена на `parse_frontmatter(content)`. **Token-логика, upper-casing, набор полей, приоритет +негативного токена, fallback `worktree → origin/main` — остаются в каждом гейте без изменений.** +Это сознательное ограничение объёма унификации: общий «умный» verdict-резолвер увеличил бы +риск тонкого регресса на гейтах (недопустимо при self-hosting). Каждый `check_*`/`_parse_*` +сохраняет сигнатуру и `tuple[bool, str]`. + +Маппинг состояний `FrontmatterParse` → существующие reason-строки (пример для tester'а, +остальные аналогично): + +| Состояние | Прежняя ветка | Сохраняемая reason-строка | +|-----------|---------------|---------------------------| +| `not has_block` | `not content.startswith("---")` | "No YAML frontmatter in test report …" | +| `malformed` | `len(parts) < 3` | "Malformed YAML frontmatter in test report" | +| `yaml_error` | `except yaml.YAMLError` | "Invalid YAML frontmatter in test report: {e}" | +| `data` (dict) | `fm.get(...)` | прежняя token-логика поверх `parse.data` | + +Точки перевода (FR-4): + +| Парсер | Файл | Поле(я) | Семантика — НЕ менять | +|--------|------|---------|----------------------| +| `check_reviewer_verdict` | `12-review.md` | `verdict:` | APPROVED→дальше; REQUEST_CHANGES→откат | +| `_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) | +| `parse_security_status` | `17-security-report.md` | `security_status:` | PASS→дальше; FAIL→откат (FAIL авторитетен) | + +`post_deploy.py` (`post_deploy_status:`, информационный) и `review_parse._strip_frontmatter`/ +`security_gate.extract_security_findings` (извлечение прозы) переводятся на +`parse_frontmatter_dict` / `strip_frontmatter` соответственно — снимает оставшиеся дубли без +изменения их «never-raise → пусто» контрактов. + +### D3. Валидатор: библиотека + warning-only, hard-fail строго под kill-switch + +`validate_schema` — **чистая библиотечная функция** (INV-4, NFR-3). Чтобы гарантировать +**нулевую регрессию гейтов**, в default-режиме валидатор **не участвует в вычислении +boolean-вердикта** ни одного гейта. Вместо этого: + +- Новый флаг `config.frontmatter_validation_strict: bool = False` + (env `ORCH_FRONTMATTER_VALIDATION_STRICT`). +- **Default (`False`):** опциональный warning-emit — при чтении machine-verdict дока, не + несущего полной схемы, единый хелпер `maybe_warn_schema(content, doc_label)` пишет + `logger.warning("frontmatter schema incomplete: missing …")` и **возвращает управление без + влияния на вердикт** (чистый no-op для `tuple[bool,str]`). Это удовлетворяет «по умолчанию + warning/лог» (FR-2), оставаясь поведенчески инертным. +- **Strict (`True`):** зарезервированный режим будущего ужесточения (ORCH-52d+). Когда + включён, тот же хелпер может вернуть гейту вето. На ORCH-52c флаг **остаётся `False`** в + проде и в `.env.staging` — иначе задача self-block'нется (её доки без полной схемы). Strict + покрывается unit-тестом, но не включается. + +Решение «валидатор вне вердикт-пути по умолчанию» — осознанный выбор в пользу безопасности +self-hosting: машинная проверка схемы **существует и тестируется**, но **физически не может** +завалить гейт при дефолте. + +### D4. Формальная спека handoff — `docs/_standards/HANDOFF_PROTOCOL.md` + +Создаётся (на стадии development, как doc-deliverable) рядом с `PIPELINE_DOCS.md`. Структура +(нормативно для разработчика): + +1. **Назначение + статус истины** — «источник истины поведения = код (`stages.py`, + `qg/checks.py`, `stage_engine.py`); спека документирует» (правило ORCH-075). +2. **Обязательная frontmatter-схема** — таблица 6 полей (`work_item`, `stage`, `author_agent`, + `status`, `created_at`, `model_used`) + смысл каждого; ссылка на `frontmatter.REQUIRED_FIELDS` + как на машинный источник. +3. **Контракт handoff по стадиям** — для каждой стадии (`created`→…→`done`): какие документы + **обязан** оставить выход стадии и какие frontmatter-ключи (machine-verdict ключ + будущая + общая схема). **Согласовано 1:1 с `PIPELINE_DOCS.md` §2–§3** (тот же набор + документов/ключей/гейтов; различие machine-verdict vs информационные сохранено). +4. **Перекрёстная ссылка** на единый API `src/frontmatter.py` и на флаг + `frontmatter_validation_strict`. + +`PIPELINE_DOCS.md` обновляется: блок «слой 1 описательный → ORCH-52c реализовала машинный +контракт» + ссылка на `HANDOFF_PROTOCOL.md` и на `src/frontmatter.py` (закрывает явную метку +«машинная проверка — отдельная задача ORCH-52c» в §5). + +### D5. Без изменений API/БД/состава гейтов + +Подтверждено INV-1/INV-5: HTTP-эндпоинты, `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, схема БД — +не трогаются. Опциональный счётчик валидации в `GET /queue` — **не вводим** (TRZ §4: не +требование; добавил бы поверхность без нужды). + +--- + +## Альтернативы (отклонены) + +- **A1. Общий «умный» verdict-резолвер** (одна функция читает поле+токены для всех 5 гейтов). + Отклонено: token-наборы и правила различаются (особенно ORCH-047 3-поля + приоритет + негатива); единая абстракция повысила бы риск тонкого регресса на гейте → недопустимо при + self-hosting. Унифицируем только парс YAML (D2). +- **A2. Класс `Frontmatter`/новый пакет.** Отклонено: состояния нет, операции чистые; класс — + лишняя церемония и больший blast radius. Функции в существующем leaf-модуле проще и + безопаснее. +- **A3. Валидатор как hard-fail на гейте по умолчанию.** Отклонено прямо BRD/NFR-3: заблокирует + собственный деплой ORCH-52c. Default — warning-only, hard-fail под флагом (D3). +- **A4. Сторонняя библиотека `python-frontmatter`.** Отклонено: новая зависимость ради ~30 + строк; `pyyaml` уже в проекте, формат тривиален, контроль над never-raise важнее. +- **A5. Ретро-фит схемы в существующие доки / правка промптов агентов.** Вне scope (это + ORCH-52d, слой 3). Схема аддитивна и forward-looking. + +--- + +## Последствия + +**Плюсы** +- Единственная точка YAML-парсинга → конец рассинхрона обработки ошибок между гейтами. +- Writer + валидатор + полная схема готовы к ORCH-52d (агенты начнут эмитить схему). +- Спека handoff закрывает пробел «что стадия обязана оставить», согласована с манифестом. +- Нулевая поведенческая регрессия по построению: семантика и reason-строки 1:1, валидатор вне + вердикт-пути при дефолте, never-raise сохранён. + +**Минусы / ограничения** +- Унификация частичная (только парс, не семантика) — token-логика всё ещё живёт в каждом + гейте. Это сознательный компромисс безопасности; полная унификация семантики — возможная + будущая задача с отдельным риск-бюджетом. +- Strict-режим валидатора пока «спящий» (тестируется, но не включён) — реальная польза от + enforcement появится только с ORCH-52d. +- Reason-строки нужно перенести **дословно** — за этим следит reviewer и анти-регресс-тесты. + +**Обратимость** +- `frontmatter_validation_strict=False` (дефолт) ⇒ поведение эквивалентно прежнему. +- Перевод гейтов на `parse_frontmatter` поведенчески инвариантен; откат — точечный возврат + inline-блока (но не требуется при зелёном регрессе). + +**Тестирование (обязательно перед мержем)** +- `tests/test_frontmatter.py` (новый): reader (контракт неизменен), writer (round-trip + `render → parse`), валидатор (полный/неполный набор, strict on/off), битый ввод → never-raise. +- Анти-регресс на каждый из 5 гейтов: старый док-вердикт **без** новой схемы → тот же + `tuple[bool,str]`, что до задачи (NFR-1/AC-4); negative-token-приоритет tester'а (ORCH-047). +- Полный `pytest tests/ -q` зелёный. + +## Связи +- Реализует: BR-1…BR-5, FR-1…FR-6, AC-1…AC-7. +- Опирается на: ORCH-075/52b (`PIPELINE_DOCS.md`, манифест), ORCH-016 (`frontmatter.py` reader), + ORCH-047 (3-полевой tester-вердикт), ORCH-022 (security-гейт), ORCH-089 (`autoDeploy`). +- Готовит почву: ORCH-52d (агенты эмитят полную схему; возможное включение strict). +- Сквозной ADR: `docs/architecture/adr/adr-0020-frontmatter-contract.md`. +- Риски/инфра/данные: `10-tech-risks.md`, `07-infra-requirements.md`, `08-data-requirements.md`. diff --git a/docs/work-items/ORCH-076/07-infra-requirements.md b/docs/work-items/ORCH-076/07-infra-requirements.md new file mode 100644 index 0000000..48cd92f --- /dev/null +++ b/docs/work-items/ORCH-076/07-infra-requirements.md @@ -0,0 +1,50 @@ +# 07 — Требования к инфраструктуре: ORCH-076 (ORCH-52c) + +Work Item: **ORCH-076** · Repo: **orchestrator** · Стадия: architecture + +## Сводка + +ORCH-52c — чисто кодово-документная задача (frontmatter-контракт + спека handoff). **Топология +инфраструктуры не меняется**: ни контейнеров, ни портов, ни volume, ни сети, ни CI-workflow. +Деплой — штатным путём конвейера через staging (8501) → прод (8500). Раздел существует для +фиксации двух операционных предусловий и одного конфиг-флага. + +## Изменения инфраструктуры + +- **Нет.** Compose-сервисы, порты (8500/8501), volume (`./data`, `./data/staging`), Gitea + Actions — без изменений. +- БД/миграции — нет (см. `08-data-requirements.md`). +- HTTP API — нет новых/изменённых эндпоинтов. + +## Конфигурация (env) + +| Ключ | Значение по умолчанию | Где | Назначение | +|------|----------------------|-----|------------| +| `ORCH_FRONTMATTER_VALIDATION_STRICT` | `false` | `.env` / `.env.staging` | Kill-switch строгой валидации схемы frontmatter. **На ORCH-52c держать `false`** (иначе self-block: доки ещё без полной схемы). Включается не раньше ORCH-52d. | + +> Флаг **аддитивный**; его отсутствие в окружении эквивалентно `false` (pydantic-дефолт +> `frontmatter_validation_strict: bool = False`). Явная установка не требуется на этой задаче; +> строка в `.env.example` добавляется документации ради. + +## Операционные предусловия + +### П-1. Лейбл `autoDeploy` (первый боевой автодеплой — ORCH-089) +На задаче выставлен лейбл `autoDeploy`: после зелёного staging и всех тех-гейтов орк **сам** +подтверждает прод-деплой (Фаза B ORCH-036/059), без ручного «Confirm Deploy». +- Предусловие: лейбл `autoDeploy` существует в Plane-проекте ORCH и проставлен на ORCH-076 + (инфра-предусловие ORCH-089). Его отсутствие = fail-safe → ручной гейт (деплой не сорвётся, + просто потребует ручного «Confirm Deploy»). +- BRD-гейт остаётся **ручным** (Слава подтверждает BRD) — `autoApprove` НЕ выставлен. +- Наблюдение: стадия `deploy` орка должна пройти через зелёные под-гейты ребра + `deploy-staging → deploy` (security → merge-gate → image-freshness → staging) до Фазы B — + `autoDeploy` физически не деплоит сломанное (BR-5 ORCH-089). Детали реакции на сбой — + `10-tech-risks.md` (R-3). + +### П-2. Self-hosting рестарт-дисциплина +Прод-контейнер `orchestrator` (8500) — общий для всех проектов. Деплой ORCH-52c проходит через +штатный detached host-хук (ORCH-036), **не** ручным `docker compose`. Ручной рестарт прод- +контейнера в рамках задачи **запрещён** (встанет конвейер enduro). Откат — `orchestrator-deploy-hook.sh --rollback` (стандартный путь), не предмет этой задачи. + +## Вне инфра-объёма +- Изменения промптов агентов, ретро-фит схемы в старые доки — ORCH-52d. +- Любые новые сервисы/демоны/cron — не вводятся. diff --git a/docs/work-items/ORCH-076/08-data-requirements.md b/docs/work-items/ORCH-076/08-data-requirements.md new file mode 100644 index 0000000..cdbb44d --- /dev/null +++ b/docs/work-items/ORCH-076/08-data-requirements.md @@ -0,0 +1,34 @@ +# 08 — Требования к данным / схеме БД: ORCH-076 (ORCH-52c) + +Work Item: **ORCH-076** · Repo: **orchestrator** · Стадия: architecture + +## Сводка + +**Изменений схемы БД нет.** Контракт frontmatter работает исключительно на **файлах** +(YAML-frontmatter номерных документов `docs/work-items//*.md`) и **in-memory** строках. +SQLite (`src/db.py`) — таблицы, индексы, миграции — **не затрагиваются** (TRZ §5). + +## Детали + +| Аспект | Состояние | +|--------|-----------| +| Новые таблицы | нет | +| Изменённые таблицы / колонки | нет | +| Индексы | нет | +| Миграции | нет (restart-safe без миграции) | +| Persistent state | нет (writer пишет в файлы доков, не в БД) | + +## Модель данных контракта (файлы, не БД) + +- **Обязательная frontmatter-схема** (машинный источник — `frontmatter.REQUIRED_FIELDS`): + `work_item`, `stage`, `author_agent`, `status`, `created_at`, `model_used`. Это контракт + **документа**, не строки БД. Фактическое проставление полей агентами — ORCH-52d (вне scope). +- **Вердикт-ключи** (читаются единым API, семантика 1:1): `verdict:` (12), `result:`/`verdict:`/ + `status:` (13, ORCH-047), `deploy_status:` (14), `staging_status:` (15), `security_status:` + (17), `post_deploy_status:` (16, информационный). Формат — ведущий YAML-блок `---…---`. + +## Совместимость данных +- Старые документы-вердикты **без** новой схемы остаются валидными (схема аддитивна; её + отсутствие не влияет на чтение вердикта — NFR-1). +- Формат writer'а (`render_frontmatter`) совместим с существующим + `split("---", 2)` + `yaml.safe_load` — старые и новые парсеры читают единообразно. diff --git a/docs/work-items/ORCH-076/10-tech-risks.md b/docs/work-items/ORCH-076/10-tech-risks.md new file mode 100644 index 0000000..40fcf66 --- /dev/null +++ b/docs/work-items/ORCH-076/10-tech-risks.md @@ -0,0 +1,25 @@ +# 10 — Технические риски: ORCH-076 (ORCH-52c) + +Work Item: **ORCH-076** · Repo: **orchestrator** · Стадия: architecture + +Информационный документ (гейтом не парсится). Источник истины по решениям — `06-adr/ADR-001`. + +| ID | Риск | Вероятн. | Влияние | Митигация | Остаточно | +|----|------|----------|---------|-----------|-----------| +| **R-1** | **Регресс чтения вердикта на гейте** (review/testing/staging/deploy/security) при переводе на единый `parse_frontmatter` → ложный откат/застревание → **остановка конвейера ВСЕХ проектов** (главный self-hosting риск). | средняя | критическое | Унифицируется только парс YAML, НЕ семантика (ADR D2); сигнатуры/`tuple[bool,str]`/токены/upper-case/fallback `worktree→origin/main` 1:1; reason-строки переносятся дословно через `FrontmatterParse`-состояния; **анти-регресс-тест на каждый из 5 гейтов** (старый док → тот же вердикт) + полный `pytest tests/` зелёный до мержа (AC-4/AC-6). | низкое | +| **R-2** | **Самоблокировка валидатором** на собственном деплое: доки ORCH-52c (и соседей) ещё без полной 6-польной схемы → strict-валидатор завалил бы гейт. | высокая (если включить strict) | высокое | `frontmatter_validation_strict` дефолт `False`; валидатор в default-режиме **вне вердикт-пути** гейтов (warning-only, чистый no-op для boolean); strict тестируется, но НЕ включается до ORCH-52d; `false` в `.env`/`.env.staging` (NFR-3, ADR D3). | очень низкое | +| **R-3** | **Первый боевой `autoDeploy`** (ORCH-089): орк сам подтверждает прод-рестарт после staging → если регресс R-1 проскользнул мимо тестов, автодеплой выкатит его без человеческой паузы. | низкая | высокое | `autoDeploy` достигает Фазы B только после зелёных под-гейтов ребра `deploy-staging→deploy` (security→merge-gate→image-freshness→staging) — не деплоит сломанное (BR-5 ORCH-089); обязательная страховка staging (8501); пост-деплой мониторинг ORCH-021 (`ALERT_ONLY` для self) ловит «зелёный деплой, красный прод» с откатом durable-freeze (ORCH-088); ручной BRD-гейт сохранён. Наблюдать стадию `deploy` вживую (Telegram-карточка). | низкое | +| **R-4** | **Дрейф reason-строк / сообщений гейтов** при рефакторе → тесты, ассертящие текст, краснеют; логи/Plane-комменты меняют формулировку. | средняя | низкое | Маппинг `FrontmatterParse → прежняя reason-строка` зафиксирован в ADR D2; переносить дословно; ассерты в анти-регресс-тестах фиксируют текущий текст. | низкое | +| **R-5** | **Расхождение спеки handoff с фактом кода** → «лживый» стандарт. | средняя | среднее | `HANDOFF_PROTOCOL.md` согласован 1:1 с `PIPELINE_DOCS.md` §2–§3 (тот же набор документов/ключей/гейтов); явная пометка «источник истины — код» (правило ORCH-075); reviewer сверяет (CLAUDE.md №2/№6). | низкое | +| **R-6** | **Скрытое исключение из writer/валидатора** прорывается в конвейер (нарушение never-raise) на битом вводе. | низкая | высокое | Контракт всего модуля never-raise (как действующий reader): любая ошибка → лог + безопасное значение; **тест на битом вводе** (невалидный YAML, не-mapping, I/O-ошибка) подтверждает отсутствие проброса (AC-5/NFR-2). | очень низкое | +| **R-7** | **Циклический импорт** при использовании `frontmatter` из `qg/checks.py`/`security_gate.py`/`post_deploy.py`/`review_parse.py`. | низкая | среднее | `frontmatter.py` — leaf без проектных зависимостей (только `logging` + ленивый `yaml`); импортируется, не импортирует проектные модули — циклов нет. | очень низкое | +| **R-8** | **Частичная унификация** (token-логика осталась в каждом гейте) воспринимается reviewer'ом как недовыполнение AC-3. | низкая | низкое | AC-3 требует «парсить YAML через единый API, а не дублированной логикой» — выполнено (D2); неунификация семантики — осознанный выбор безопасности, зафиксирован в ADR (альтернатива A1 отклонена). | очень низкое | + +## Сводные митигации (обязательные перед мержем) +1. Анти-регресс-тест на каждый из 5 вердикт-гейтов (старый док без схемы → прежний вердикт). +2. `tests/test_frontmatter.py`: reader (контракт неизменен) / writer (round-trip) / валидатор + (полный/неполный, strict on/off) / битый ввод → never-raise. +3. Полный `pytest tests/ -q` зелёный. +4. `frontmatter_validation_strict=False` в прод/staging env. +5. Живое наблюдение стадии `deploy` (первый `autoDeploy`); готовность к + `orchestrator-deploy-hook.sh --rollback` штатным путём (не ручной рестарт).