Files
orchestrator/docs/deployment/LITE_SETUP.md
claude-bot 8351e91382 docs(deployment): ORCH-10a Lite-тираж — LITE_SETUP.md + канон watchdog-конфига + анти-дрейф контур
Закрывает Type A эпика ORCH-10 (поверх 10-common ORCH-101). Docs+tests
(паттерн ORCH-077/092): src/**, docker-compose.yml, Dockerfile, scripts/** —
ноль изменений; конвейер (STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict/
схема БД) — байт-в-байт.

- docs/deployment/LITE_SETUP.md (D1/D2): golden source Lite-тиража — 13
  нормативных разделов в порядке маршрута оператора, каждый шаг =
  fenced-команда + явная «Проверка:»/PASS/FAIL, хост-специфика только
  плейсхолдерами; канон не форкается (статусы/env/вебхуки/smoke — ссылками
  на ONBOARDING §1 / REPLICATION §2–§4 / SETUP_WEBHOOKS; явно — только
  fail-closed Confirm Deploy/STOP и обязательные ключи нового хоста).
- .env.watchdog.example (D5, исход А-4): третий канонический env-example;
  key-set = блок WATCHDOG_* .env.example (19 ключей, токены — пустые
  плейсхолдеры); закрывает ловушку файла-носителя (sidecar читает ТОЛЬКО
  .env.watchdog); C-1 ORCH-100 + когерентность порта в шапке; .env.watchdog
  добавлен в .gitignore (секрет-гигиена, зеркало .env.staging).
- tests/test_lite_setup_doc.py (D8): 25 структурных тестов без
  сети/LLM/subprocess — 13 разделов в порядке D2, кирпичи FR-6.1, key-sync
  watchdog-канона, env-ключи ⊂ .env.example, compose-подмножество (ровно
  орк+watchdog по дефолту, staging за профилем, анти-появление
  plane*/gitea*), fenced-скан FORBIDDEN (импорт из test_no_host_hardcodes)
  + секрет-эвристика с негативным самочеком, «22 статуса» сверкой импорта
  plane_sync._PLANE_NAME_TO_KEY, перекрёстность.
- Перекрёстные доки (FR-7): REPLICATION.md §1 (Type A — Lite →  ORCH-102 +
  ссылка), README.md (способность Lite + docs/deployment/ в структуре),
  INFRA.md (.env.watchdog в секрет-нормативе + ссылка на deployment),
  CLAUDE.md (блок ORCH-102), CHANGELOG.md.

Нормативы разделов: Gitea — branch protection на main НЕ включать (D3 /
ADR D10 ORCH-009 / INV-4), pre-receive не вводится, ОДИН глобальный
webhook-секрет; staging-вилка опциональна (D6); источник кода —
параметризованный git clone <ORCHESTRATOR_GIT_URL> (D7); stateless —
данные/задачи/секреты боевого хоста НЕ переносятся (AC-3).

Тесты: pytest tests/ -q — 1789 passed (полный регресс зелёный).

Refs: ORCH-102

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 00:42:15 +03:00

33 KiB
Raw Blame History

LITE_SETUP — Lite-тираж: оркестратор + watchdog на вашей инфраструктуре (ORCH-102)

Golden source Lite-тиража (Type A эпика ORCH-10). Сквозной маршрут «голый хост → работающий конвейер» для внешнего оператора/заказчика. Каждый шаг — исполняемая команда + явная проверка результата (Проверка: / PASS / FAIL). Хост-специфика в командах — только плейсхолдеры <...> и $ENV_VAR. Тираж stateless: данные/задачи/секреты исходного (боевого) хоста НЕ переносятся ни на одном шаге — всё создаётся заново (§12).


1. Рамка Lite

Что разворачиваем: два контейнера платформы — orchestrator (конвейер, порт ORCH_DEPLOY_PROD_TARGET_PORT, дефолт 8500) и orchestrator-watchdog (независимый sidecar-мониторинг). Третий сервис orchestrator-staging существует в том же compose-файле, но строго за профилем staging и в базовом Lite-контуре не поднимается (§9).

