---
work_item: ORCH-103
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-11
model_used: claude-opus-4-8
---

# 01 — BRD: ORCH-103 — ORCH-10b Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт

Work Item: **ORCH-103** · Repo: **orchestrator** (self-hosting) · Стадия: analysis
Заказчик: Слава · Эпик: **ORCH-10** (домен D5 «Масштаб», `docs/epics/self-evolution.md`) · Тип: **B — Bundled**

---

## 1. Бизнес-контекст и проблема

### 1.1. Цель эпика ORCH-10
Тираж платформы — РАЗДАЧА текущей функциональности нескольким заказчикам **на тест**.
Решения Владельца 10.06 (приняты как требования): ДВА типа тиража, ОБА **stateless**
(наши задачи/данные/секреты НЕ переносим — чистый старт):

- **Тип A (Lite, ORCH-102 ✅)** — переносится ТОЛЬКО орк+watchdog; Plane/Gitea/LLM/Telegram
  заказчик донастраивает сам по инструкции `docs/deployment/LITE_SETUP.md`.
- **Тип B (Bundled, эта задача)** — **весь стек одним комплектом**
  (орк + watchdog + Gitea + Plane + рантайм-обвязка агентов) — «под ключ».

### 1.2. Проблема, которую закрывает ORCH-103
Lite предполагает, что у заказчика **уже есть** (или он сам поднимет) свои Plane и Gitea.
Для заказчика без собственной инфраструктуры это барьер: Plane CE self-hosted — это ~14
контейнеров со своей БД/брокером/хранилищем, Gitea — отдельная установка, и поверх всего —
первичная инициализация (админы, токены, workspace, 22 статуса, лейблы, вебхуки в обе стороны,
git-доступ агентов). Сегодня репо не содержит ни compose-описания этого стека, ни автоматизации
его доводки: разворачивание «с нуля до работающего конвейера» = многочасовая ручная работа по
сторонним докам с рисками дефолтных паролей и дрейфа от канона платформы.

ORCH-103 должен дать: **один compose-комплект** всего стека + **bootstrap-скрипт**, доводящий
свежеподнятый стек до рабочего состояния одной командой/визардом, + **новые секреты** на каждую
инсталляцию + **инструкцию `docs/deployment/BUNDLED_SETUP.md`** с требованиями к хосту.

### 1.3. Установленные факты (проверено по репо — не изобретать)
- **Корневой `docker-compose.yml` защищён анти-дрейфом:** ровно 3 сервиса
  (`orchestrator`, `orchestrator-watchdog`, `orchestrator-staging` за `profiles: ["staging"]`);
  `tests/test_lite_setup_doc.py` (TC-04) проверяет точное множество сервисов и **запрещает**
  появление в нём имён/образов с подстроками `plane`/`gitea` → bundle-компоуз обязан быть
  **отдельным файлом**, корневой compose не форкается и не расширяется.
- **Кирпичи уже в `main` (переиспользовать, не дублировать):**
  - `scripts/gen_secrets.py` (ORCH-101) — криптослучайные webhook-секреты
    (`ORCH_PLANE_WEBHOOK_SECRET`/`ORCH_GITEA_WEBHOOK_SECRET`), печать по умолчанию,
    `--write` отказывает при существующем `.env`, `--force` — перезапись; exit 0/2.
  - `scripts/onboard_project.py` (ORCH-009) — `plan` (GET-only) / `apply` (идемпотентный ensure,
    без delete) / `verify`: Plane-проект + **22 статуса** (read-only импорт
    `plane_sync._PLANE_NAME_TO_KEY`, fail-closed имена `Confirm Deploy`/`STOP`) + лейблы
    `autoApprove`/`autoDeploy`/`Bug`; Gitea-репо + per-repo webhook (`push`/`pull_request`/`status`,
    ОДИН глобальный `ORCH_GITEA_WEBHOOK_SECRET`); недоступное в Plane CE API → `manual-step`
    (fail-safe); exit 0/2/1.
  - `docs/operations/REPLICATION.md` (ORCH-101) — карта env (§2), чек-лист секретов (§3),
    **smoke §4** (шаги 0–6 с PASS/FAIL: config-резолв → `/health` → `/queue`+`/metrics` →
    onboard plan/apply/verify → тестовая задача → артефакты `01–04` → опц. до `done`); §1 —
    таблица границ, где Type B помечен «отдельная задача».
  - `docs/deployment/LITE_SETUP.md` (ORCH-102) — канон тиражной инструкции: 13 нормативных
    разделов, каждый шаг = fenced-команда + явная «Проверка:» PASS/FAIL, хост-специфика только
    плейсхолдерами; канон не форкается — общие шаги ссылками.
  - `.env.example` — канон 100% ключей орка; `.env.watchdog.example` — канон watchdog
    (key-set-sync тестом, D5 ORCH-102).
