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

8.5 KiB
Raw Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-114 architecture architect proposed 2026-06-15 claude-opus-4-8

10 — Технические риски: ORCH-114 — Durable transition-ownership lease + expected-stage CAS

Work Item: ORCH-114 · Repo: orchestrator · Стадия: architecture

Информационный (гейтом не парсится). Риски реализации и митигейшн. Сверено по коду.

Реестр рисков

ID Риск Вер. Влия. Митигейшн
TR-1 Дедлок / over-block: «жёсткое» владение заклинивает легитимный путь — reaper не добивает зависший finalizer, задача висит нетерминальной с удержанным lease (клинит serial-gate репо). Сред. Выс. ADR D3/D8: liveness = pid+boot (мёртвый владелец реклеймится немедленно); Tier-3 reaper_max_running_s игнорирует lease и добивает зависшего живого; reaper при реапе force-освобождает lease. release в try/finally. Опц. POST /transition-lease/release. Обязательный тест AC-3 (release на исключении/откате) + AC-5 (bounded reclaim).
TR-2 Lease «течёт» при исключении/откате в advance_stage → задача навсегда заблокирована. Сред. Выс. release строго в finally вокруг всего side-effectful региона (ADR D5); регресс-тест AC-3 (acquire→raise→release). Бэкстоп: stale-реклейм по pid/boot + Tier-3.
TR-3 Buggy «smart recovery» сам становится источником двойного применения необратимого шага после рестарта. Сред. Выс. ADR D7: НЕ новый recovery-мозг, а композиция requeue_running_jobs + stale-clear + существующие авторитетные факты (SHA-in-main ORCH-071/073, INITIATED ORCH-036, coverage-ratchet CAS). Обязательный restart-recovery регресс (BR-8/AC-6): процесс убит в середине финализации → ровно один исход, без второго merge_pr/ratchet/deploy.
TR-4 Скрытые обходные пути (gitea handle_push/handle_ci_status/handle_pr, plane _rollback_stage:806) пишут стадию мимо advance_stage → CAS-инвариант обходится. Выс. Сред. ADR D4: 6 обходных update_task_stage переведены на update_task_stage_cas; граница зафиксирована в ADR. Структурный аудит (AC-11): ни одного безусловного update_task_stage на side-effectful/конкурентных путях при флаге on.
TR-5 Гонка consult→acquire: актор A прошёл guard «lease свободен», но B захватил между проверкой и acquire A. Сред. Сред. Двойной слой (ADR D1): даже если оба прошли consult, acquire атомарен (rowcount-guard, один INSERT выигрывает), а проигравший CAS на коммите не пишет стадию и не делает side-effect. Consult — лишь дешёвый front-defer, не источник истины.
TR-6 Multi-process (--workers>1): pid+boot-liveness и SQLite-CAS на одной БД корректны для одного процесса; ввод воркеров потребует верификации (SQLite write-lock contention, boot-id на процесс). Низ. Сред. Вне объёма (BRD §scope: модель остаётся одно-процессной). Durable-форма (таблица + pid/boot + CAS) спроектирована совместимой; epoch-колонка — документированное форвард-расширение (ADR D2). Зафиксировано как ограничение в adr-0045 «Последствия ()».
TR-7 Бюджетный конфликт: lease, удерживаемый дольше reaper_max_running_s, нарушает сквозной инвариант ORCH-065/109/110/113. Низ. Выс. ADR D8: lease без собственного TTL, потолок = Tier-3 reaper_max_running_s (5400); reaper_finalize_grace_s/reaper_max_running_s НЕ меняются; инвариант 5400 > Σ(≈4460)+grace цел. Новых бюджетных констант нет.
TR-8 Регрессия ORCH-113: обобщение finalizer_liveness ломает его контракт/тест или меняет поведение при выключенном ORCH-114. Сред. Сред. ADR D6: finalizer_liveness.py не правится, остаётся поведением kill-switch-off (reaper → in-memory, Tier-2/deploy-staging); on → durable cross-path. Зелёный существующий тест ORCH-113 + AC-9 (флаг off → байт-в-байт). Сверка маркеров ORCH-113 (TRACEABILITY/ORCH-078).
TR-9 Сбой механизма заклинивает общую очередь всех проектов (enduro + orchestrator), нарушая AC-8 ORCH-088. Низ. Выс. ADR D9: hot-path claim_next_job не трогается (lease консультируется на пути перехода/reaper/reconciler/webhook, не в claim). never-raise; acquire/guard-ошибка → defer (не клин), CAS-ошибка → аборт записи. Регресс AC-10.
TR-10 Ложная stale-реклейм живого владельца (pid переиспользован ОС после рестарта, boot-id совпал случайно). Низ. Сред. owner_boot_id — достаточно энтропийный нонс старта процесса (не предсказуемый), плюс pid-проверка; коллизия (тот же boot-id И тот же pid у нового процесса) практически невозможна. Бэкстоп: CAS на коммите не даст двойной записи даже при ложном реклейме.

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

Доминирующий класс рисков — корректность владения и восстановления на необратимых рёбрах (TR-1/TR-2/TR-3) и полнота охвата путей (TR-4/TR-5). Все они снимаются архитектурой defense-in-depth (lease на входе + CAS на коммите) и принципом «не строить новый recovery-мозг, опереться на существующие авторитетные факты» (D7) — это сознательно минимизирует площадь нового кода, способного двоить необратимый шаг.

Эскалация arch:major-change: рекомендуется. Изменение вводит новый durable-компонент (leaf transition_lease + таблица), трогает движок переходов и ≥5 фоновых акторов и помечается сводным сквозным ADR (adr-0045). Реализующему агенту (developer) обязательны: регресс двойного эффекта (AC-1, red→green), restart-recovery (AC-6), kill-switch-off байт-в-байт (AC-9), сохранность бюджета (AC-5/NFR-6) и аудит обходных путей (TR-4/AC-11). Возврата в анализ не требуется — требования полны и реализуемы без нарушения архитектурных принципов (всё в Docker/одном процессе/SQLite, без новых внешних зависимостей и без рестарта прода). Остаточный риск для прод-конвейера (self-hosting) при дисциплине тестов — низкий; единственное осознанное ограничение — multi-process (TR-6), явно вне объёма.