Что заказчик ставит и администрирует сам (вне этой инструкции — как продукты):

  • Plane (task-management) — своя инсталляция; здесь только её подключение (§5);
  • Gitea (git-хостинг) — своя инсталляция; здесь только её подключение (§6);
  • Telegram-боты (нотификации) — свои боты (§8);
  • LLM-доступ (claude CLI + node) — свой дистрибутив и аутентификация (§7).

Границы слоёв тиража (10-common vs Lite vs Bundled) — docs/operations/REPLICATION.md §1. Этот док собирает кирпичи 10-common (карта env, секреты, smoke) в один маршрут и не форкает их.

Платформенные конвенции (не менять):

  • репо платформы обязан называться orchestrator (узел безопасности SELF_HOSTING_REPO);
  • имена compose-сервисов/профиля (orchestrator, orchestrator-watchdog, orchestrator-staging, профиль staging) — константы платформы;
  • контейнерные пути (/app/data, /repos, /opt/claude-code) — layout контейнера, не хост-значения; не параметризуются.

2. Предусловия хоста

Поддерживаемый контур: Linux x86_64, Docker Engine + Compose v2, git, python3, node + дистрибутив claude-code на хосте. Вне контура — вне гарантии Lite. Каждое предусловие — команда проверки; все проверки PASS → можно переходить к §3.

2.1. ОС и базовые зависимости.

uname -sm                      # Linux x86_64
docker --version               # Docker Engine
docker compose version         # Compose v2
git --version
python3 --version              # 3.x (для scripts/*.py)
node --version                 # путь к бинарю станет ORCH_HOST_NODE_BIN

Проверка: все команды отвечают версиями без ошибок — PASS; любая отсутствует — FAIL (доставить пакет средствами вашего дистрибутива).

2.2. Пользователь-владелец и uid/gid (инвариант ORCH-040). Контейнеры бегут под ORCH_RUN_UID:ORCH_RUN_GID — это обязан быть uid:gid владельца каталога репозиториев ORCH_HOST_REPOS_DIR, иначе git-артефакты конвейера не запишутся.

id -u <deploy-user>; id -g <deploy-user>     # значения для ORCH_RUN_UID / ORCH_RUN_GID
mkdir -p <путь-каталога-репозиториев>        # станет ORCH_HOST_REPOS_DIR
stat -c '%u:%g' <путь-каталога-репозиториев> # обязан совпасть с ORCH_RUN_UID:ORCH_RUN_GID

Проверка: uid:gid владельца каталога = будущие ORCH_RUN_UID/ORCH_RUN_GID — PASS.

2.3. Группа docker (доступ к docker.sock).

getent group docker            # третье поле — gid, станет ORCH_DOCKER_GID

Проверка: строка вида docker:x:<gid>:... есть — PASS; группы нет — FAIL (установите Docker корректно / создайте группу).

2.4. Каталог ssh-ключей деплой-хука (понадобится для git-push артефактов агентов и деплой-хуков; монтируется в $HOME/.ssh акторов).

mkdir -p <путь-ssh-каталога>                 # станет ORCH_HOST_SSH_DIR
ssh-keygen -t ed25519 -f <путь-ssh-каталога>/id_ed25519 -N "" -C "orchestrator@<host>"
ls -l <путь-ssh-каталога>                    # ключи читаемы пользователем из 2.2

Проверка: каталог существует, ключи на месте, владелец — пользователь из 2.2 — PASS. Публичный ключ добавьте в Gitea (Settings → SSH Keys) на шаге §6.

2.5. Свободные порты (дефолты платформы: 8500 — прод, 8501 — staging).

ss -ltn | grep -E ':(8500|8501)\b' || echo "ports free"

Проверка: вывод ports free — PASS. Порты заняты → выберите другие и на шаге §4 синхронно задайте ORCH_DEPLOY_PROD_TARGET_PORTWATCHDOG_METRICS_URLORCH_POST_DEPLOY_BASE_URLORCH_STAGING_PORT ≠ прод-порт — guard ORCH-058 fail-closed).


