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>
65 lines
6.2 KiB
Markdown
65 lines
6.2 KiB
Markdown
# Блок 1. Архитектура: компоненты и связи
|
||
|
||
> Обзорный уровень. Полная таблица компонентов с деталями и историей решений — в
|
||
> [инженерном справочнике](../architecture/README.md) («Компоненты») и
|
||
> [internals](../architecture/internals.md); здесь — цельная картина, как части складываются
|
||
> в конвейер.
|
||
|
||
## Поток одной задачи
|
||
|
||
```
|
||
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](../architecture/README.md) · [внутренности и схема БД](../architecture/internals.md) ·
|
||
следующий блок — [конвейер и стадии](tech-pipeline.md).*
|