- **Хост-параметризация завершена (ORCH-101):** платформа разворачивается без правки кода —
  только env (`${VAR:-default}`-интерполяция compose, `ARG APP_*` Dockerfile); анти-регресс
  `tests/test_no_host_hardcodes.py` (FORBIDDEN-литералы: IP/`/home/slin`/`mva154`/`duckdns`).
- **Claude CLI НЕ запечён в образ орка:** монтируется с хоста
  (`ORCH_HOST_CLAUDE_CODE_DIR`/`ORCH_HOST_NODE_BIN`/`ORCH_HOST_CLAUDE_DIR`/`ORCH_HOST_CLAUDE_JSON`).
  «Агенты» в комплекте = рантайм-обвязка запуска; **инсталляция Claude CLI и LLM-ключ — внешнее
  предусловие хоста заказчика** (как Lite §7), bundle их не содержит и не генерирует.
- **Нормативы тиражной Gitea:** branch protection на `main` НЕ включать (D10 ORCH-009 / INV-4 —
  мерж только через Gitea PR-merge API); pre-receive не вводится.
- **Plane CE self-hosted ≈ 14 контейнеров** (web/admin/space/api/worker/beat/live/migrator +
  postgres/redis/mq/minio/proxy) — ресурсоёмко; часть первичной инициализации в CE недоступна
  по API → честные ручные чекпоинты (паттерн `manual-step` ORCH-009).

---

## 2. Объём (scope)

### 2.1. В объёме
- **Bundle-compose** — отдельный compose-комплект всего стека: орк + watchdog + Gitea +
  Plane-стек (~14 контейнеров); пиннинг версий; чистые именованные тома; согласованная
  сетевая достижимость (вебхуки в обе стороны).
- **Bootstrap-скрипт** — один запуск (команда/визард): поднять всё → дождаться
  готовности/миграций → инициализация Gitea (админ/токен) → инициализация Plane
  (instance/workspace/API-токен; CE-ограничения → явные manual-step чекпоинты) →
  онбординг sandbox-проекта (22 статуса/3 лейбла/репо/вебхуки — через `onboard_project.py`) →
  git-доступ агентов → сборка `.env`/`.env.watchdog` орка → health → smoke-подсказка.
- **Инициализация секретов** — генерация НОВЫХ на каждую инсталляцию (reuse `gen_secrets.py` +
  bundle-внутренние креды: пароли БД/брокера/хранилища Plane, админ Gitea); дефолтных паролей
  в репо нет.
- **`docs/deployment/BUNDLED_SETUP.md`** — инструкция запуска bundle по канону LITE_SETUP,
  включая **требования к хосту (RAM/диск/CPU/порты)**.
- **Структурные анти-дрейф тесты** (без docker/сети/LLM в CI) + полный зелёный pytest + CHANGELOG.
- Отметка Type B в `docs/operations/REPLICATION.md` §1 (границы трёх задач эпика).

### 2.2. Вне объёма (явно, не делать)
- Изменения рантайма: `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `.gitea/workflows/`,
  `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict/схема БД — байт-в-байт.
- Перенос наших задач/данных/секретов (stateless — решение Владельца 10.06).
- Автоматическая установка Claude CLI / выдача LLM-ключей / создание Telegram-ботов —
  внешние предусловия заказчика (документируются, не автоматизируются).
- HTTPS/домены/публичный reverse-proxy заказчика — за рамками bundle (документируется
  как ручной шаг при необходимости).
- Процедура обновления (upgrade) развёрнутого bundle; миграция Lite→Bundled; кластерные/
  multi-host топологии; мультитенантность (D5.6) и горизонтальный воркер-пул (D5.4).
- Какая-либо активация bundle на НАШЕМ боевом хосте.

---

## 3. Заинтересованные стороны
- **Владелец (Слава)** — раздаёт платформу заказчикам на тест; принимает результат.
- **Оператор заказчика** — целевой читатель BUNDLED_SETUP.md: чистый Linux-хост,
  docker+compose, без знания внутренностей платформы.