3. Перенос кода

Переносится только код — чекаут репо orchestrator. НИКАКИХ данных, БД или .env с исходного хоста (норматив §12). Watchdog отдельно не переносится: его код — каталог watchdog/ того же репо, образ собирается локально compose'ом.

git clone <ORCHESTRATOR_GIT_URL> <путь-чекаута>   # путь станет ORCH_DEPLOY_HOST_REPO_PATH
cd <путь-чекаута>

Конкретный канал дистрибуции (<ORCHESTRATOR_GIT_URL> — зеркало/архив/доступ к Gitea поставщика) согласуйте с поставщиком платформы; опционально — --branch <тег-среза>.

Проверка:

git -C <путь-чекаута> log --oneline -1            # коммит виден
ls <путь-чекаута>/docker-compose.yml <путь-чекаута>/watchdog/Dockerfile \
   <путь-чекаута>/.env.example <путь-чекаута>/.env.watchdog.example

Все файлы на месте — PASS.


4. Конфигурация

.env собирается с нуля от канона .env.example (100% ключей старта; полная карта переменных и их семантика — docs/operations/REPLICATION.md §2). Дефолт каждого ключа = значению исходного хоста, поэтому задаёте только то, что отличается у вас.

4.1. Создать .env и выпустить webhook-секреты.

cd <путь-чекаута>
cp .env.example .env
python3 scripts/gen_secrets.py        # печатает свежие ORCH_PLANE_WEBHOOK_SECRET / ORCH_GITEA_WEBHOOK_SECRET

Вставьте оба напечатанных значения в .env. Секреты выпускаются только заново — боевые не копируются (§12).

4.2. Обязательные ключи нового хоста (заполняются в .env по ходу §5§8):

Группа Ключи Откуда
Plane ORCH_PLANE_API_URL, ORCH_PLANE_WEB_URL, ORCH_PLANE_WORKSPACE_SLUG, ORCH_PLANE_API_TOKEN §5
Gitea ORCH_GITEA_URL, ORCH_GITEA_PUBLIC_URL, ORCH_GITEA_OWNER, ORCH_GITEA_TOKEN §6
Webhook-секреты ORCH_PLANE_WEBHOOK_SECRET, ORCH_GITEA_WEBHOOK_SECRET 4.1 (gen_secrets.py)
Telegram ORCH_TELEGRAM_BOT_TOKEN, ORCH_TELEGRAM_CHAT_ID §8
Реестр проектов ORCH_PROJECTS_JSONобязателен: встроенный fallback несёт Plane-UUID исходного хоста §10
Хост-параметры ORCH_AGENT_HOME_DIR, ORCH_HOST_REPOS_DIR, ORCH_HOST_CLAUDE_DIR, ORCH_HOST_CLAUDE_JSON, ORCH_HOST_SSH_DIR, ORCH_HOST_CLAUDE_CODE_DIR, ORCH_HOST_NODE_BIN, ORCH_RUN_UID, ORCH_RUN_GID, ORCH_DOCKER_GID, ORCH_DEPLOY_HOST_REPO_PATH, ORCH_AGENT_GIT_NAME, ORCH_GIT_EMAIL_DOMAIN значения из §2§3
Порты ORCH_DEPLOY_PROD_TARGET_PORTWATCHDOG_METRICS_URLORCH_POST_DEPLOY_BASE_URL; ORCH_STAGING_PORT ≠ прод-порт §2.5

4.3. Конфиг sidecar-watchdog — отдельный файл-носитель. Sidecar-контейнер читает ТОЛЬКО .env.watchdog; ключ WATCHDOG_ENABLED (и любой другой WATCHDOG_*), положенный в .env, для sidecar инертен.

cp .env.watchdog.example .env.watchdog
# заполнить два ключа: WATCHDOG_TG_BOT_TOKEN / WATCHDOG_TG_CHAT_ID (бота создадим в §8)

Проверка (резолв всей конфигурации):

docker compose config >/dev/null && echo "compose config: PASS"

