16 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-027 | analysis | analyst | ready-for-review | 2026-06-10 | claude-opus-4-8 |
01 — BRD (бизнес-требования): ORCH-027 — Code coverage как гейт (защита от деградации покрытия тестами)
Work Item: ORCH-027 · Repo: orchestrator · Стадия: analysis
1. Бизнес-контекст и проблема
Оркестратор ведёт автономную разработку: код пишет агент developer без человеческого
фильтра, а на стадии testing агент tester сам решает, достаточно ли тестов. Существующие
тестовые гейты проверяют только факт прохождения тестов, а не их полноту:
check_ci_green(реброdevelopment → review) — зелёный прогонpytest tests/в Gitea CI (.gitea/workflows/ci.yml), судит по exit-code, покрытие не меряет.check_tests_passed(реброtesting → deploy-staging) — читает machine-verdictresult:/verdict:/status:из13-test-report.md; это вердикт LLM-tester'а, а не измеренная метрика.- Merge-gate re-test (ORCH-043) — повторный
pytestна догнанной ветке, тоже только exit-code.
Ни один гейт не замечает, что фича добавила 300 строк кода и 0 тестов, или что багфикс
изменил поведение без регрессионного теста. При пакетном автономном прогоне (эпик ORCH-088,
«10–20 задач за ночь») это означает монотонную деградацию покрытия: каждая задача может
«срезать угол» на тестах, и за десятки задач проект тихо теряет тестируемость. Предложено
Стрим, одобрено Славой (00-business-request.md).
Задача вводит измеримый гейт покрытия: покрытие тестами измеряется инструментально и не должно опускаться ниже политики (абсолютный порог и/или «не ниже базовой линии»). Это структурная защита от деградации, аналогичная по духу security-гейту (ORCH-022) — детерминированная метрика вместо доверия суждению агента.
Self-hosting. Гейт работает на инструменте, который в проде обслуживает все проекты из общей БД и очереди (
CLAUDE.md§self-hosting). Измерение покрытия — это исполнение тест-сьюта в изолированном worktree; оно не трогает прод-контейнер и не касаетсяmain.
2. Объём (scope)
В объёме
- Инструментальное измерение покрытия тестами для репозитория
orchestrator(стек Python / pytest) перед слиянием ветки задачи вmain. - Гейт-решение: покрытие не ниже заданной политики порога. Политика поддерживает два режима:
абсолютный порог (
%) и «не ниже базовой линии» (no-regression / ratchet), а также их комбинацию. - Хранение и обновление базовой линии покрытия (last-known покрытие
main). - Наблюдаемость результата: артефакт-отчёт о покрытии с machine-readable вердиктом, строка в
GET /queue, сигнал в Telegram при провале. - Конфигурируемость: kill-switch + per-repo область + настраиваемый порог/политика + поведение при ошибке инструмента (fail-open/closed).
Вне объёма
- Реализация измерения покрытия для НЕ-Python стеков (jest / jacoco для будущих репозиториев) — фактическая интеграция инструментов оставлена на будущее; в ORCH-027 закладывается лишь расширяемость (политика и хранилище не должны быть жёстко завязаны на Python).
- Изменение существующей семантики
check_ci_green/check_tests_passed/check_reviewer_verdictдля репозиториев, где гейт покрытия выключен. - Принудительное доведение покрытия до 100% или установка агрессивного абсолютного порога — стартовая политика консервативна (см. NFR-4).
- Покрытие самих тестовых файлов и мутационное тестирование.
- Выбор конкретного инструмента/механизма интеграции и его расположения в конвейере как
архитектурного решения — это зона архитектора (
06-adr/); BRD/ТЗ фиксируют требования и кандидатные точки, выведенные из фактического кода.
3. Заинтересованные стороны
- Заказчик / инициатор: Стрим (предложение), Слава (одобрение).
- Затрагиваются: конвейер
orchestrator(self-hosting); агентыdeveloper/tester(теперь обязаны держать покрытие); проект enduro-trails — не должен быть затронут (гейт по умолчанию неактивен вне сконфигурированных репозиториев). - Принимает результат: reviewer (стадия
review) + финальная стадия конвейера; владелец (Owner) — по факту работы гейта в проде.
4. Бизнес-требования (BR)
- BR-1 — Измерение покрытия. Перед слиянием ветки задачи в
mainпокрытие тестами репозитория измеряется инструментально (исполнением тест-сьюта под coverage-инструментацией), а не оценивается на глаз. Результат — числовая метрика покрытия (как минимум line coverage). - BR-2 — Гейт деградации. Если измеренное покрытие нарушает политику (ниже абсолютного
порога ИЛИ ниже базовой линии — в зависимости от выбранного режима), конвейер не
пропускает задачу дальше к деплою и инициирует штатный откат на
developmentдля доработки тестов. - BR-3 — Базовая линия (ratchet). Поддерживается режим «не ниже предыдущего»: гейт
сравнивает покрытие ветки с зафиксированной базовой линией
main. Базовая линия обновляется вверх при успешном слиянии задачи вmain(покрытие может только расти или держаться, но не падать). - BR-4 — Конфигурируемость и нулевая регрессия. Гейт управляется kill-switch'ем и
per-repo областью (по образцу
merge_gate/security_gate/image_freshness, ORCH-035/043/058). Для репозиториев вне области (в частности enduro-trails) гейт — полный no-op, поведение конвейера 1:1 как до задачи. Порог, политика (absolute|baseline|both) и поведение при ошибке инструмента — настраиваемы. - BR-5 — Наблюдаемость. Результат измерения виден: (а) артефакт-отчёт о покрытии с
machine-readable вердиктом в
docs/work-items/<id>/; (б) read-only блок вGET /queue; (в) уведомление в Telegram при провале гейта (кликабельный номер задачи, как у прочих алертов). Сообщение указывает измеренное покрытие, порог/базовую линию и дельту. - BR-6 — Стек-расширяемость. Логика политики (PASS/FAIL по метрике/базовой линии) и хранилище базовой линии не зависят от конкретного инструмента; добавление измерителя для другого стека (jest/jacoco) в будущем не требует переписывания ядра гейта.
5. Нефункциональные требования (NFR)
- NFR-1 — never-raise / fail-safe. Ядро гейта — изолированный leaf-модуль (по образцу
src/security_gate.py,src/serial_gate.py,src/labels.py): любая внутренняя ошибка обрабатывается, исключение никогда не всплывает вadvance_stageи не роняет конвейер всех проектов. - NFR-2 — Поведение при недоступности/ошибке инструмента. По умолчанию ошибка измерения (coverage-инструмент упал/недоступен) → fail-open + громкий warning (анти-петля, прецедент ORCH-061/ORCH-022 dep-audit), переключаемое в fail-closed флагом. Дефолт не должен заклинивать автономный конвейер из-за инфраструктурного сбоя.
- NFR-3 — Self-hosting безопасность. Гейт только исполняет тесты в изолированном worktree,
читает метрику, пишет отчёт и принимает решение. Он никогда не вызывает деплой-хук, не
перезапускает прод-контейнер, не пушит/форс-пушит в
main. - NFR-4 — Консервативный старт (анти-флап). Стартовая политика не должна массово заворачивать
существующие задачи: базовая линия инициализируется фактическим покрытием
main, абсолютный порог — как мягкий backstop. Допускается малый отрицательный допуск (epsilon) на шум измерения, чтобы дрожание ±доли процента не заворачивало задачу. - NFR-5 — Совместимость.
STAGE_TRANSITIONS, состав/семантикаQG_CHECKSиcheck_*, machine-verdict ключи существующих доков (verdict:/result:/deploy_status:/staging_status:/security_status:) — не меняются. Любая новая БД-сущность — аддитивна (без миграции существующих таблиц). Restart-safe. - NFR-6 — Детерминизм. Решение гейта — чистая функция от (измеренное покрытие, базовая линия, порог, политика); без участия LLM в критическом пути (как security/merge/image-freshness под-гейты).
6. Допущения и ограничения
- Тест-сьют
orchestratorзапускается командойpython -m pytest tests/из корня репозитория (подтверждено.gitea/workflows/ci.yml,pytest.initestpaths = tests); измерение покрытия накладывается на этот же прогон. - Coverage-инструмент для Python (
coverage.py/pytest-cov) добавляется как pip-зависимость; он не требует сети во время измерения. - Репозиторий
orchestrator— единственный self-hosting (предикатis_self_hosting_repo); стартовая область гейта — он. enduro-trails и прочие репозитории по умолчанию вне области. - Базовая линия привязана к покрытию
main; её первичная инициализация выполняется один раз (bootstrap) фактическим замером текущегоmain. - Тесты исполняются в per-branch worktree (
ensure_worktree), что безопасно при параллельных активных задачах (прецедентcheck_tests_local/merge-gate re-test).
7. Критерии успеха
- Покрытие тестами
orchestratorизмеряется на каждой задаче и не может опуститься ниже политики, не заблокировав продвижение к деплою. - При выключенном флаге / вне области — конвейер ведёт себя 1:1 как до ORCH-027 (нулевая регрессия для enduro-trails).
- Сбой coverage-инструмента не заклинивает автономный конвейер (дефолт fail-open + warning).
- Результат измерения прозрачен (отчёт +
GET /queue+ Telegram при провале).
Детальные PASS/FAIL — 03-acceptance-criteria.md.
8. Риски
- Флап на шуме измерения — недетерминированное покрытие (например, зависящее от порядка/ окружения) может дрожать у границы → ложные заворота. Митигировать epsilon-допуском (NFR-4).
- Петля заворотов — слишком высокий абсолютный порог завернёт многие задачи в бесконечный rework. Митигировать консервативной стартовой политикой и baseline-режимом.
- Гонка базовой линии при параллельных слияниях — два слияния в
mainмогут конкурентно обновлять baseline. Требуется атомарное/сериализованное обновление (опереться на окно сериализации merge-lease, ORCH-043). - Инфраструктурная хрупкость — coverage-инструмент недоступен/несовместим с версией pytest → закрыто требованием NFR-2 (fail-open + warning).
Детальная техническая проработка рисков — 10-tech-risks.md (заполняет архитектор).