Files
orchestrator/docs/overview/tech-architecture.md
claude-bot c4a97a7a28 fix(stage-engine): address ORCH-114 review — env/docs canon + in-region rollback CAS
Resolves the REQUEST_CHANGES findings on ORCH-114 (durable transition-ownership
lease + expected-stage CAS):

P1 — documentation = golden source:
- .env.example: add ORCH_TRANSITION_LEASE_ENABLED / ORCH_TRANSITION_LEASE_REPOS
  (canon of 100% start keys, ORCH-101), next to the other gate kill-switches.
- CLAUDE.md: add the ORCH-114 passport section (mechanism, invariant, flags,
  ADR links) so a future agent editing advance_stage/reaper/webhooks finds the
  ownership invariant in the first mandatory-read doc (ORCH-078 traceability index).

P2 — should-fix:
- docs/overview/ (system showcase, ORCH-011): add transition_lease to
  tech-data-model.md (helper tables), tech-observability.md (/queue blocks) and
  tech-architecture.md (components).
- ADR-001 D4 alignment: the four side-effectful-edge rollback handlers
  (_handle_merge_gate_rollback / _handle_security_gate / _handle_coverage_gate /
  _handle_image_freshness) now write `development` through the expected-stage CAS
  via a shared _rollback_stage_cas helper (defence against the rollback↔done
  contradiction, BR-6) instead of a bare unconditional update_task_stage. Under the
  held lease the sole owner always wins; a lost race aborts WITHOUT side effects.
  Kill-switch off / out-of-scope repo -> degenerates to the prior write -> 1:1.
- Test isolation: make tests/test_webhooks.py order-independent by pinning the
  proj-1 registry per-test (mirrors test_webhook_dedup.proj_registry); it had only
  passed by relying on import order. Drop the needless module-level ORCH_DB_PATH
  setdefault in test_orch114 (fresh_db already isolates db_path).

New regression tests (TC-11): in-region rollback writes route through CAS;
rollback CAS wins when at expected stage; rollback CAS-lost does NOT clobber `done`;
kill-switch-off rollback degenerates to the unconditional write.

ruff clean (src/stage_engine.py, src/transition_lease.py); full suite 2052 passed.

Refs: ORCH-114
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:28:38 +03:00

6.2 KiB
Raw Blame History

Блок 1. Архитектура: компоненты и связи

Обзорный уровень. Полная таблица компонентов с деталями и историей решений — в инженерном справочнике («Компоненты») и internals; здесь — цельная картина, как части складываются в конвейер.

Поток одной задачи

            Plane (трекер)            Gitea (git/CI)
                 │ вебхук                  │ вебхук
                 ▼                         ▼
        ┌────────────────────────────────────────┐
        │   FastAPI-приём (HMAC-подпись, дедуп)  │
        └───────────────────┬────────────────────┘
                            ▼
   вебхук → очередь (jobs) → агент (Claude CLI в worktree) → гейт (QG) → переход стадии
                            ▲                                              │
                            └────────── следующая стадия / откат ◄─────────┘

Каждое продвижение задачи — один и тот же цикл: событие принято → в очередь поставлен job → worker запустил агента стадии → результат проверен гейтом качества → state machine перевела задачу на следующую стадию (или откатила назад).

Компоненты

Компонент Роль
Webhook-приёмники (src/webhooks/) Принимают события Plane (статусы задач) и Gitea (push, PR, CI). Проверяют HMAC-подпись, дедуплицируют повторные доставки.
Очередь задач (jobs + worker) Собственная очередь на SQLite: атомарный захват job'а, ретраи с backoff, зависимости между job'ами, ограничение параллелизма.
State machine (src/stages.py) Карта стадий STAGE_TRANSITIONS: для каждой стадии — следующая, агент и гейт выхода. Единственный источник истины о конвейере.
Stage engine (src/stage_engine.py) Исполняет переходы: диспетчеризация гейтов, откаты, под-гейты деплойного ребра, синхронизация статусов с Plane.
Transition-lease (src/transition_lease.py) Durable-владение side-effectful переходом стадии: один владелец на задачу (lease на входе + expected-stage CAS на записи), liveness по pid+boot-id. Не даёт конкурентному или после-рестартовому повторному входу дважды применить необратимый эффект (merge / деплой / ratchet).
Agent launcher (src/agents/launcher.py) Запускает Claude CLI агента в изолированном git worktree ветки задачи, следит за процессом (watchdog), авто-продвигает стадию по завершении.
Реестр гейтов (src/qg/checks.py) QG_CHECKS — машинные проверки выхода со стадий; вердикты читаются только из YAML-frontmatter артефактов.
Plane-sync (src/plane_sync.py) Индикация статусов в Plane (слой «показать человеку», никогда не управление конвейером).
Notifications (src/notifications.py) Живая Telegram-карточка задачи и алерты.

Фоновые демоны (самовосстановление)

Поднимаются в lifespan FastAPI-приложения (src/main.py) и работают рядом с конвейером:

  • reconciler — находит расхождения «БД говорит одно, реальность другое» (зависшие стадии, потерянные ветки) и возвращает задачи в консистентное состояние;
  • job-reaper — возвращает в очередь job'ы, чей исполнитель умер (упавший процесс, рестарт);
  • disk-watchdog — следит за местом на диске, чистит устаревшие worktree;
  • build-cache-pruner — прибирает докер-кэш сборок.

Отдельно от приложения живёт sidecar-watchdog — независимый контейнер-наблюдатель: следит за самим оркестратором снаружи (health, метрики) и шлёт алерты в собственный Telegram-канал. Наблюдатель сознательно отделён от наблюдаемого: падение оркестратора не валит сторожа.

Изоляция работы агентов

Каждая задача живёт в собственной git-ветке (feature/<ID>-slug) и собственном worktree — изолированной рабочей копии репозитория. Агенты разных задач не видят незакоммиченную работу друг друга; слияние в main происходит только через PR в Gitea после всех гейтов.


Подробнее: компоненты и API · внутренности и схема БД · следующий блок — конвейер и стадии.