PASS без ошибок интерполяции — конфигурация согласована; ошибка — FAIL (ищите незакрытую кавычку/невалидный JSON в ORCH_PROJECTS_JSON).


5. Подключение Plane

Инсталляция Plane — ваша; платформа подключается к ней API-токеном и webhook'ом.

5.1. Workspace и проект. Создайте workspace (его slug → ORCH_PLANE_WEB_URL / ORCH_PLANE_WORKSPACE_SLUG) и проект под вашу разработку — через UI Plane.

# базовая доступность API из хоста оркестратора:
curl -fsS "$ORCH_PLANE_API_URL/api/v1/workspaces/<workspace-slug>/projects/" \
  -H "X-API-Key: <plane-api-token>" | head -c 200

Проверка: HTTP 200 и JSON со списком проектов — PASS; 401/403 — токен (5.2) ещё не выпущен или не имеет прав.

5.2. API-токен. Plane UI → Workspace Settings → API tokens → выпустить токен → ORCH_PLANE_API_TOKEN в .env. Токен должен иметь право создавать проекты/статусы (нужно для onboard_project.py apply, §10).

5.3. Модель статусов — НЕ вручную. Конвейеру нужны 22 канонических статуса с точными именами и группами; их создаёт python3 scripts/onboard_project.py apply (§10), полная таблица — docs/operations/ONBOARDING.md §1 (golden source; здесь не дублируется). Два имени фиксируем явно, потому что они fail-closed (без них ветка просто не активируется, без ошибки): Confirm Deploy (человеческий гейт прод-деплоя) и STOP (отмена задачи; обязан быть в группе cancelled).

# после §10 — проверить, что статусы созданы:
curl -fsS "$ORCH_PLANE_API_URL/api/v1/workspaces/<workspace-slug>/projects/<project-uuid>/states/" \
  -H "X-API-Key: $ORCH_PLANE_API_TOKEN" | python3 -m json.tool | grep -c '"name"'

Проверка: счётчик имён = 22 (или больше, если в проекте остались дефолтные статусы Plane) и среди них Confirm Deploy и STOP — PASS.

5.4. Webhook + HMAC. Приёмник — POST https://<orchestrator-public-host>/webhook/plane; подпись — заголовок X-Plane-Signature (HMAC-SHA256, hex digest); секрет — значение ORCH_PLANE_WEBHOOK_SECRET из 4.1. События: Issue, Issue Comment.

Каверза Plane CE: webhook не экспонирован во внешнем /api/v1 — настраивается одним из двух путей.

Путь А — UI (если ваша сборка Plane его показывает): Workspace Settings → Webhooks → Add Webhook → URL + Secret (значение ORCH_PLANE_WEBHOOK_SECRET) → события Issue, Issue Comment → Save.

Путь Б — прямой SQL в Postgres инсталляции Plane:

WORKSPACE_ID=$(docker exec -e PGPASSWORD=<plane-db-password> <plane-db-container> \
  psql -U plane -d plane -t -A -c "SELECT id FROM workspaces WHERE slug='<workspace-slug>'")
WEBHOOK_ID=$(cat /proc/sys/kernel/random/uuid)
docker exec -e PGPASSWORD=<plane-db-password> <plane-db-container> psql -U plane -d plane -c "
INSERT INTO webhooks (id, created_at, updated_at, deleted_at, workspace_id, url, is_active,
  secret_key, project, issue, module, cycle, issue_comment, is_internal, version)
VALUES ('${WEBHOOK_ID}', NOW(), NOW(), NULL, '${WORKSPACE_ID}',
  'https://<orchestrator-public-host>/webhook/plane',
  true, '<значение ORCH_PLANE_WEBHOOK_SECRET>', true, true, false, false, true, false, 'v1');
"

Проверка:

docker exec -e PGPASSWORD=<plane-db-password> <plane-db-container> psql -U plane -d plane -c \
  "SELECT url, is_active FROM webhooks;"

