Единая точка входа в документацию платформы (ADR-001 D1–D9): - docs/overview/ — 10 файлов: индекс (маршруты «Я заказчик / Я менеджер / Я разработчик» + норматив «изменил функциональность → обнови витрину в том же PR»), business.md (без жаргона, 6 сценариев), 7 тех-блоков (link-first), presentation.md (16 слайдов + процедура сборки «команда + Проверка:»). - scripts/build_presentation.py — генератор .pptx в тёмном дизайне (python-pptx; чистый stdlib-парсер parse_slides + ленивый import pptx; бинарь не коммитится, build/ в .gitignore; зависимость НЕ в прод-образе — машинный гард TC-09). - tests/test_system_docs.py — структурный анти-дрейф: derive-сверки стадий/ гейтов/агентов импортом STAGE_TRANSITIONS/QG_CHECKS/glob промптов/config, валидность ссылок, FORBIDDEN-скан + секрет-эвристика, слайды каноническим парсером, NFR-2, указатели. - reviewer.md — ось обзорных доков ORCH-079 расширена на витрину (D7; канон 52d байт-в-байт, только текст внутри секций) + анти-регресс ассерт в test_agent_prompts_canon.py. - Указатели: README.md, CLAUDE.md (правила №2/№6, «Структура»), PRODUCT_VISION.md (врезка-ссылка), CHANGELOG.md. Рантайм байт-в-байт: src/**, docker-compose.yml, Dockerfile, requirements* — ноль изменений (docs+tests+dev-скрипт, паттерн ORCH-102/103). pytest: 1873 passed. Refs: ORCH-011 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
327 lines
24 KiB
Markdown
327 lines
24 KiB
Markdown
# Multi-Agent Orchestrator
|
||
|
||
> См. [CLAUDE.md](CLAUDE.md) (паспорт проекта) и [docs/architecture/README.md](docs/architecture/README.md) (архитектура).
|
||
>
|
||
> **Витрина системы — [docs/overview/](docs/overview/README.md)**: единая точка входа в документацию
|
||
> (бизнес + тех, 7 блоков, маршруты для заказчика / менеджера / разработчика, презентация). ORCH-011.
|
||
|
||
FastAPI-сервис для оркестрации мульти-агентного пайплайна разработки. Принимает webhooks от Plane и Gitea, управляет жизненным циклом задач через Quality Gates, запускает Claude CLI агентов на каждой стадии.
|
||
|
||
## Архитектура
|
||
|
||
```
|
||
Plane (task mgmt) ──webhook──┐
|
||
├──► Orchestrator (FastAPI) ──► Quality Gates ──► Agent Launcher
|
||
Gitea (git events) ─webhook──┘ │ │
|
||
▼ ▼
|
||
SQLite DB Claude CLI
|
||
(events, tasks, (analyst, architect,
|
||
agent_runs) developer, reviewer, tester)
|
||
```
|
||
|
||
## Стадии пайплайна
|
||
|
||
```
|
||
created → analysis → architecture → development → review → testing → deploy-staging → deploy → done
|
||
↑ │
|
||
└───── REQUEST_CHANGES ─────┘ (max 3 retries)
|
||
```
|
||
|
||
| Стадия | Агент | Quality Gate (выход) | Триггер перехода |
|
||
|--------|-------|---------------------|------------------|
|
||
| created | — | — | Plane webhook (work_item.created) |
|
||
| analysis | analyst | Файлы BRD/TRZ/AC/TestPlan | Push docs/ |
|
||
| architecture | architect | ADR или infra-requirements | Push docs/ |
|
||
| development | developer | check_ci_green (Gitea CI зелёный на ветке) | Auto-advance после developer |
|
||
| review | reviewer | check_reviewer_verdict (`verdict:` во frontmatter 12-review.md) | Auto-advance после reviewer |
|
||
| testing | tester | check_tests_passed (test-report.md) | Auto-advance после tester |
|
||
| deploy-staging | deployer | check_staging_status (15-staging-log.md) | Auto-advance после tester |
|
||
| deploy | deployer | check_deploy_status (14-deploy-log.md) | Auto-advance после staging |
|
||
| done | — | — | — |
|
||
|
||
## API Endpoints
|
||
|
||
| Method | Path | Описание |
|
||
|--------|------|----------|
|
||
| GET | `/health` | Health check |
|
||
| GET | `/status` | Активные задачи (stage != done) |
|
||
| GET | `/queue` | Очередь задач (ORCH-1): counts по статусам + max_concurrency + последние 10 jobs |
|
||
| POST | `/webhook/plane` | Plane webhook receiver |
|
||
| POST | `/webhook/gitea` | Gitea webhook receiver |
|
||
| POST | `/bug-fast-track/escalate?work_item=<id>` | Эскалация багфикс-задачи в полный цикл (ORCH-019): сброс `track` `'bug'→'full'` → следующий переход уходит в `architecture` |
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
src/
|
||
├── main.py # FastAPI app, lifespan (orphan recovery)
|
||
├── config.py # Pydantic settings (env vars)
|
||
├── db.py # SQLite: init, get_db, update_task_stage
|
||
├── stages.py # State machine (transitions, agents, QG)
|
||
├── notifications.py # Уведомления (логирование)
|
||
├── plane_sync.py # Синхронизация статусов с Plane API
|
||
├── queue_worker.py # ORCH-1: фоновый воркер очереди (claim → launch_job)
|
||
├── agents/
|
||
│ └── launcher.py # AgentLauncher: launch/launch_job, monitor, watchdog, auto-advance
|
||
├── webhooks/
|
||
│ ├── plane.py # Plane webhook handler
|
||
│ └── gitea.py # Gitea webhook handler (push, PR, CI status)
|
||
└── qg/
|
||
└── checks.py # Quality Gate checks (filesystem + Gitea API)
|
||
data/
|
||
├── orchestrator.db # SQLite database
|
||
└── runs/ # Agent output logs ({run_id}.log)
|
||
docs/
|
||
├── PRODUCT_VISION.md # Видение продукта
|
||
├── deployment/
|
||
│ └── LITE_SETUP.md # Lite-тираж: орк+watchdog на инфре заказчика (ORCH-102)
|
||
├── architecture/
|
||
│ ├── README.md # Обзор архитектуры, компоненты, API
|
||
│ ├── internals.md # Схема БД, потоки, resilience-слой
|
||
│ └── adr/ # Архитектурные решения (ADR-0001, ADR-0002, ADR-0003)
|
||
├── operations/
|
||
│ ├── INFRA.md # Топология, порты, env, self-hosting риски
|
||
│ ├── DEPLOY_HOOK.md # Деплой-хук
|
||
│ ├── STAGING.md # Staging-окружение
|
||
│ ├── STAGING_CHECK.md # Проверки staging
|
||
│ └── SETUP_WEBHOOKS.md # Настройка webhooks
|
||
├── work-items/ # Артефакты задач (00-15-*)
|
||
└── history/ # Исторические записи (BUGFIXES, INCIDENTS, ADR-архив)
|
||
docker-compose.yml # Deployment config
|
||
Dockerfile # Python 3.12 + Docker CLI + tini
|
||
```
|
||
|
||
## Запуск
|
||
|
||
### Docker (production)
|
||
|
||
```bash
|
||
docker compose up -d --build
|
||
```
|
||
|
||
### Dev
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
uvicorn src.main:app --reload --port 8500
|
||
```
|
||
|
||
## Конфигурация
|
||
|
||
Все переменные с префиксом `ORCH_`:
|
||
|
||
| Переменная | Описание | Default |
|
||
|-----------|----------|---------|
|
||
| `ORCH_PLANE_API_URL` | Plane API URL | `http://localhost:8091` |
|
||
| `ORCH_PLANE_API_TOKEN` | Plane API token | — |
|
||
| `ORCH_PLANE_WEBHOOK_SECRET` | Webhook secret | — |
|
||
| `ORCH_PLANE_WORKSPACE_SLUG` | Workspace slug | — |
|
||
| `ORCH_PLANE_PROJECT_ID` | Project UUID | — |
|
||
| `ORCH_GITEA_URL` | Gitea URL | `http://localhost:3000` |
|
||
| `ORCH_GITEA_TOKEN` | Gitea API token | — |
|
||
| `ORCH_GITEA_WEBHOOK_SECRET` | Gitea webhook secret | — |
|
||
| `ORCH_GITEA_OWNER` | Gitea repo owner | `admin` |
|
||
| `ORCH_DEFAULT_REPO` | Default repository (fallback) | `enduro-trails` |
|
||
| `ORCH_PROJECTS_JSON` | Multi-repo реестр (JSON-массив, ORCH-6) | `""` → дефолт в `src/projects.py` |
|
||
| `ORCH_CLAUDE_BIN` | Путь к Claude CLI | `/opt/claude-code/bin/claude.exe` |
|
||
| `ORCH_REPOS_DIR` | Repos dir (container) | `/repos` |
|
||
| `ORCH_HOST_REPOS_DIR` | Repos dir (host) | `/home/slin/repos` |
|
||
| `ORCH_DB_PATH` | SQLite path | `/app/data/orchestrator.db` |
|
||
| `ORCH_RUNS_DIR` | Базовый каталог per-run логов агентов (`<runs_dir>/{run_id}.log`, ORCH-087) | `/app/data/runs` |
|
||
| `ORCH_MAX_CONCURRENCY` | Сколько jobs воркер запускает параллельно (ORCH-1) | `1` |
|
||
| `ORCH_QUEUE_POLL_INTERVAL` | Период опроса очереди воркером, сек (ORCH-1) | `2.0` |
|
||
| `ORCH_PREFLIGHT_CACHE_TTL` | Кэш preflight (CLI/net), сек (ORCH-1 resilience) | `45` |
|
||
| `ORCH_BACKOFF_BASE_SECONDS` | База exp-backoff для transient (429) | `10` |
|
||
| `ORCH_BACKOFF_MAX_SECONDS` | Потолок backoff | `600` |
|
||
| `ORCH_TRANSIENT_MAX_ATTEMPTS` | Ретраи для 429/недоступности | `5` |
|
||
| `ORCH_BREAKER_THRESHOLD` | transient подряд до открытия breaker | `3` |
|
||
| `ORCH_BREAKER_PAUSE_SECONDS` | Пауза при открытом breaker | `300` |
|
||
| `ORCH_RECONCILE_ENABLED` | Kill-switch sweeper потерянных webhook (ORCH-053) | `true` |
|
||
| `ORCH_RECONCILE_PLANE_ENABLED` | Отдельный флаг F-2 (опрос Plane API) | `true` |
|
||
| `ORCH_RECONCILE_INTERVAL_S` | Период фонового прохода reconciler, сек | `120` |
|
||
| `ORCH_RECONCILE_GRACE_DEFAULT_S` | Порог «застряла» по `tasks.updated_at`, сек | `600` |
|
||
| `ORCH_RECONCILE_GRACE_OVERRIDES_JSON` | Per-stage пороги, напр. `{"development":300}` | `""` |
|
||
| `ORCH_RECONCILE_NOTIFY_UNBLOCK` | Telegram при разблокировке застрявшей задачи | `true` |
|
||
| `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED` | F-1 Guard 2 (ORCH-060): пропуск задач в Plane-статусе Blocked / Needs Input; `false` глушит только сетевой Guard 2 (Guard 1 escalated всегда активен) | `true` |
|
||
| `ORCH_QG0_TITLE_MAX` | Верхний лимит длины заголовка QG-0 (вход `_qg0_errors`); невалидное/пустое значение → дефолт (ORCH-069) | `200` |
|
||
| `ORCH_STOP_STATUS_ENABLED` | Kill-switch отмены задачи по Plane-статусу **STOP** + закрытия дыры релонча (ORCH-090); `false` → поведение 1:1 как до ORCH-090 | `true` |
|
||
| `ORCH_STOP_STATUS_REPOS` | CSV область репо для STOP-отмены; пусто = все репо (ORCH-090) | `""` |
|
||
| `ORCH_BUG_FAST_TRACK_ENABLED` | Kill-switch багфикс-трека (ORCH-019): задача с меткой Plane `Bug` пропускает стадию `architecture`; `false` → старт и маршрут 1:1 как до ORCH-019 (нулевая регрессия) | `true` |
|
||
| `ORCH_BUG_FAST_TRACK_LABEL` | Имя метки Plane, активирующей багфикс-трек (ORCH-019) | `Bug` |
|
||
| `ORCH_BUG_FAST_TRACK_REPOS` | CSV область репо для багфикс-трека; **пусто → self-hosting only** (`orchestrator`) — enduro подключается явным CSV (ORCH-019) | `""` |
|
||
| `ORCH_AGENT_HOME_DIR` | ORCH-101: HOME акторских процессов + таргет маунтов `.claude`/`.ssh` + `ARG APP_HOME` (группа ORCH-040) | `/home/slin` |
|
||
| `ORCH_AGENT_GIT_NAME` / `ORCH_GIT_EMAIL_DOMAIN` | ORCH-101: git-идентичность коммитов агентов (`claude-bot@mva154.local` при дефолтах) | `claude-bot` / `mva154.local` |
|
||
| `ORCH_STAGING_PORT` | ORCH-101: порт staging (читают `image_freshness` и compose); guard fail-closed при совпадении с прод-портом (ORCH-058 AC-9) | `8501` |
|
||
| `ORCH_HOST_CLAUDE_DIR` / `_CLAUDE_JSON` / `_SSH_DIR` / `_CLAUDE_CODE_DIR` / `_NODE_BIN` | ORCH-101: host-источники bind-маунтов (compose-интерполяция) | боевые пути mva154 |
|
||
| `ORCH_RUN_UID` / `ORCH_RUN_GID` / `ORCH_DOCKER_GID` | ORCH-101: uid:gid контейнера и gid docker-группы (`group_add`, ORCH-040) | `1000`/`1000`/`999` |
|
||
|
||
Тираж платформы на новый хост (полная карта, секреты, smoke) — `docs/operations/REPLICATION.md` (ORCH-101).
|
||
**Lite-тираж под ключ (ORCH-102):** разворачивание орк+watchdog на инфраструктуре заказчика
|
||
по одной сквозной инструкции «голый хост → работающий конвейер» (Plane/Gitea/Telegram/LLM
|
||
заказчик ставит сам и подключает по шагам) — `docs/deployment/LITE_SETUP.md`; канон конфига
|
||
sidecar-watchdog — `.env.watchdog.example`; анти-дрейф — `tests/test_lite_setup_doc.py`.
|
||
|
||
## Очередь задач (ORCH-1 / F-2b)
|
||
|
||
Webhook-хэндлеры больше не спавнят claude-агентов синхронно в процессе uvicorn.
|
||
Вместо этого они кладут **job** в персистентную SQLite-таблицу `jobs`
|
||
(`enqueue_job`, мгновенный ответ), а фоновый воркер (`src/queue_worker.py`)
|
||
забирает jobs с учётом `ORCH_MAX_CONCURRENCY` и запускает агента (`launch_job`,
|
||
та же Popen-логика, что и раньше).
|
||
|
||
Преимущества:
|
||
- **Рестарт-safe.** При старте jobs со статусом `running` возвращаются в `queued`
|
||
(queue-recovery в lifespan) — работа не теряется.
|
||
- **Лимит параллелизма.** Воркер не превышает `ORCH_MAX_CONCURRENCY`.
|
||
- **Ретраи.** Упавший job (exit≠0) ретраится пока `attempts < max_attempts`,
|
||
потом `failed` + Telegram-нотификация.
|
||
|
||
Статусы job: `queued → running → done | failed`; **`cancelled`** — терминальный
|
||
исход STOP-отмены (ORCH-090), нигде не реквью'ится. Наблюдаемость — через `GET /queue`.
|
||
|
||
## Отмена задачи: статус STOP (ORCH-090)
|
||
|
||
Перевод задачи в выделенный Plane-статус **STOP** отменяет её: оркестратор
|
||
останавливает активного агента (graceful SIGTERM-каскад), снимает все job'ы
|
||
(терминальный `cancelled`, без авто-requeue), удаляет worktree и **рабочую**
|
||
ветку в Gitea (**никогда** `main`, без force-push), сбрасывает прогресс в
|
||
durable-терминал `tasks.stage='cancelled'` и тумбстонит натуральные ключи
|
||
(`#cancelled-<id>`), чтобы повторный «To Analyse» создал задачу **с нуля**.
|
||
Docs-артефакты (`01..17`) сохраняются. STOP во время критичного шага merge/deploy
|
||
— **откладывается** до его честного завершения (никакого half-merge / рестарта
|
||
прода). Параллельно закрыта «дыра релонча»: ручной перевод в промежуточный рабочий
|
||
статус больше не релончит агента — единственный вход к запуску пайплайна остаётся
|
||
«To Analyse» (релонч агента сменой статуса разрешён только на стадии `analysis` —
|
||
владельце Needs Input). Всё под kill-switch `ORCH_STOP_STATUS_ENABLED`, аддитивно,
|
||
never-raise. Наблюдаемость — блок `stop` в `GET /queue`. Деталь — `docs/work-items/
|
||
ORCH-090/06-adr/ADR-001-stop-cancel-task.md` + сквозной
|
||
`docs/architecture/adr/adr-0026-stop-cancel-task.md`.
|
||
|
||
> **Инфра-предусловие:** на доске Plane проекта ORCH создать статус **«STOP»** с
|
||
> группой `cancelled`. До создания статуса фича в fail-safe (нет UUID → ветка STOP
|
||
> не активируется).
|
||
|
||
## Багфикс-трек: дешёвый маршрут для багов (ORCH-019)
|
||
|
||
Задача с меткой Plane `Bug` (имя метки — `ORCH_BUG_FAST_TRACK_LABEL`, дефолт `Bug`)
|
||
идёт **укороченным маршрутом** конвейера: `analysis(lite) → development → review →
|
||
testing → deploy-staging → deploy → done`, т.е. **пропускается стадия `architecture`**
|
||
(отдельный прогон opus-агента `architect` + ADR + exit-гейт `check_architecture_done`).
|
||
Мини-аналитик выдаёт облегчённый пакет (короткий bug-report + обязательный план
|
||
регресс-теста), но всё равно все 4 файла analysis — гейт `check_analysis_complete`
|
||
не меняется.
|
||
|
||
**Корневой инвариант:** упрощается только аналитика/архитектура — **все Quality
|
||
Gate'ы и под-гейты исполняются без изменений** (`STAGE_TRANSITIONS` / `QG_CHECKS` /
|
||
`check_*` / machine-verdict ключи — байт-в-байт прежние). Маршрутизация багфикса —
|
||
свойство планировщика (routing-override в `advance_stage` по `tasks.track='bug'`),
|
||
**не** Quality Gate.
|
||
|
||
Классификация (`src/bug_fast_track.py`, never-raise): локальный `bug_fast_track_applies(repo)`
|
||
ПЕРВЫМ (выключенный флаг = нулевой сетевой оверхед), затем `is_bug_task` через
|
||
`labels.has_label` (источник истины — Plane API). Тип хранится в аддитивной колонке
|
||
`tasks.track` (`'full'` | `'bug'`), читается в горячем пути из БД (не из сети).
|
||
**Эскалация** сложного/архитектурного бага в полный цикл — `POST /bug-fast-track/escalate?work_item=<id>`
|
||
(сброс `'bug'→'full'`). Всё под kill-switch `ORCH_BUG_FAST_TRACK_ENABLED`, область —
|
||
`ORCH_BUG_FAST_TRACK_REPOS` (пусто → self-hosting only), fail-safe → полный цикл.
|
||
Наблюдаемость — блок `bug_fast_track` в `GET /queue` + отметка `🐞` в Telegram-карточке.
|
||
Деталь — `docs/work-items/ORCH-019/06-adr/ADR-001-bug-fast-track.md` + сквозной
|
||
`docs/architecture/adr/adr-0032-bug-fast-track.md`.
|
||
|
||
> **Инфра-предусловие:** на доске Plane проекта ORCH создать метку **`Bug`**. До её
|
||
> создания фича в fail-safe (нет метки → задача идёт полным циклом).
|
||
|
||
**Resilience-слой:** дешёвый preflight (CLI/net, кэш, без токенов) гейтит claim;
|
||
429/overload детектится по логу (transient vs permanent), transient ретраится с
|
||
exp-backoff (`available_at`, Retry-After); circuit breaker паузит воркер после N
|
||
transient подряд. Подробности: `docs/history/ORCH-1_JOB_QUEUE.md`.
|
||
|
||
## Multi-repo: реестр проектов (ORCH-6)
|
||
|
||
Оркестратор обслуживает несколько репозиториев через реестр проектов
|
||
(`src/projects.py`), ключ = **Plane project id**. Plane-webhook фильтрует события
|
||
по проекту (неизвестный проект → `ignored`) и резолвит `repo` / `work_item_prefix` /
|
||
Plane-проект из маппинга.
|
||
|
||
По умолчанию (если `ORCH_PROJECTS_JSON` пуст) зарегистрированы два проекта:
|
||
|
||
| Проект | Plane project id | repo | prefix |
|
||
|--------|------------------|------|--------|
|
||
| enduro-trails | `7a79f0a9-5278-49cd-9007-9a338f238f9c` | `enduro-trails` | `ET` |
|
||
| orchestrator | `8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a` | `orchestrator` | `ORCH` |
|
||
|
||
### Как добавить новый проект
|
||
|
||
1. Убедись, что gitea-репо уже клонировано в `/repos/<repo>` (авто-clone — отдельно).
|
||
2. Узнай Plane project uuid (из URL проекта в Plane или через Plane API).
|
||
3. Добавь запись в `ORCH_PROJECTS_JSON` в `.env` (JSON-массив). **Важно:** если
|
||
задаёшь `ORCH_PROJECTS_JSON`, он полностью заменяет дефолт — перечисли **все**
|
||
нужные проекты (включая enduro-trails и orchestrator):
|
||
|
||
```bash
|
||
ORCH_PROJECTS_JSON='[
|
||
{"plane_project_id":"7a79f0a9-5278-49cd-9007-9a338f238f9c","repo":"enduro-trails","work_item_prefix":"ET","name":"enduro-trails"},
|
||
{"plane_project_id":"8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a","repo":"orchestrator","work_item_prefix":"ORCH","name":"orchestrator"},
|
||
{"plane_project_id":"<новый-uuid>","repo":"<новый-repo>","work_item_prefix":"<PREFIX>","name":"<имя>"}
|
||
]'
|
||
```
|
||
|
||
4. Пересобери: `docker compose up -d --build`.
|
||
5. Проверь резолв:
|
||
```bash
|
||
docker exec orchestrator python3 -c "from src.projects import get_project_by_plane_id as g; print(g('<новый-uuid>'))"
|
||
```
|
||
|
||
Поля `name` опционально (по умолчанию = `repo`). Подробности — `docs/architecture/internals.md`.
|
||
|
||
## Ключевые механизмы
|
||
|
||
### Auto-advance
|
||
После успешного завершения агента (exit_code=0), `_try_advance_stage()` проверяет QG и автоматически продвигает задачу + запускает следующего агента.
|
||
|
||
### Review bounce
|
||
При REQUEST_CHANGES от reviewer задача откатывается в development, developer перезапускается (до 3 попыток). При исчерпании — эскалация.
|
||
|
||
### Orphan recovery (M-1)
|
||
При старте контейнера каждый run с `finished_at IS NULL` старше 35 минут помечается exit_code=-1, логируется per-run warning и отправляется Telegram-уведомление «нужна ручная проверка/перезапуск» (не молча).
|
||
|
||
### Запись task-файлов (B-1)
|
||
Task-файлы `.task-*.md` пишутся **прямой записью в смонтированный volume `/repos/<repo>/`** (без docker). При ошибке записи — RuntimeError (не молчит). В `.gitignore` проекта.
|
||
|
||
### Логи агентов (B-2)
|
||
stdout/stderr агента перенаправляются СРАЗУ в `/app/data/runs/{id}.log` на уровне ОС (без PIPE). monitor-поток делает `proc.wait()` → реальный exit_code, нет зомби.
|
||
|
||
### Watchdog
|
||
Каждый агент имеет timeout 30 минут. При превышении — SIGKILL + запись exit_code=-9.
|
||
|
||
### Event routing
|
||
Gitea events роутятся по типу:
|
||
- `push` → проверка файлов, advance architecture/development
|
||
- `pull_request*` (wildcard) → review approved/rejected, PR merge
|
||
- `status` → Gitea CI статус; ORCH-045: авторитетный гейт развития (`development → review`) — `check_ci_green` читает статус ветки с polling-retry (устраняет гонку «pending сразу после push»)
|
||
|
||
## Тесты
|
||
|
||
```bash
|
||
pytest tests/ -v
|
||
```
|
||
|
||
## Известные ограничения
|
||
|
||
Реально открытые ограничения (сверено с кодом, ORCH-079):
|
||
|
||
1. **Telegram 48h** — карточки-сироты старше 48 часов неудаляемы (лимит Telegram Bot API); зачистка сирот самозалечивает только свежие (ORCH-087).
|
||
2. **Зависимости задач — только intra-repo (v1)** — `job_deps` выражают связи в пределах одного репозитория; кросс-репо зависимости пока не поддержаны (ORCH-026).
|
||
3. **Пакетный автоном — Этап 1** — per-repo serial gate сериализует задачи одного репо (ORCH-088); полный пакетный автономный прогон «10–20 задач за ночь» — в развитии (эпик ORCH-088).
|
||
|
||
### Закрыто (история)
|
||
|
||
Пункты, ранее значившиеся ограничениями, закрыты кодом — оставлены как трассировка:
|
||
|
||
- **Single-task / shared `/repos` checkout** → git worktree per task (`ensure_worktree`) + serial-gate (ORCH-088) + task-deps (ORCH-026).
|
||
- **In-process daemon-потоки** → персистентная очередь задач (SQLite `jobs`, `src/queue_worker.py`), restart-safe (ORCH-1).
|
||
- **Gitea CI не настроен** → активный гейт стадии `development` — `check_ci_green` (`src/qg/checks.py`); `check_tests_local` помечен DEPRECATED.
|
||
- **No retry on API errors** → exp-backoff + circuit breaker в `queue_worker.py` (`ORCH_BACKOFF_*` / `ORCH_BREAKER_*` / `ORCH_TRANSIENT_MAX_ATTEMPTS`) + retry-loop в `check_ci_green` (ORCH-1 resilience / ORCH-045).
|
||
- **Plane sync — маппинг issue ID** → зрелый `src/plane_sync.py` (`find_issue_id`, `fetch_issue_sequence_id`) со статус-моделью и TTL-самозалечиванием (ORCH-010 / 066 / 068).
|
||
- **Tester timeout — Playwright e2e** → orchestrator является pytest-сервисом (Playwright неприменим); реальный механизм — конфигурируемый watchdog (`agent_timeout_seconds`, ORCH-7).
|