Files
orchestrator/docs/work-items/ORCH-114/02-trz.md
claude-bot a685e0cbc9
All checks were successful
CI / test (push) Successful in 1m6s
analyst(ET): auto-commit from analyst run_id=708
2026-06-15 15:57:22 +03:00

16 KiB
Raw Blame History

work_item, stage, author_agent, status, created_at, model_used, escalate
work_item stage author_agent status created_at model_used escalate
ORCH-114 analysis analyst ready-for-review 2026-06-15 claude-opus-4-8 full-cycle

02 — ТЗ (TRZ): ORCH-114 — Ownership-lease для side-effectful переходов стадий + умное восстановление при старте

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

ТЗ описывает конкретные требования к реализации, выведенные из BRD (01-brd.md) и фактического кода. Выбор механизма (durable lease / heartbeat / transition-epoch / форма хранения, набор покрываемых рёбер) и архитектурное обоснование — задача архитектора (06-adr/). Здесь — что должно быть истинно и какие модули затрагиваются, не как именно.

1. Сводка изменения

Вводится единый инвариант владения side-effectful переходом стадии: запись стадии и исполнение тяжёлых под-гейтов/финализации защищаются durable-механизмом владения (lease/epoch) + CAS на запись стадии, так что в любой момент переход конкретной задачи доводит ровно один актор, а конкурентный/после-рестартовый повторный вход (reaper / reconciler / webhook / startup-requeue) откладывается или становится no-op вместо повторного применения необратимых эффектов (merge_pr / coverage-ratchet / image-rebuild / инициация прод-деплоя / противоречивый rollback↔done). Аддитивно, под kill-switch, never-raise; STAGE_TRANSITIONS / QG_CHECKS / check_* / вердикт-ключи / схемы существующих таблиц — не трогаются (обобщает и делает durable процесс-локальный finalizer_liveness ORCH-113).

2. Задействованные модули / пути