Строка с вашим URL и is_active = t — PASS. Сквозная проверка доставки — §11 (smoke); generic-образец команд и формат подписи — docs/operations/SETUP_WEBHOOKS.md.


6. Подключение Gitea

6.1. Токен. Gitea UI → Settings → Applications → Generate Token, scope: repo, admin:repo_hookORCH_GITEA_TOKEN в .env.

curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" "$ORCH_GITEA_URL/api/v1/user" | head -c 200

Проверка: HTTP 200 с JSON вашего пользователя — PASS; владелец репозиториев (организация/пользователь) → ORCH_GITEA_OWNER, браузерный URL → ORCH_GITEA_PUBLIC_URL.

6.2. Репо проекта. Создаёт onboard_project.py apply (§10) — или вручную (пустой репо + initial push). Чекаут обязан появиться в $ORCH_HOST_REPOS_DIR/<repo> (общий каталог репозиториев из §2.2). Публичный ключ из §2.4 добавьте в Gitea (Settings → SSH Keys), чтобы акторы могли пушить.

git -C "$ORCH_HOST_REPOS_DIR" clone <git-url-репо-проекта> <repo>
stat -c '%u:%g' "$ORCH_HOST_REPOS_DIR/<repo>"     # владелец = ORCH_RUN_UID:ORCH_RUN_GID

Проверка: чекаут на месте, владелец совпадает — PASS.

6.3. Per-repo webhook. Создаёт onboard_project.py apply (§10). Параметры (если вручную): URL https://<orchestrator-public-host>/webhook/gitea, content type json, события push / pull_request / status, branch filter *, подпись — X-Gitea-Signature (HMAC-SHA256). Секрет — ОДИН глобальный ORCH_GITEA_WEBHOOK_SECRET на ВСЕ репо (приёмник валидирует только его; «свой секрет на репо» сломал бы HMAC остальных).

curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" \
  "$ORCH_GITEA_URL/api/v1/repos/<owner>/<repo>/hooks" | python3 -m json.tool

Проверка: hook с вашим URL и тремя событиями существует, active: true — PASS.

6.4. Норматив защиты main (ВАЖНО). Branch protection на main НЕ включать (никаких required-approvals / required-status-checks): merge-актор конвейера мержит PR строго через Gitea PR-merge API (INV-4), и protection-правила дают 405/409-класс отказов → ложные HOLD задач (ADR D10 ORCH-009). pre-receive хуки платформа не вводит и не проверяет — защита main держится конвенцией (агенты не пушат main) + скоупом токенов.

curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" \
  "$ORCH_GITEA_URL/api/v1/repos/<owner>/<repo>/branch_protections" | python3 -m json.tool

Проверка: пустой список [] — PASS; есть правила на main — FAIL (удалите их, симптом «PR не мержится / HOLD» — §13.7).


7. LLM (claude CLI)

Агенты конвейера — процессы claude CLI внутри контейнера, но дистрибутив, node и аутентификация живут на хосте и пробрасываются маунтами (источники маунтов = ключи .env).

7.1. Дистрибутив claude-code и node. Установите claude-code (npm-дистрибутив Anthropic) и node на хост. Пути → .env:

which node                                   # → ORCH_HOST_NODE_BIN
npm root -g                                  # каталог глобальных модулей
ls "<npm-root>/@anthropic-ai/claude-code"    # → ORCH_HOST_CLAUDE_CODE_DIR

Проверка: каталог дистрибутива существует и непуст — PASS. Внутри контейнера бинарь доступен как ORCH_CLAUDE_BIN (дефолт менять не нужно).

7.2. Аутентификация CLI. Выполните первичный интерактивный логин claude CLI на хосте под пользователем из §2.2 (по инструкции Anthropic к claude-code). Логин создаёт каталог ~/.claude и файл ~/.claude.json — их пути задайте в ORCH_HOST_CLAUDE_DIR / ORCH_HOST_CLAUDE_JSON.

claude --version                                            # CLI работает
sudo -u "#<uid-из-2.2>" test -r <путь-~/.claude>/.credentials.json && echo "creds: PASS"