- **Self-hosting прод** (`orchestrator`, общий для всех проектов) — не должен быть затронут:
  задача — артефакты репо (compose/скрипт/доки/тесты), активируемые только явным запуском
  на ЦЕЛЕВОМ хосте.

---

## 4. Бизнес-требования (BR)

| ID | Требование | Связь |
|----|------------|-------|
| BR-1 | Единый bundle-compose (отдельный файл) поднимает ВЕСЬ стек одной командой: орк, watchdog, Gitea, Plane-стек. Корневой `docker-compose.yml` не форкается и не меняется. | AC-1, AC-6, FR-1 |
| BR-2 | Bootstrap-скрипт ОДНИМ запуском (команда/визард) доводит свежеподнятый стек до рабочего состояния: готовность/миграции → init Gitea → init Plane → онбординг sandbox-проекта → git-доступ агентов → конфиг орка → health. Шаги, физически недоступные через Plane CE API, оформляются явными интерактивными manual-step чекпоинтами (fail-safe, паттерн ORCH-009) — без молчаливых пропусков. | AC-1, FR-2 |
| BR-3 | После bootstrap smoke проходит: тестовый проект создан, тестовая задача доезжает минимум до артефактов `01–04` в ветке (минимальный сигнал REPLICATION §4 шаг 5); расширенно — до `done`. Вебхуки работают в ОБЕ стороны (Plane→орк, Gitea→орк, орк→Plane/Gitea API). | AC-2, FR-2/FR-6 |
| BR-4 | Stateless: каждая инсталляция стартует с чистых томов/БД (Plane, Gitea, орк) и НОВЫХ секретов (`gen_secrets.py` + bundle-внутренние креды). Боевые данные/секреты не используются ни на одном шаге; в репо нет ни одного реального секрета/дефолтного пароля. | AC-3, FR-3 |
| BR-5 | `docs/deployment/BUNDLED_SETUP.md` написан по канону LITE_SETUP (fenced-команды + «Проверка:» PASS/FAIL, плейсхолдеры вместо хост-специфики, канон не форкается — общие шаги ссылками на LITE_SETUP/ONBOARDING/REPLICATION) и фиксирует требования к хосту: RAM/диск/CPU/занимаемые порты (Plane ~14 контейнеров — ресурсоёмко). | AC-4, FR-4 |
| BR-6 | Переиспользование кирпичей без дублирования: секреты — `gen_secrets.py`; статусы/лейблы/репо/вебхуки — `onboard_project.py` (22 статуса — из `plane_sync._PLANE_NAME_TO_KEY`, нулевой дрейф); smoke — шаги REPLICATION §4. Bootstrap не реализует собственную копию этих канонов. | FR-2/FR-3, AC-7 |
| BR-7 | Идемпотентность/fail-safe: повторный запуск bootstrap безопасен (ensure/skip, без delete-операций); запуск на «грязном» хосте (существующие тома/занятые порты/нехватка ресурсов) → явный отказ preflight с понятной подсказкой, а не молчаливое переиспользование чужого состояния. | FR-2, AC-8 |
| BR-8 | Наш прод не затрагивается: вся задача — вне рантайма и вне конвейера; kill-switch не требуется (активация — только явный запуск человеком на целевом хосте, паттерн ORCH-009). | NFR-1/NFR-2, AC-6 |
| BR-9 | Анти-дрейф: структурные тесты держат bundle-канон (compose-структура, док-канон, env-ключи, FORBIDDEN-литералы, секрет-эвристика, кросс-ссылки); существующие `test_lite_setup_doc.py`/`test_no_host_hardcodes.py` остаются зелёными; полный `pytest tests/ -q` зелёный; CHANGELOG обновлён. | AC-5, AC-6, AC-7, FR-5 |

---

## 5. Нефункциональные требования (NFR)

