7.3 KiB
7.3 KiB
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/смены БД-контракта; новая таблица аддитивна).