Путь Действие Назначение в ORCH-114
src/stage_engine.py изменить advance_stage: захват владения на границе side-effectful перехода/финализации; CAS на запись стадии; release в try/finally; проигравший — чистый аборт. Покрыть _handle_merge_verify, под-гейты deploy-staging→deploy, run_deploy_finalizer (Phase C), advance_if_gate_passed (F-1).
src/db.py изменить CAS-вариант записи стадии (запись только при совпадении ожидаемой текущей стадии/эпохи; rowcount-результат). Durable-хелперы владения (acquire / heartbeat-touch / release / reclaim / snapshot) — форму хранилища задаёт архитектор.
src/finalizer_liveness.py изменить/обобщить Отправная точка: обобщить process-local реестр до durable, кросс-путевого владения (или надстроить durable-слой поверх). Сохранить контракт never-raise + kill-switch.
src/job_reaper.py изменить Tier-2/Tier-3 осведомлены о durable-владении на всех релевантных путях (не только Tier-2/deploy-staging): defer при живом, реклейм мёртвого/устаревшего в ограниченное время (NFR-6).
src/queue_worker.py изменить requeue_running_jobs / стартовое восстановление сверяется с durable-владением: не пере-исполнять уже применённый необратимый шаг (умное восстановление). claim_next_job — не вносить сетевых зависимостей.
src/reconciler.py изменить F-1 (advance_if_gate_passed) — defer при активном lease перехода (по образцу skip-guard'ов escalated/Blocked/deps).
src/webhooks/plane.py изменить Пути продвижения (_try_advance_stage, Approved / Confirm Deploy) — defer при активном lease.
src/webhooks/gitea.py изменить (учесть) Прямые записи стадии в обход advance_stage (handle_push/handle_ci_status/handle_pr) должны попадать под тот же CAS-инвариант либо явно исключаться архитектором (граница в ADR).
src/main.py изменить Порядок старта демонов / точка восстановления; read-only блок в GET /queue; опц. operator-эндпоинт реклейма.
src/config.py изменить Kill-switch(и) + бюджеты/таймауты владения + (опц.) CSV-скоуп репо.
tests/test_orch114_transition_ownership.py создать Покрытие FR-1…FR-7 (см. 04-test-plan.yaml).

3. Функциональные требования

FR-1 — Единое владение side-effectful переходом (BR-1, BR-6)

На границе, где начинается side-effectful финализация/переход (минимум: под-гейты deploy-staging→deploy, _handle_merge_verify на deploy→done, Phase C run_deploy_finalizer), актор захватывает владение задачей. Пока владение активно, другой актор не исполняет тот же переход. Release — детерминированно в try/finally (в т.ч. на исключении/откате). Владение durable (NFR-4): переживает рестарт и доступно для проверки другому актору/новому процессу.

FR-2 — Compare-and-swap / epoch на запись стадии (BR-2)

Запись стадии для side-effectful переходов выполняется только если предусловие (ожидаемая текущая стадия и/или эпоха перехода) не изменилось с момента чтения. Реализуется через CAS-вариант update_task_stage (UPDATE … SET stage=?[, epoch=epoch+1] WHERE id=? AND stage=?[ AND epoch=?], решение по форме — архитектор). Проигравший гонку писатель получает «lost-race» результат, не мутирует стадию и не выполняет ни одного побочного эффекта (merge_pr / ratchet / rebuild / deploy-init / enqueue следующего агента). Инвариант распространяется и на пути, пишущие стадию в обход advance_stage (gitea-webhook), — либо CAS, либо явное исключение (граница в ADR).

FR-3 — Reaper, осведомлённый о владении на всех путях (BR-3, NFR-6)

Job-reaper перед реклеймом сверяется с durable-владением не только в Tier-2 для deploy-staging (текущая область ORCH-113), а на всех релевантных тирах/рёбрах: живой владелец → defer (лог + счётчик, без повторного advance); мёртвый/устаревший владелец → реклейм в ограниченное время (Tier-3 backstop reaper_max_running_s добивает зависшего; маркер владения backstop не обходит инвариант бюджета). Сохранить атомарный reap_running_job rowcount-guard.

FR-4 — Умное восстановление при старте (BR-4, BR-6, NFR-7)

Стартовое восстановление (requeue_running_jobs + последующий цикл) использует durable-владение/эпоху, чтобы детерминированно различить: (a) финализация не начиналась / безопасно перезапустить → re-drive; (b) необратимый шаг уже применён (мерж в main / ratchet / прод-деплой инициирован) → сойтись к done/консистентному исходу без повторного применения. Источник истины для «уже применено» — авторитетные durable-факты (SHA-in-main ORCH-071/073, маркер INITIATED self-deploy, durable-lease/эпоха), а не in-memory состояние.

FR-5 — Skip/defer в reconciler и webhook (BR-5)

Reconciler F-1 (advance_if_gate_passed) и webhook-пути продвижения (plane._try_advance_stage, Approved/Confirm Deploy) при активном lease перехода для задачи откладывают действие (silent skip + наблюдаемость), по образцу существующих skip-guard'ов F-1 (escalated / Blocked / task-deps). Fail-safe: неопределённость состояния lease → консервативный skip (не дублировать).

FR-6 — Наблюдаемость (BR-7)

Аддитивный read-only блок в GET /queue (по образцу serial_gate/merge_gate/reaper): держатели lease, возраст владения, defer-счётчики, форсированные/устаревшие реклеймы. Алерт (send_telegram, кликабельный номер) на форсированный/устаревший реклейм. Опц. запись в lessons-journal (ORCH-098, source="auto"). Опц. operator-эндпоинт ручного реклейма (по образцу POST /serial-gate/unfreeze).

FR-7 — Конфигурация, обратимость, never-raise (BR-9, NFR-1, NFR-2, NFR-8)

Все публичные функции владения — never-raise (ошибка → безопасный дефолт + WARNING). Kill-switch возвращает поведение байт-в-байт к до-ORCH-114 (lease не пишется/не читается, CAS вырождается в прежний безусловный update_task_stage). Hot-path — fail-open; prod-safety — fail-closed.

4. Изменения API

  • GET /queue — аддитивный read-only блок владения переходом (имя ключа уточнит архитектор, напр. transition_ownership / transition_lease). Существующие ключи /queue — байт-в-байт.
  • (Опционально, по решению архитектора) POST /transition-lease/release?work_item=<id> — операторский ручной реклейм застрявшего владения (паттерн POST /serial-gate/unfreeze).
  • GET /metrics (ORCH-099) — при необходимости аддитивное поле без бампа schema_version (sidecar обязан толерировать незнакомые ключи). Прочие эндпоинты — не трогаются.

5. Изменения схемы БД

Требование (форму выбирает архитектор, 06-adr/ + 08-data-requirements.md): для durable-владения (NFR-4) и CAS/epoch (FR-2) требуется аддитивное, идемпотентное durable-состояние. Кандидаты (не предписание):

  • доп. аддитивная таблица владения (CREATE TABLE IF NOT EXISTS, паттерн repo_freeze/ coverage_baseline/lessons) с (task_id/job_id, owner, run_id, stage, acquired_at, heartbeat_at, expires_at); либо
  • аддитивные колонки на tasks/jobs (_ensure_column, паттерн tasks.track/tasks.cancelled_at), включая возможную epoch/version-колонку для CAS.

Жёсткие ограничения (NFR-3): только аддитивно/идемпотентно; схемы существующих таблиц (tasks/jobs/agent_runs и пр.) — байт-в-байт; никаких изменений существующих столбцов/индексов, ломающих обратную совместимость; restart-safe инициализация в init_db().

6. Требования к новым/изменённым QG checks

Нет. QG_CHECKS / check_* / _parse_* / машинные вердикт-ключи — не трогаются. Владение переходом — свойство движка переходов и фоновых акторов, а не Quality Gate и не стадия (аналогично тому, как merge-lease/serial-gate/finalizer-liveness — врезки/leaf'ы, а не QG). Никаких новых стадий/рёбер в STAGE_TRANSITIONS.

7. Совместимость / регресс

  • Kill-switch (новый флаг в config.py, env ORCH_*): False → CAS вырождается в прежний безусловный update_task_stage, lease не пишется/не читается, reaper/reconciler/webhook ведут себя как до ORCH-114 — байт-в-байт (включая зелёный существующий pytest tests/).
  • Область раската: по образцу leaf-гейтов; durable-lease минимально применяется к self-hosting рёбрам (deploy-staging/deploy→done, где живут необратимые эффекты); generic-CAS инертен при отсутствии гонки (нулевая стоимость на не-затронутых переходах). Точную область фиксирует архитектор.
  • Обратимость: механизм аддитивен и изолирован; откат = выключить kill-switch (durable-таблица/ колонки остаются инертными).
  • never-raise / fail-open / fail-closed: hot-path claim/guard — fail-open (не клинить общую очередь, AC-8 ORCH-088); prod-safety/необратимость — fail-closed; любой сбой механизма — WARNING + безопасный дефолт.
  • Сквозной бюджет: lease согласован с reaper_max_running_s/reaper_finalize_grace_s (ORCH-065/ 109/110/113) — не удлиняет финализацию за backstop; устаревший владелец добивается в ограниченное время.
  • Маркеры трассировки (ORCH-078): правки в блоках с маркерами ORCH-065/109/110/111/113 (reaper, finalizer-liveness, merge-gate) сверяются с их ADR перед изменением; новый код помечается ORCH-114.
  • enduro-trails: при флаге off / репо вне области — нулевая регрессия.