| ID | Требование |
|----|------------|
| NFR-1 | **Рантайм/конвейер байт-в-байт:** `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, machine-verdict ключи, схема БД орка — не тронуты. Задача — docs+scripts+compose-bundle+tests. |
| NFR-2 | **Self-hosting безопасность:** ни один артефакт задачи не рестартит/не деплоит/не конфигурирует наш прод-контейнер; bundle-артефакты в нашем контуре инертны (никто их не исполняет). |
| NFR-3 | **Секрет-гигиена:** в репо не попадают реальные секреты, высокоэнтропийные литералы и хост-литералы (FORBIDDEN-скан `test_no_host_hardcodes.py` распространяется на новые артефакты); bootstrap не печатает секреты в лог; сгенерированные файлы конфигов — только на целевом хосте, в `.gitignore`. |
| NFR-4 | **Переносимость:** bundle не зависит от нашей инфраструктуры; вся хост-специфика — переменные/плейсхолдеры; целевая платформа — одиночный Linux x86_64 хост с docker+compose. |
| NFR-5 | **Норматив сопровождения** (зеркало NFR-5 ORCH-102): изменение шагов тиража в будущих задачах → обновление `BUNDLED_SETUP.md` в том же PR. |
| NFR-6 | **Воспроизводимость:** версии образов Gitea/Plane-стека зафиксированы (пиннинг тегов/digest, не `latest`); состав bundle детерминирован. |
| NFR-7 | **Без новых тяжёлых зависимостей:** bootstrap — в духе существующих скриптов (stdlib-инструментарий `gen_secrets.py`/`onboard_project.py`); точный стек (bash/python) — решение архитектора. |

---

## 6. Допущения и ограничения
- Целевой хост: чистый одиночный Linux x86_64 с установленными docker + docker compose;
  оператор имеет sudo. Прочие ОС — вне целевой платформы (best-effort).
- Ресурсы: Plane-стек ресурсоёмок; ориентир для проверки — **не менее 4 vCPU / 8 GB RAM /
  40 GB диска** (финальные минимумы УТОЧНЯЮТСЯ при реализации замером на тестовом
  развёртывании и фиксируются в BUNDLED_SETUP.md — см. AC-4; цифры выше — гипотеза, не факт).
- Внешние предусловия заказчика (bundle не поставляет): инсталляция/аутентификация Claude CLI
  + LLM-доступ Anthropic; Telegram-боты (трекер + watchdog) — опциональны, их отсутствие
  деградирует только нотификации (never-raise), не конвейер.
- Часть инициализации Plane CE недоступна по API (instance-setup/workspace/API-токен) —
  допускаются документированные интерактивные шаги внутри визарда; «одной командой» означает
  «один запуск bootstrap с явными чекпоинтами», а не «ноль действий человека».
- Версии upstream-образов (Plane CE/Gitea) фиксируются на момент реализации; их обновление —
  отдельные будущие задачи (NFR-6).

---

## 7. Критерии успеха (резюме; детали — 03-acceptance-criteria.md)
Пять AC из постановки Владельца (сохранены 1:1 как AC-1…AC-5) + производные проверяемые:
- AC-1 единый bundle-compose поднимает ВЕСЬ стек; bootstrap доводит до рабочего состояния
  одной командой/визардом.
- AC-2 после bootstrap smoke проходит (тестовый проект + задача доезжает).
- AC-3 stateless (чистые Plane/Gitea/БД, новые секреты).
- AC-4 BUNDLED_SETUP.md + требования к хосту (RAM/диск) задокументированы.
- AC-5 pytest зелёный; CHANGELOG.
- AC-6 корневой compose/рантайм не тронуты (анти-дрейф зелёный); AC-7 нулевой дрейф канонов
  (22 статуса/лейблы/секреты — через существующие кирпичи); AC-8 идемпотентность/fail-safe
  bootstrap; AC-9 секрет-гигиена новых артефактов.

---

## 8. Риски (детали — 10-tech-risks.md, заполняет архитектор)
- **R-1 Ресурсоёмкость Plane:** ~14 контейнеров → OOM/медленный старт на слабом хосте;
  смягчение — preflight-проверка ресурсов + честные требования в доке (AC-4).
- **R-2 Дыры Plane CE API:** первичная инициализация частично UI-only → ручные чекпоинты;
  риск — UX «одной команды» размывается; смягчение — явные manual-step с проверкой результата
  (паттерн ORCH-009), минимизация числа ручных шагов.
- **R-3 Дрейф upstream-образов:** «плавающие» теги ломают воспроизводимость → пиннинг (NFR-6).
- **R-4 Сетевая достижимость вебхуков:** орк (host network) ⟷ Plane/Gitea (bridge-сеть bundle)
  — двунаправленные URL должны быть согласованы bootstrap'ом; ошибка = «задача не появилась»
  (труднодиагностируемо); смягчение — smoke проверяет оба направления.
- **R-5 Соблазн форкнуть корневой compose** (анти-дрейф TC-04 `test_lite_setup_doc.py` упадёт)
  → bundle строго отдельным файлом.
- **R-6 Утечка секретов в логи/репо** при генерации bundle-кред → секрет-эвристика в тестах,
  запрет печати секретов (NFR-3).
