Files
orchestrator/docs/operations/INFRA.md
claude-bot 5a7f8d4000
All checks were successful
CI / test (push) Successful in 16s
CI / test (pull_request) Successful in 17s
feat(reconciler): sweeper потерянных webhook (реконсиляция застрявших стадий)
Конвейер продвигается только входящими webhook; потерянное событие (502 на
ребилде, отсутствие ретраев у Plane/Gitea, неразрезолвленный sha→branch)
оставляет задачу молча застрявшей (класс инцидента ORCH-044). Новый фоновый
daemon-поток src/reconciler.py (паттерн queue_worker) доигрывает пропущенный
переход через те же штатные гейты/обработчики, что и webhook:

- F-1 gate-side: для задач stage≠done, без активного job и age(updated_at) ≥
  grace_for_stage(stage) — read-only пред-оценка канонического QG; зелёный →
  stage_engine.advance_stage(..., finished_agent=None); красный → тишина (спам
  нотификаций структурно невозможен). analysis F-1 не трогает (человеческий гейт).
- F-2 plane-side: опрос Plane API per-project (plane_sync.list_issues_by_state,
  курсорная пагинация, never-raise) → реплей In Progress/Approved/Rejected через
  существующие handle_status_start/handle_verdict (async из sync-потока, asyncio.run).
- F-3: усиление sha→branch в handle_ci_status — БД-fallback по единственной
  development-задаче repo (неоднозначность → не резолвим), debug→info.
- Анти-дубль на создании (db.create_task_atomic под process-wide Lock): гонка
  reconcile↔webhook не плодит второй task/branch/worktree/analyst-job (AC-4).
- F-4 observability: лог-строка разблокировки + Telegram + блок reconcile в /queue.

Старт/стоп в main.lifespan (после worker.start() / перед worker.stop()),
restart-safe, never-raise на единицу работы. Kill-switches ORCH_RECONCILE_ENABLED
/ ORCH_RECONCILE_PLANE_ENABLED + grace-настройки. Схема БД и реестры
STAGE_TRANSITIONS/QG_CHECKS не менялись.

Тесты: test_reconciler.py, test_reconciler_plane.py, test_gitea_sha_resolve.py,
test_config.py (33 новых, 563 всего зелёные). Документация обновлена (golden source):
architecture/README.md, INFRA.md, README.md, CHANGELOG.md, adr-0007 → accepted.

Refs: ORCH-053

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 20:37:01 +00:00

12 KiB
Raw Blame History

INFRA.md — инфраструктура и эксплуатация оркестратора

RUNBOOK. Топология, контейнеры, порты, переменные окружения, границы. Секреты тут НЕ хранятся — только дескрипторы. Реальные значения — в .env на хосте.

Топология

                 host: mva154 (slin@82.22.50.71), network_mode: host
 ┌──────────────────────────────────────────────────────────────────────┐
 │  orchestrator        (PROD)     :8500   env_file .env                  │
 │    БД: ./data/orchestrator.db          (обслуживает ВСЕ прод-проекты)  │
 │                                                                        │
 │  orchestrator-staging (STAGING) :8501   env_file .env.staging          │
 │    БД: ./data/staging/orchestrator.db  (изолирована, только sandbox)   │
 │    profile: staging — НЕ стартует обычным `docker compose up`          │
 └──────────────────────────────────────────────────────────────────────┘
        │ webhooks                                  │ git
        ▼                                           ▼
   Plane (ag_proj)                            Gitea (localhost:3000)
   /repos/<project>  ← общий каталог репозиториев (host: /home/slin/repos)

Контейнеры

Контейнер Роль Порт env_file БД (хост) Старт
orchestrator прод 8500 .env ./data/orchestrator.db docker compose up -d
orchestrator-staging staging / песочница 8501 .env.staging ./data/staging/orchestrator.db docker compose --profile staging up -d orchestrator-staging

Оба: network_mode: host, init: true (tini как PID 1 — reaping зомби, B-2), restart: unless-stopped.

Рантайм-uid (ORCH-040)

