Files
orchestrator/docs/work-items/ORCH-088/10-tech-risks.md

7.3 KiB
Raw Blame History

10 — Технические риски: ORCH-088 (Serial gate)

Work Item: ORCH-088 · Repo: orchestrator · Стадия: architecture Связь: ADR 06-adr/ADR-001-serial-gate.md, ТЗ 02-trz.md §11-12, BRD §8.

Оценка: Вероятность (Н/С/В) × Влияние (Н/С/В). Self-hosting: «Влияние В» = риск для конвейера ВСЕХ проектов (общий прод/БД/очередь).

ID Риск Вер. Влия. Митигейшн
R-1 Stale-base сохраняется, если ветка режется на входе (_create_gitea_branch в start_pipeline) до завершения предшественника — гейт только claim не лечит (BRD R-1, AC-6 FAIL). С В Relocation среза ветки на claim analyst-job (ADR D1): start_pipeline не создаёт ветку для применимого репо; ensure_worktree режет от свежего origin/main уже после открытия gate. Тест AC-6: git merge-base --is-ancestor <validated_sha A> <base B>.
R-2 Gate, ошибочно fail-closed на транзиентной ошибке БД в hot-claim, заклинивает очередь ВСЕХ проектов (enduro встаёт). С В build_claim_clause обёрнут в try/except → ошибка построения = пустой фрагмент = claim без gate (fail-open, ADR D10/AC-8). Freeze fail-closed применяется только в Python-слое, не в тотальном сбое hot-claim. Unit-тест: исключение в построении clause → claim не падает, выбирает job.
R-3 «Вечный freeze» / залипшая Blocked-задача останавливает пакет незаметно (D6 — Blocked держит gate). С С Наблюдаемость GET /queue блок serial_gate (active_task, waiting, frozen+reason); Telegram-алерт при выставлении freeze и карточка « ждёт …»; ручное снятие — простой эндпоинт POST /serial-gate/unfreeze (D4). Оператор видит причину застоя.
R-4 Async→sync релокация _create_gitea_branch/_create_initial_docs: эти вызовы async (httpx) в start_pipeline, а launcher _spawn — sync. Неверная обёртка → исключение/блок event-loop. С С Developer оборачивает Gitea-API для sync-контекста (httpx sync client / asyncio.run в отдельном пути). Контракт launcher never-raise: сбой материализации ветки → лог + job в retry, прод не трогается. Тест: claim analyst-job создаёт ветку и worktree без падения.
R-5 Потребитель ожидает материализованную ветку до запуска analyst (между start_pipeline и claim ветки нет): Telegram-карточка / Plane-sync / reconciler могут предполагать существование ветки. Н С Проверено: трекер/Plane-sync используют branch как строку имени, не git-ref. Перед разработкой — аудит читателей tasks.branch/Gitea-ветки на стадии до analyst. start_pipeline по-прежнему пишет branch в task-row (имя), не материализуя ref.
R-6 SQL-инъекция / поломка clause через serial_gate_repos CSV при встраивании в IN (...). Н С Санитизация repo-токенов ^[A-Za-z0-9._-]+$ в build_claim_clause (ADR D8); невалидный токен дропается. CSV — операторский конфиг (не пользовательский ввод), риск низкий, но гард обязателен. Unit-тест на мусорный CSV.
R-7 Rework-analyst блокирует сам себя: rejection-path start_pipeline re-enqueue analyst активной задачи; наивный gate «есть незавершённая задача репо» удержал бы её навсегда. С В Условие t2.id != jobs.task_id (ADR D1) — учитываются только другие задачи. Unit-тест: rework-analyst задачи A при единственной незавершённой A — claim проходит.
R-8 Freeze не учитывает уже-done задачу: деградировавшая задача к моменту DEGRADED уже stage='done' (BR-7) ⇒ обычный gate её не удержит, следующая стартует до выставления freeze (гонка). Н С Freeze — отдельный durable сигнал (repo_freeze), не зависит от stage (ADR D2/D3). set_repo_freeze вызывается в DEGRADED-ветке монитора; до снятия gate закрыт безусловно. Возможная узкая гонка «done→claim next до записи freeze» приемлема Этапом 1 (следующий тик уже видит freeze); при необходимости — выставлять предупредительный freeze в начале окна мониторинга (вне скопа).
R-9 Default-all область неожиданно сериализует enduro (меняет поведение при включении флага). С Н Осознанное решение (ADR D5, ТЗ §8): enduro выигрывает от serial e2e; max_concurrency=1 и так ограничивает параллелизм. Оператор может сузить ORCH_SERIAL_GATE_REPOS=orchestrator. «Нулевая регрессия» гарантирована при выключенном kill-switch (NFR-4).
R-10 Миграция на общей прод-БД (repo_freeze) роняет init при неудачном порядке/блокировке. Н В CREATE TABLE IF NOT EXISTS + idempotent index; выполняется в существующем init-пути схемы; аддитивно, не трогает enduro-строки. Прод-контейнер не рестартится механизмом gate (NFR-6).

Сводный вывод

Архитектурно безопасных блокеров нет. Критические векторы — R-1 (закрыт relocation среза ветки), R-2/R-7 (закрыты fail-open hot-claim и t2.id != jobs.task_id). Все механизмы аддитивны, под kill-switch, never-raise, не рестартят прод. Главный операционный риск — R-3 (ручной freeze), смягчён наблюдаемостью и алертами. Реализация — стандартный путь стадии development без эскалации arch:major-change (нет новой стадии/QG/смены БД-контракта; новая таблица аддитивна).