Проверка: версия печатается; creds: PASS — креды читаемы uid'ом контейнера (иначе — chown -R <uid>:<gid> каталога, симптом §13.3).

7.3. Модели агентов. Резолв модели/эффорта — только из конфига (ORCH-41/74): дефолты канона уже в .env.example (ORCH_AGENT_MODEL_DEFAULT, ORCH_AGENT_EFFORT_DEFAULT и per-агент ключи рядом) — менять не обязательно.

grep -E '^ORCH_AGENT_(MODEL|EFFORT)_DEFAULT=' .env

Проверка: оба ключа присутствуют и непусты — PASS.


8. Telegram

Каналов два и они независимы (C-1 ORCH-100): бот live-трекера оркестратора и отдельный бот sidecar-watchdog. Токен орка для watchdog переиспользовать ЗАПРЕЩЕНО — упавший орк не сможет сообщить о себе своим же ботом.

8.1. Бот трекера. BotFather → /newbot → токен → ORCH_TELEGRAM_BOT_TOKEN в .env.

curl -fsS "https://api.telegram.org/bot<токен-трекера>/getMe"
# напишите боту любое сообщение (или добавьте его в чат), затем:
curl -fsS "https://api.telegram.org/bot<токен-трекера>/getUpdates" | python3 -m json.tool | grep -m1 '"id"'

Проверка: getMe"ok":true; id чата из getUpdatesORCH_TELEGRAM_CHAT_ID — PASS.

8.2. Watchdog-бот (отдельный). BotFather → /newbot ещё раз → токен и chat-id → .env.watchdog (WATCHDOG_TG_BOT_TOKEN / WATCHDOG_TG_CHAT_ID). Помните о файле-носителе: эти ключи работают только в .env.watchdog (§4.3).

curl -fsS "https://api.telegram.org/bot<токен-watchdog>/getMe"
grep -E '^WATCHDOG_TG_(BOT_TOKEN|CHAT_ID)=.+' .env.watchdog

Проверка: getMe"ok":true; оба ключа в .env.watchdog непусты — PASS.


9. Запуск

9.1. Базовый Lite-контур (дефолт): орк + watchdog.

cd <путь-чекаута>
docker compose config --services    # ровно: orchestrator, orchestrator-watchdog, orchestrator-staging
docker compose up -d --build
docker compose ps

Проверка: запущены ровно два контейнера — orchestrator и orchestrator-watchdog; orchestrator-staging НЕ поднялся (он строго за profiles: [staging]) — PASS. Поднялось что-то ещё/меньше — FAIL.

9.2. Health-чек контрактов.

curl -fsS http://127.0.0.1:8500/health
curl -fsS http://127.0.0.1:8500/queue   | python3 -m json.tool | head -20
curl -fsS http://127.0.0.1:8500/metrics | python3 -m json.tool | head -10

Проверка: /health → HTTP 200, "status":"ok"; /queue → штатный JSON (счётчики очереди); /metrics → JSON со "schema_version": 1 — PASS. (Порт замените, если меняли ORCH_DEPLOY_PROD_TARGET_PORT.)

9.3. Вилка staging (опционально). Базовому контуру «гонять СВОИ проекты» staging не нужен. Он нужен ТОЛЬКО если вы регистрируете проект orchestrator (self-hosting развитие самой платформы у себя): стадия deploy-staging требует песочницу на ORCH_STAGING_PORT (изолированная БД ./data/staging; guard ORCH-058: staging-порт ≠ прод-порт, fail-closed).

cp .env.staging.example .env.staging      # заполнить по аналогии с .env
docker compose --profile staging up -d orchestrator-staging
curl -fsS http://127.0.0.1:8501/health

Проверка (только для этой вилки): /health staging → 200 — PASS.


10. Регистрация проекта заказчика

Onboarding-CLI создаёт Plane-проект с 22 статусами и лейблами (autoApprove / autoDeploy / Bug), Gitea-репо с webhook'ом, скелет репо (kit) и печатает merged-реестр. Полный runbook — docs/operations/ONBOARDING.md; Lite-последовательность:

cd <путь-чекаута>
python3 scripts/onboard_project.py plan \
  --name "<имя проекта>" --description "<зачем проект>" \
  --repo <repo> --prefix <PREFIX> \
  --stack "<стек>" --test-cmd "<команда тестов>" \
  --prod-port <порт-прода-проекта> --staging-port <порт-staging-проекта> \
  --webhook-url https://<orchestrator-public-host>/webhook/gitea
# план устроил → тот же вызов с apply; затем read-only контроль:
python3 scripts/onboard_project.py verify <те же аргументы>

Проверка: apply завершился без ошибок (exit 0; 2 = остались 🖐 ручные шаги — выполните их по отчёту), verify зелёный — PASS.

Дальше реестр и рестарт:

# 1) строку ORCH_PROJECTS_JSON=[...] из отчёта apply вставить в .env (заменить целиком);
# 2) дождаться тихого окна и управляемо перезапустить орк:
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -20   # нет running-job
docker compose up -d --force-recreate orchestrator
# 3) убедиться, что инстанс жив и реестр подхвачен:
curl -fsS http://127.0.0.1:8500/health
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -20

Проверка: /health → 200 после рестарта; в Plane создан проект со статусами (см. §5.3), в Gitea — репо с webhook (§6.3) — PASS.


11. Smoke: «конвейер доехал»

Процедура — чек-лист docs/operations/REPLICATION.md §4 (шаги 05; шаг 6 «до done» — опционально), без форка; каждый шаг несёт явный PASS/FAIL. Lite-предусловия: §2§10 этого дока выполнены, проект заказчика зарегистрирован (§10).

Минимальный сигнал «конвейер доехал» (шаги 45 чек-листа): создайте issue в Plane-проекте и переведите в статус To Analyse, затем:

# задача появилась и analyst-job в очереди:
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -40
# по завершении стадии analysis — артефакты 0104 в ветке задачи:
git -C "$ORCH_HOST_REPOS_DIR/<repo>" fetch origin
git -C "$ORCH_HOST_REPOS_DIR/<repo>" ls-tree --name-only origin/<ветка-задачи> "docs/work-items/<id-задачи>/"

Проверка: в /queue виден job задачи; ls-tree показывает 01-brd.md04-test-plan.yaml — PASS.

Итоговый вердикт: все шаги чек-листа PASS ⇒ тираж PASS; любой шаг FAIL ⇒ тираж FAIL — соберите docker logs orchestrator --tail 100 и снапшот GET /queue, разбор — §13.


12. Stateless-проверка

Нормативно: данные/задачи/секреты боевого (исходного) хоста НЕ переносятся (зеркало docs/operations/REPLICATION.md §5). БД создаётся пустой при первом старте; .env / .env.staging / .env.watchdog собраны с нуля (§4); секреты — только свежевыпущенные (gen_secrets.py + чек-лист внешних токенов docs/operations/REPLICATION.md §3).

Проверка чистоты развёрнутого инстанса (выполнить ДО первой своей задачи):

ls -la <путь-чекаута>/data/                                  # БД появилась только после первого старта
curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool # счётчики jobs нулевые

Проверка: в /queue нулевые счётчики и НИ ОДНОЙ задачи чужих проектов (никаких ORCH-*/ET-* исходного хоста) — PASS. Любая чужая задача/перенесённый файл БД — FAIL: инстанс собран не stateless, пересоберите data/ с нуля.


13. Траблшутинг первичной настройки

Формат: симптом → команда диагностики → лечение.

13.1. Webhook отвечает 401 / HMAC mismatch.

docker logs orchestrator --tail 50 2>&1 | grep -i "webhook\|signature\|401"

Лечение: секрет в .env (ORCH_PLANE_WEBHOOK_SECRET / ORCH_GITEA_WEBHOOK_SECRET) обязан байт-в-байт совпадать с секретом в настройке webhook'а (Plane §5.4 / Gitea §6.3); после правки .env — управляемый рестарт (§10). Формат подписи — docs/operations/SETUP_WEBHOOKS.md.