Оба сервиса бегут под user: "1000:1000" (slin), не root. Артефакты конвейера (git worktree /repos/_wt/..., коммиты в docs/work-items/...) создаются как slin:slin, поэтому git pull / git reset на хосте под slin работают без ручного chown. Доступ к docker.sock сохранён через group_add: ["999"] (gid docker, не через root — НЕ удалять). При переносе на другой хост uid пересматривается. См. ADR docs/work-items/ORCH-040/06-adr/ADR-001-run-agents-as-host-uid.md и глобальный docs/architecture/adr/adr-0005-container-runs-as-host-uid.md.

Host-prerequisites (обязательная процедура Owner, в git не коммитятся):

  • P-1 (блокер): uid 1000 читает claude creds — chown -R 1000:1000 /home/slin/.claude; проверка sudo -u '#1000' test -r /home/slin/.claude/.credentials.json. Без этого preflight (ORCH-044) заворачивает весь конвейер.
  • P-2: ssh-ключи в /home/slin/.orchestrator-ssh читаемы uid 1000 (маунт ведёт в /home/slin/.ssh).
  • P-3: id slin1000:1000; /repos, /app/data уже 1000:1000.
  • P-4: прод-рестарт self — только в окно тишины (GET /status без активных задач): общий инстанс с enduro-trails.
  • Разовый разгребающий chown -R 1000:1000 /home/slin/repos/orchestrator для старых root:root файлов из истории (вне объёма кода).

Тома (volumes)

  • ./data/app/data (БД; у staging — ./data/staging)
  • /home/slin/repos/repos (рабочие репозитории проектов)
  • /var/run/docker.sock (для docker-операций деплоя)
  • claude-code, node, ~/.claude* (CLI агентов, ro)
  • ~/.orchestrator-ssh/home/slin/.ssh (ro, деплой по ssh; target в HOME агента, согласован с HOME=/home/slin из launcher — ORCH-040, ранее /root/.ssh)

Переменные окружения (карта; значения — в .env)

Переменная Назначение
ORCH_PLANE_API_URL / _TOKEN / _WORKSPACE_SLUG доступ к Plane API
ORCH_PLANE_WEB_URL внешний (браузерный) web-URL Plane для кликабельных ссылок на issue в уведомлениях (ORCH-017); пусто → фолбэк на ORCH_PLANE_API_URL, loopback-фолбэк → ссылка опускается
ORCH_PLANE_WEBHOOK_SECRET HMAC-проверка вебхуков Plane
ORCH_GITEA_URL / _TOKEN / _WEBHOOK_SECRET доступ к Gitea + HMAC
ORCH_CLAUDE_BIN путь к claude CLI
ORCH_REPOS_DIR / ORCH_HOST_REPOS_DIR каталог репозиториев (в контейнере / на хосте)
ORCH_DB_PATH путь к SQLite БД
ORCH_PROJECTS_JSON реестр проектов (Plane id → repo + prefix); пусто → дефолт из src/projects.py
ORCH_AGENT_MODEL_DEFAULT LLM-модель агентов по умолчанию (ORCH-41); дефолт claude-opus-4-8
ORCH_AGENT_MODEL_<AGENT> per-agent модель (ANALYST/ARCHITECT/DEVELOPER/REVIEWER/TESTER/DEPLOYER); пусто → default
ORCH_AGENT_EFFORT_DEFAULT режим работы --effort по умолчанию (ORCH-41): low|medium|high|xhigh|max; дефолт high
ORCH_AGENT_EFFORT_<AGENT> per-agent effort; дефолт: думающие → high, tester/deployer → medium
ORCH_AGENT_FALLBACK_MODEL опц. фолбэк-модель при overloaded (--fallback-model); пусто → без флага
ORCH_RECONCILE_ENABLED kill-switch sweeper потерянных webhook (ORCH-053); дефолт true. При инциденте/раскаткеfalse глушит весь фоновый reconciler
ORCH_RECONCILE_PLANE_ENABLED отдельный флаг F-2 (опрос Plane API); false гасит только plane-ветку, F-1 продолжает работать; дефолт 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}; невалидный JSON → дефолт
ORCH_RECONCILE_NOTIFY_UNBLOCK слать Telegram при разблокировке застрявшей задачи; дефолт true
DEPLOY_SSH_USER / _HOST / DEPLOY_HOOK_SCRIPT параметры деплой-хука

Секреты — только в .env / .env.staging на хосте, в гит НЕ коммитятся. Канон — .env.example, .env.staging.example.

