--- work_item: ORCH-103 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-11 model_used: claude-opus-4-8 --- # 02 — ТЗ (TRZ): ORCH-103 — ORCH-10b Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт Work Item: **ORCH-103** · Repo: **orchestrator** · Стадия: analysis > ТЗ фиксирует **что** должно измениться и **где** (артефакты/контракты/границы). **Как** > (расположение bundle-каталога, состав сервисов Plane-стека, язык/режимы bootstrap, механизм > сетевой связности) — решает архитектор в `06-adr/`. Архитектурные решения здесь не принимаются. --- ## 1. Сводка изменения Добавить в репо **Bundled-комплект тиража (Type B эпика ORCH-10)**: (1) отдельный **bundle-compose** всего стека (орк + watchdog + Gitea + Plane-стек ~14 контейнеров), (2) **bootstrap-скрипт**, доводящий свежеподнятый стек до рабочего конвейера одним запуском (с явными manual-step чекпоинтами там, где Plane CE API не позволяет автоматизацию), (3) **генерацию новых секретов** на инсталляцию (reuse `gen_secrets.py` + bundle-внутренние креды), (4) инструкцию **`docs/deployment/BUNDLED_SETUP.md`** с требованиями к хосту, (5) **структурные анти-дрейф тесты**. Всё — вне рантайма и вне конвейера: `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД — байт-в-байт (паттерн ORCH-009/ORCH-102). Kill-switch не требуется: активация — только явный запуск оператором на целевом хосте. --- ## 2. Задействованные модули / пути | Путь | Действие | Назначение | |------|----------|------------| | `deploy/bundled/docker-compose.yml` *(рабочая гипотеза; финальное расположение/имя — ADR; далее «bundle-compose»)* | **создать** | Compose-комплект всего стека (FR-1) | | `deploy/bundled/.env.bundled.example` *(имя — ADR; далее «bundle-конфиг-канон»)* | **создать** | Канон 100% переменных bundle (порты/версии/плейсхолдеры кред), паттерн `.env.watchdog.example` | | `scripts/bootstrap_bundle.py` *(имя/язык — ADR)* | **создать** | Bootstrap-скрипт (FR-2) | | `docs/deployment/BUNDLED_SETUP.md` | **создать** | Golden source инструкции Bundled-тиража (FR-4) | | `docs/operations/REPLICATION.md` | **изменить (точечно)** | §1: строка Type B → ✅ ORCH-103 + ссылка на BUNDLED_SETUP.md (FR-6) | | `tests/test_bundle_compose.py` | **создать** | Структура bundle-compose + изоляция корневого compose (FR-5) | | `tests/test_bundled_setup_doc.py` | **создать** | Канон дока, env-ключи, FORBIDDEN/секрет-эвристика, кросс-рефы (FR-5) | | `tests/test_bootstrap_script.py` | **создать** | Структурные/unit-ассерты bootstrap (FR-5) | | `CHANGELOG.md` | **изменить** | Запись `feat: ORCH-103` | | `.gitignore` | **изменить (при необходимости)** | Сгенерированные на хосте конфиги bundle не коммитятся (NFR-3) | | **НЕ трогать:** `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `.gitea/workflows/**`, `.env.example` (кроме явно обоснованного в ADR аддитива), `onboarding/**`, промпты `.openclaw/agents/**` | — | NFR-1; анти-дрейф `test_lite_setup_doc.py` (TC-04: ровно 3 сервиса, нет `plane*`/`gitea*`) и `test_no_host_hardcodes.py` остаются зелёными | --- ## 3. Функциональные требования ### FR-1 — Bundle-compose всего стека (BR-1) - **Отдельный файл**, корневой `docker-compose.yml` не изменяется (жёсткое ограничение: анти-дрейф TC-04 `tests/test_lite_setup_doc.py` проверяет точное множество сервисов корневого compose и запрещает подстроки `plane`/`gitea` в именах сервисов/образов/контейнеров). - **Состав стека:** `orchestrator` (образ собирается из существующего корневого `Dockerfile` — без его правки), `orchestrator-watchdog` (существующий `watchdog/Dockerfile`), Gitea (+ её хранилище), полный Plane CE-стек (~14 контейнеров: web/admin/space/api/worker/beat/ live/migrator + postgres/redis/mq/minio/proxy — точный состав и версии пиннит архитектор по upstream-référence). Staging-контур орка (8501) — НЕ в дефолтном `up` (вне скоупа заказчика; включать ли за профилем — ADR). - **Пиннинг версий** всех сторонних образов (тег или digest; не `latest`) — NFR-6. - **Тома:** только именованные/каталожные тома bundle (узнаваемый префикс); чистый первый старт; пересечений с томами/путями нашего прод-контура нет. - **Сеть и достижимость (двунаправленно):** (a) Plane→орк и Gitea→орк webhooks доставляются; (b) орк→Plane API и орк→Gitea API доступны; (c) git push/fetch агентов в Gitea работает. Механизм (bridge-сеть + публикация портов, `extra_hosts: host-gateway`, host-network — что выбрать) — ADR; ТЗ фиксирует только инвариант достижимости, проверяемый smoke (FR-6). - **Порты:** карта портов по умолчанию задокументирована (BUNDLED_SETUP «Требования к хосту»); порты конфигурируемы через bundle-конфиг; конфликт порта → отказ preflight bootstrap (FR-2), не молчаливый сбой. Дефолт порта орка — существующий 8500 (`ORCH_DEPLOY_PROD_TARGET_PORT`). - **Конфиг-канон:** все параметры bundle (порты/версии/пути/плейсхолдеры кред) — в bundle-конфиг-каноне; key-set синхронизируется структурным тестом (паттерн key-sync `.env.watchdog.example`, D5 ORCH-102). Ключи орка НЕ дублируются — `.env` орка собирается bootstrap'ом из существующего канона `.env.example`. ### FR-2 — Bootstrap-скрипт: один запуск до рабочего состояния (BR-2, BR-6, BR-7) Последовательность (нумерация — норматив поведения; механика шагов — ADR): 1. **Preflight (fail-fast, до любых мутаций):** docker+compose присутствуют; свободные RAM/диск ≥ задокументированных минимумов; целевые порты свободны; тома bundle отсутствуют (чистый хост) — иначе явный отказ с подсказкой (BR-7); наличие Claude CLI/кред — warning (не блокер: конвейер без LLM не поедет, но стек поднимется). 2. **Секреты (FR-3):** генерация полного набора НОВЫХ секретов инсталляции. 3. **Up:** подъём bundle-compose; ожидание готовности каждого сервиса (healthcheck/готовность БД/завершение миграций Plane и Gitea) с таймаутами и внятной диагностикой. 4. **Init Gitea:** административная учётка + API-токен (через официальные механизмы Gitea — CLI/env/API; конкретика — ADR); branch protection НЕ настраивается (норматив D10 ORCH-009). 5. **Init Plane:** instance-setup/workspace/API-токен. Всё, что недоступно в CE по API, — **интерактивный manual-step чекпоинт**: скрипт печатает точную инструкцию (URL/что нажать/ что ввести), ждёт подтверждения, **проверяет результат** (например, валидность введённого API-токена запросом) и только тогда продолжает (fail-safe; молчаливый пропуск запрещён). 6. **Онбординг sandbox-проекта:** вызов `scripts/onboard_project.py apply` + `verify` (22 статуса из `plane_sync._PLANE_NAME_TO_KEY`, лейблы `autoApprove`/`autoDeploy`/`Bug`, Gitea-репо, per-repo webhook под глобальным секретом). Собственная реализация этих шагов в bootstrap **запрещена** (BR-6, нулевой дрейф канона). 7. **Git-доступ агентов:** обеспечить push/fetch созданного репо из контейнера орка (ssh-ключ + регистрация в Gitea ИЛИ токен-remote — механизм ADR); клон репо в repos-каталог орка (`ORCH_HOST_REPOS_DIR`). 8. **Конфиг орка:** собрать `.env` (на базе `.env.example`: URL'ы Plane/Gitea bundle-инсталляции, токены, webhook-секреты, `ORCH_PROJECTS_JSON` из вывода onboard) и `.env.watchdog` (из `.env.watchdog.example`); файлы остаются только на целевом хосте. 9. **Health + итог:** `GET /health`, `GET /queue`, `GET /metrics` зелёные; финальная сводка PASS/FAIL по всем шагам + следующая команда оператора (smoke FR-6). Требования к скрипту: - **Идемпотентность:** повторный запуск на уже-инициализированном bundle безопасен (ensure/skip-семантика, как `onboard_project.py apply`); никаких delete-операций. - **Exit-коды:** `0` — успех; `2` — остановка на manual-step/незавершённое предусловие; `1` — ошибка (паттерн `onboard_project.py`). - **Логи без секретов** (NFR-3): значения кред не печатаются (только имена ключей/пути файлов). - **Никогда не адресует наш прод:** в скрипте нет боевых хостов/путей (FORBIDDEN-скан), работает только с локальным docker целевого хоста. - Желателен режим `plan` (печать шагов без мутаций, паттерн ORCH-009) — финально ADR. ### FR-3 — Инициализация секретов: новые на каждую инсталляцию (BR-4) - **Webhook-секреты орка** — строго через существующий `scripts/gen_secrets.py` (не реализовывать заново). - **Bundle-внутренние креды** (генерирует bootstrap, криптослучайно, stdlib `secrets`): пароли postgres/redis*/mq/minio Plane-стека, секрет-ключи Plane, админ-пароль и API-токен Gitea. В репо — только плейсхолдеры в bundle-конфиг-каноне; **ни одного дефолтного пароля**. - **Внешние секреты заказчика** (не генерятся, чек-лист в доке): Anthropic/Claude CLI доступ, Telegram-токены (опционально), `ORCH_PLANE_API_TOKEN` (если выдаётся вручную на manual-step). - Сгенерированные файлы: только на целевом хосте, права `600`, в `.gitignore`; повторный запуск НЕ перетирает существующие секреты без явного флага (паттерн `--force` gen_secrets). ### FR-4 — `docs/deployment/BUNDLED_SETUP.md` (BR-5) - **Канон LITE_SETUP** (ORCH-102): нормативные разделы в порядке маршрута оператора; каждый исполняемый шаг = fenced-команда + явная «Проверка:» с PASS/FAIL; хост-специфика — только плейсхолдеры; запрещены FORBIDDEN-литералы и реальные секреты (структурный тест). - **Обязательные разделы** (минимум; точные заголовки — автор дока, проверяемость — тест): (1) рамка Bundled (что входит/что НЕ входит: Claude CLI, Telegram, HTTPS; границы vs Lite); (2) **требования к хосту** — RAM/диск/CPU/порты, явно «Plane ≈ 14 контейнеров», финальные цифры — по замеру на тестовом развёртывании; (3) предусловия (docker/compose/sudo); (4) получение кода; (5) секреты; (6) запуск bundle-compose; (7) bootstrap (включая перечень manual-step чекпоинтов Plane); (8) LLM/Claude CLI (ссылкой на канон LITE_SETUP §7); (9) Telegram (ссылкой на LITE_SETUP §8); (10) онбординг следующих проектов (ссылкой на ONBOARDING.md); (11) smoke (шаги REPLICATION §4); (12) stateless-проверка; (13) остановка/полный сброс инсталляции; (14) траблшутинг (минимум: webhook не доходит, не хватает RAM/OOM, порт занят, claude не найден, Plane-миграции не завершились). - **Канон не форкается:** общие с Lite шаги — ссылками (LITE_SETUP §5–§8, ONBOARDING §1, REPLICATION §2–§4), не копипастой; fail-closed имена `Confirm Deploy`/`STOP` и «22 статуса» — согласованы с `plane_sync._PLANE_NAME_TO_KEY` (число — сверкой импорта в тесте, не литералом). ### FR-5 — Структурные анти-дрейф тесты (BR-9) Все тесты — без docker/сети/LLM/subprocess-мутаций (CI-безопасные; паттерн `test_lite_setup_doc.py`): - **bundle-compose:** файл существует, валидный YAML; обязательные сервисы присутствуют (`orchestrator`, `orchestrator-watchdog`, Gitea, Plane-стек — по списку из ADR); все сторонние образы пиннованы (нет `:latest`/безтегового образа); корневой `docker-compose.yml` НЕ изменён (множество сервисов == текущему эталону); - **док:** BUNDLED_SETUP.md существует, несёт обязательные разделы (включая «Требования к хосту»), каждый env-ключ из дока существует в канонах (`.env.example` ∪ bundle-конфиг-канон), кросс-ссылки на LITE_SETUP/ONBOARDING/REPLICATION присутствуют; - **гигиена:** FORBIDDEN-литералы (импорт списка из `test_no_host_hardcodes.py`) отсутствуют в bundle-compose/доке/bootstrap; секрет-эвристика (hex ≥32 / alnum ≥40, паттерн D8 ORCH-102) по новым файлам; - **bootstrap:** скрипт существует; структурно ссылается на `gen_secrets`/`onboard_project` (не дублирует канон); не содержит delete-операций уровня `docker volume rm`/`rm -rf` вне явного отдельного «сброс»-режима; чистые функции (preflight-решение, сборка плана шагов, рендер `.env`) покрыты unit-тестами; - **кросс-рефы:** REPLICATION.md §1 несёт отметку Type B → BUNDLED_SETUP.md; CHANGELOG содержит `ORCH-103`. ### FR-6 — Smoke и наблюдаемость результата (BR-3) - Smoke Bundled = шаги REPLICATION §4 (0–6) поверх bundle-инсталляции, зафиксированные в BUNDLED_SETUP §smoke: config-резолв → `/health` → `/queue`+`/metrics` → onboard verify → тестовая задача (Plane issue → «To Analyse» → job в очереди) → **минимальный сигнал: артефакты `01–04` в ветке** → опционально полный цикл до `done`. - Прохождение фиксируется оператором по PASS/FAIL каждого шага; это ручная приёмка AC-2 (e2e в CI не гоняется — нет docker/LLM). --- ## 4. Изменения API **Нет.** Эндпоинты орка не добавляются/не меняются; bundle использует существующие `/health`, `/queue`, `/metrics`, вебхуки `/webhook/plane`, `/webhook/gitea`. ## 5. Изменения схемы БД **Нет** (схема БД орка не тронута). БД Plane/Gitea внутри bundle — их собственные, на чистых томах инсталляции; к схеме орка отношения не имеют. ## 6. Требования к новым/изменённым QG checks **Нет.** Реестр `QG_CHECKS`/`check_*`/`STAGE_TRANSITIONS` — байт-в-байт. Bundled-тираж — это артефакты дистрибуции, а не гейты конвейера. --- ## 7. Совместимость / регресс - **Kill-switch не требуется** (паттерн ORCH-009): артефакты вне рантайма; в нашем контуре ничего их не исполняет; активация — явный запуск оператором на целевом хосте. - **Нулевая регрессия:** корневой compose/`Dockerfile`/`src/**` не изменены ⇒ наш прод, staging-контур и enduro-trails не затронуты по построению; существующие анти-дрейф тесты (`test_lite_setup_doc.py`, `test_no_host_hardcodes.py`, канон-тесты ORCH-009) остаются зелёными без правки их ассертов. - **Обратимость:** удаление bundle-каталога/скрипта/дока возвращает репо в текущее состояние; на целевом хосте полный сброс = задокументированная процедура (FR-4 §13). - **Эскалация:** если при реализации выяснится необходимость править `src/**`/корневой compose (например, недостающая параметризация, не закрытая ORCH-101) — это выход за рамки ТЗ: остановиться и вернуть задачу с обоснованием (CLAUDE.md правило 4), не «дотачивать молча». --- ## 8. Артефакты pipeline (создать/обновить в ТОМ ЖЕ PR) - `docs/work-items/ORCH-103/06-adr/ADR-001-.md` — решения архитектора (см. §9 OQ); при сквозном значении — зеркало в `docs/architecture/adr/adr-NNNN-.md`. - `docs/architecture/README.md` — раздел «Bundled-тираж (ORCH-103)» рядом с 10-common/Lite. - `CLAUDE.md` — краткий абзац Type B (паттерн абзацев ORCH-101/102). - `docs/operations/REPLICATION.md` §1 — отметка Type B (FR-6). - `CHANGELOG.md` — `feat: ORCH-103 …`. - При выявлении инфра-предусловий целевого хоста — `07-infra-requirements.md` (архитектор). --- ## 9. Открытые вопросы для архитектора (не блокируют анализ) - **OQ-1** Расположение/имя bundle-каталога и compose-файла (`deploy/bundled/` vs `bundle/`; один compose vs include-композиция); судьба staging-контура орка в bundle (исключить vs профиль). - **OQ-2** Точный состав/версии Plane CE-стека (по upstream selfhost-référence) и Gitea; стратегия пиннинга (тег vs digest). - **OQ-3** Перечень физически автоматизируемых шагов инициализации Plane CE (instance-setup/ workspace/API-токен): что через API/CLI/seed, что — manual-step чекпоинт. - **OQ-4** Язык и режимы bootstrap (python stdlib vs bash; `plan`/`apply` vs линейный визард); способ ожидания готовности (healthchecks vs poll). - **OQ-5** Механизм сетевой связности орк (host network?) ⟷ bundle bridge-сеть: публикация портов, `host-gateway`, либо весь bundle в host-network — и согласование URL вебхуков. - **OQ-6** Механизм git-доступа агентов к bundle-Gitea (ssh-ключ vs http-токен) и наполнение repos-каталога. - **OQ-7** Делать ли отдельный явный «сброс»-режим (teardown) частью скрипта или только документированной процедурой в BUNDLED_SETUP §13.