13.2. Задача в Plane создана, но в оркестраторе не появилась.

curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -30   # есть ли job
docker logs orchestrator --tail 50 2>&1 | grep -i "ignored\|unknown project"
grep ORCH_PROJECTS_JSON .env                                              # uuid вашего проекта в реестре?

Лечение: (а) проект отсутствует/с чужим UUID в ORCH_PROJECTS_JSON → поправить реестр (§10) + рестарт; (б) webhook не доставлен → Plane: SELECT url, is_active FROM webhooks; (§5.4), Gitea: Recent Deliveries в настройках hook'а; (в) подпись → §13.1.

13.3. claude CLI не найден / не аутентифицирован (агент падает на старте).

docker exec orchestrator /usr/bin/claude --version
sudo -u "#<uid-из-2.2>" test -r <путь-~/.claude>/.credentials.json && echo "creds: PASS"

Лечение: маунты указывают на фактические пути хоста (ORCH_HOST_CLAUDE_CODE_DIR, ORCH_HOST_NODE_BIN, ORCH_HOST_CLAUDE_DIR, ORCH_HOST_CLAUDE_JSON в .env); креды читаемы uid'ом из §2.2 (chown -R <uid>:<gid>); при невалидной сессии — повторный логин на хосте (§7.2) + docker compose up -d --force-recreate orchestrator.

13.4. docker.sock: permission denied в логах орка/watchdog.

getent group docker          # фактический gid
grep ORCH_DOCKER_GID .env    # gid в конфиге

Лечение: значения обязаны совпадать → выставить ORCH_DOCKER_GID = фактическому gid и docker compose up -d --force-recreate.

13.5. Permission denied при создании worktree (права /repos, ORCH-040/057).

stat -c '%u:%g' "$ORCH_HOST_REPOS_DIR" "$ORCH_HOST_REPOS_DIR/<repo>"
grep -E '^ORCH_RUN_(UID|GID)=' .env

Лечение: владелец каталога репо обязан совпадать с ORCH_RUN_UID:ORCH_RUN_GID (§2.2) → chown -R <uid>:<gid> "$ORCH_HOST_REPOS_DIR" (включая legacy root-owned файлы) и пересоздать контейнер.

13.6. Telegram молчит (нет карточек/алертов).

curl -fsS "https://api.telegram.org/bot<токен-трекера>/getMe"
curl -fsS "https://api.telegram.org/bot<токен-watchdog>/getMe"
grep -E '^ORCH_TELEGRAM_(BOT_TOKEN|CHAT_ID)=.+' .env
grep -E '^WATCHDOG_TG_(BOT_TOKEN|CHAT_ID)=.+' .env.watchdog

Лечение: оба бота отвечают "ok":true; chat-id корректен (бот добавлен в чат / получил сообщение); ключи watchdog-бота лежат именно в .env.watchdog.env они инертны, §4.3); пустой токен = режим «логи без отправки» (fail-safe, не ошибка).

13.7. PR задачи не мержится / задача в HOLD. Первая проверка — не включена ли branch protection на main (§6.4):

curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" \
  "$ORCH_GITEA_URL/api/v1/repos/<owner>/<repo>/branch_protections" | python3 -m json.tool

Лечение: непустой список правил на main → удалить (норматив §6.4); merge выполняет PR-merge API оркестратора, ручной merge не требуется.


Golden source Lite-тиража (ORCH-102, ADR-001). Норматив сопровождения (NFR-5): меняешь шаги тиража (env-ключи, compose-сервисы, маршрут онбординга, smoke) → обнови этот док В ТОМ ЖЕ PR (правило агентов №2). Полноту и гигиену дока держит структурный анти-дрейф тест tests/test_lite_setup_doc.py; кирпичи-каноны: REPLICATION.md (карта env §2, секреты §3, smoke §4), ONBOARDING.md (статусы §1, онбординг), SETUP_WEBHOOKS.md (формат вебхуков), .env.example / .env.watchdog.example (канон ключей).