Реестр проектов (src/projects.py, ORCH-6)

Связывает Plane project id → gitea repo + work-item prefix. Источник: ORCH_PROJECTS_JSON, fallback — встроенный дефолт. Прод видит: enduro-trails (ET), orchestrator (ORCH). Staging видит ТОЛЬКО orchestrator-sandbox (SANDBOX) — изоляция.

Модель и effort агентов (src/config.py + src/agents/launcher.py, ORCH-41)

Модель LLM и режим работы (--effort) каждого агента конфигурируемы — глобально per-agent (env) и per-project (через ORCH_PROJECTS_JSON).

Приоритет резолвинга (resolve_agent_model / resolve_agent_effort):

  1. per-project override — agent_models / agent_efforts в записи ORCH_PROJECTS_JSON;
  2. per-agent env — ORCH_AGENT_MODEL_<AGENT> / ORCH_AGENT_EFFORT_<AGENT> (если непусто);
  3. глобальный дефолт — ORCH_AGENT_MODEL_DEFAULT (claude-opus-4-8) / ORCH_AGENT_EFFORT_DEFAULT (high);
  4. пусто → флаг не передаётся, действует дефолт CLI.

Значения effort: low < medium < high < xhigh < max — рычаг «качество vs стоимость/время». Дефолтная раскладка: думающие агенты (analyst/architect/developer/reviewer) → high, механические (tester/deployer) → medium. Невалидное значение → лог-warning, флаг опускается.

Per-project override в ORCH_PROJECTS_JSON (поля agent_models / agent_efforts опциональны, старые записи работают):

{"plane_project_id":"...","repo":"orchestrator","work_item_prefix":"ORCH",
 "agent_models":{"developer":"claude-opus-4-8","reviewer":"claude-sonnet-4-6"},
 "agent_efforts":{"developer":"xhigh","tester":"low"}}

⚠️ Бюджет (ORCH-38): claude-opus-4-8 дефолт в коде; реальное переключение прод-env делается отдельно после согласования.

⚠️ Self-hosting — оркестратор дорабатывает САМ СЕБЯ

Факт: прод-инстанс orchestrator (8500) — ОДИН на ВСЕ прод-проекты (enduro-trails + orchestrator), с ОБЩЕЙ БД ./data/orchestrator.db и общей очередью задач (ORCH-1).

Следствие — групповой риск: когда орк выполняет задачу из проекта ORCH (дорабатывает себя), он бежит в том же инстансе, что обслуживает enduro-trails.

  • Рестарт / падение прод-контейнера орк-задачей → конвейер ВСЕХ проектов встаёт.
  • Кривой self-деплой (ORCH-36, Вариант B) → лежат все проекты сразу.
  • Общая очередь → орк-задача занимает concurrency-слоты других проектов.

Что изолировано (безопасно):

  • Staging (8501) — отдельная БД (./data/staging), отдельный реестр (ORCH_PROJECTS_JSON = только sandbox). Прод-проекты не видит.
  • Репозитории разделены, изоляция веток через git worktree (ORCH-2).

Страховки:

  • Стадия deploy-staging (порт 8501) — обязательный гейт перед прод-деплоем орка. Прод-деплой недостижим, пока staging-гейт не зелёный (см. STAGING.md, ORCH-35). Гейт условный: реален только для self-hosting (repo=orchestrator), для остальных проектов — no-op.

Правила для агентов при задачах ORCH:

  1. НЕ перезапускать / не ронять прод-контейнер orchestrator в рамках задачи.
  2. Все проверки деплоя — на staging (8501), боевой 8500 не трогать.
  3. Деплой self — только через хук с health-check + авто-rollback (DEPLOY_HOOK.md).

Эксплуатация (быстрые команды)

# статус
docker ps --filter name=orchestrator
curl -s http://localhost:8500/health
curl -s http://localhost:8500/status   # активные задачи
curl -s http://localhost:8500/queue    # очередь

# поднять staging-песочницу
docker compose --profile staging up -d orchestrator-staging
curl -s http://localhost:8501/health

# логи
docker logs --tail 100 orchestrator

RUNBOOK 2026-06-05. Обновлять при изменении топологии/портов/переменных. См. CONTRIBUTING.md §8.