---
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-<slug>.md` — решения архитектора (см. §9 OQ);
  при сквозном значении — зеркало в `docs/architecture/adr/adr-NNNN-<slug>.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.
