--- work_item: ORCH-104 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-11 model_used: claude-opus-4-8 --- # 01 — BRD: ORCH-104 — Установочный скрипт Lite-тиража (интерактивный installer) Work Item: **ORCH-104** · Repo: **orchestrator** (self-hosting) · Стадия: analysis Заказчик: Слава (поставщик платформы); конечный потребитель — внешний оператор Lite-тиража --- ## 1. Бизнес-контекст и проблема ### 1.1. Текущее состояние Type A эпика ORCH-10 («Lite»: заказчик разворачивает у себя только `orchestrator` + `orchestrator-watchdog`, подключая СВОИ Plane/Gitea/Telegram/LLM) закрыт задачей ORCH-102 **документом**: `docs/deployment/LITE_SETUP.md` — golden source из 13 нормативных разделов, ~30+ ручных fenced-команд и ~20 обязательных ключей нового хоста (§4.2 LITE_SETUP). Маршрут полный и честный (каждый шаг несёт «Проверка:»/PASS/FAIL), но **порог входа высокий**: - оператор вручную собирает и переносит в `.env` значения, которые машина определяет сама (uid/gid владельца каталога репо, gid группы docker, пути node / дистрибутива claude-code, занятость портов); - ошибки конфигурации (символ в секрете, рассинхрон тройки прод-порта, ключи watchdog не в том файле-носителе, branch protection на `main`) проявляются **поздно** — на smoke или первой задаче, а не в момент ввода; - каверзные шаги (webhook Plane CE без внешнего API — §5.4; два независимых Telegram-бота — §8; статусы строго через onboarding-CLI — §5.3) требуют дисциплины чтения дока. ### 1.2. Бизнес-запрос (источник — описание задачи Plane) Нужен **один установочный файл**, который: 1. на автомате выполняет установку Lite; 2. информацию, которую знает только пользователь (токен Plane и т.п.), **запрашивает в момент установки**; 3. **сканирует систему**, говорит чего не хватает и **предлагает установить**; 4. если на хосте **несколько инсталляций** (например, Plane) — **показывает их и предлагает выбрать**. Цель — максимально упростить установку для пользователей. ### 1.3. Прецедент в платформе (переиспользовать, не изобретать) - **Bundled (ORCH-103)** уже имеет установочный скрипт `scripts/bootstrap_bundle.py`: режимы `plan` (дефолт) / `apply` / `verify`, step-движок check→ensure (повторный запуск = каскад skip), exit-коды `0/2/1`, python stdlib-only, manual-checkpoint с обязательной верификацией, **ни одной delete-операции**, секреты никогда не печатаются. **Lite такого инструмента не имеет** — только док. ORCH-104 закрывает этот разрыв тем же выверенным паттерном, добавляя три новых способности: интерактивный сбор данных, скан предусловий с офером установки, discovery нескольких инсталляций с выбором. - **Кирпичи готовы:** `scripts/gen_secrets.py` (webhook-секреты; x-mode «существующий файл → отказ exit 2», перезапись только `--force`) и `scripts/onboard_project.py` (`plan`/`apply`/`verify`: Plane-проект + 22 статуса + лейблы, Gitea-репо + webhook, kit, merged-`ORCH_PROJECTS_JSON`). Канон статусов/лейблов **не форкается** — только кирпич. ### 1.4. Установленные факты (проверено по репо, не изобретать) - Маршрут Lite = LITE_SETUP.md §2–§12; кирпичи-каноны: REPLICATION.md (карта env §2, секреты §3, smoke §4), ONBOARDING.md (статусы §1), SETUP_WEBHOOKS.md, `.env.example` / `.env.watchdog.example` (канон 100% ключей). - Контур Lite: **Linux x86_64, Docker Engine + Compose v2, git, python3, node** (§2 LITE_SETUP); вне контура — вне гарантии. - Webhook Plane CE **не экспонирован во внешнем `/api/v1`** — настраивается через UI (если сборка показывает) или прямым SQL в Postgres Plane (§5.4 LITE_SETUP). - Branch protection на `main` **НЕ включать** (норматив §6.4, ADR D10 ORCH-009 / INV-4). - Telegram-канала **два и они независимы** (C-1 ORCH-100); токен орка для watchdog переиспользовать запрещено; sidecar читает только `.env.watchdog` (ловушка файла-носителя). - Тираж **stateless**: данные/БД/секреты боевого хоста не переносятся; секреты — только свежевыпущенные (§12 LITE_SETUP, REPLICATION §3/§5). - Скрипт живёт в репо → `git clone` чекаута остаётся единственным ручным предшагом (разрешает «курицу и яйцо» §3: к моменту запуска скрипта чекаут уже есть). - Структуру LITE_SETUP.md пиннит анти-дрейф тест `tests/test_lite_setup_doc.py` («13 разделов в порядке», кирпичи, env-ключи ⊂ `.env.example`) — правка дока требует синхронной правки теста в том же PR. --- ## 2. Объём (scope) ### 2.1. В объёме - **Новый операторский CLI** в `scripts/` (один файл; кандидат имени — `setup_lite.py`, финализирует архитектор), автоматизирующий маршрут LITE_SETUP §2–§12. - **Скан предусловий** хоста (§2 + §7 LITE_SETUP) с честными вердиктами и **офером установки** недостающего (с явного согласия; неподдерживаемый дистрибутив → manual-step). - **Discovery инсталляций Plane/Gitea** на хосте (через локальный Docker): ≥2 → показать и предложить выбрать; 1 → подставить по умолчанию; 0 → ручной ввод + честная подсказка. - **Интерактивный сбор** обязательных ключей нового хоста (§4.2) с **немедленной верификацией** каждого введённого значения фактическим вызовом (Plane API / Gitea API / Telegram getMe). - **Автодетект хост-параметров** (uid/gid, docker gid, пути node/claude-code, порты) — то, что машина может узнать сама, у пользователя не спрашивается. - **Сборка `.env` / `.env.watchdog`** от канонов `.env.example` / `.env.watchdog.example`; webhook-секреты — кирпичом `gen_secrets.py`; существующие файлы молча не перетираются. - **Запуск Lite-контура** (ровно орк + watchdog) и health-проверки; stateless-проверка чистоты. - **Регистрация проекта заказчика** строго кирпичом `onboard_project.py` (plan→apply→verify). - **Итоговый отчёт** PASS/FAIL/MANUAL + exit-коды `0/2/1`; **идемпотентный повторный запуск** (resume после manual-step). - **Обновление `docs/deployment/LITE_SETUP.md`** (скрипт = рекомендованный быстрый путь; ручной маршрут сохраняется как канон-fallback) + синхронные анти-дрейф тесты. ### 2.2. Вне объёма (явно, не делать) - **Установка/администрирование Plane и Gitea** — Lite-рамка: это продукты заказчика (§1 LITE_SETUP); кейс «нет инфраструктуры вовсе» закрыт Bundled (ORCH-103). Скрипт только обнаруживает и подключает. - Любые правки **`src/**`**, корневого `docker-compose.yml`, `Dockerfile`, схемы БД, `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` — рантайм байт-в-байт. - Teardown / uninstall / миграция данных (stateless-канон; delete-операций в скрипте нет). - Поддержка не-Linux платформ (контур Lite не расширяется). - Автоматизация интерактивного OAuth-логина claude CLI (логин — по инструкции Anthropic, вручную; скрипт только верифицирует результат). - Самодостаточный «скачиватель» до чекаута (`curl | bash`): канал дистрибуции чекаута согласуется с поставщиком (§3 LITE_SETUP) и не фиксирован платформой. - Изменение `bootstrap_bundle.py` (Bundled) и `onboard_project.py` (кирпич переиспользуется как есть). --- ## 3. Заинтересованные стороны - **Внешний оператор/заказчик Lite** — главный потребитель: ставит платформу одной командой, отвечая на вопросы по ходу; не обязан заранее читать 13 разделов. - **Поставщик платформы (Слава/Owner)** — снижение стоимости каждого внедрения и числа обращений «не взлетело»; принимает результат. - **Платформа/конвейер** — не затрагиваются: операторский инструмент вне рантайма; прод и enduro-trails не подвержены риску (активация — только явный запуск человеком). --- ## 4. Бизнес-требования (BR) | ID | Требование | Связь | |----|------------|-------| | BR-1 | **Один входной файл:** установка Lite выполняется одной командой из корня чекаута на голом python3; каждый шаг маршрута LITE_SETUP §2–§12 либо выполняется скриптом, либо явно выдаётся как ручной чекпоинт с верификацией результата. Молчаливых пропусков нет. | FR-1/FR-10, AC-1 | | BR-2 | **Скан системы:** скрипт определяет недостающие предусловия (docker/compose/git/python3/node/claude-code/uid-gid/docker-group/ssh/порты), честно перечисляет их и предлагает установить/исправить; установка — только с явного согласия пользователя; неподдерживаемое окружение → manual-step с готовыми командами. | FR-2, AC-2 | | BR-3 | **Запрос данных в момент установки:** все обязательные ключи нового хоста (§4.2 LITE_SETUP) запрашиваются интерактивно тогда, когда нужны; каждый токен/URL немедленно верифицируется фактическим вызовом; ввод секретов скрытый, значения секретов нигде не печатаются. | FR-4, AC-4 | | BR-4 | **Discovery с выбором:** при нескольких инсталляциях Plane/Gitea на хосте скрипт показывает кандидатов и предлагает выбрать; при одной — подставляет её по умолчанию; при нуле — ручной ввод и честная подсказка (Lite не ставит Plane/Gitea; «нет инфраструктуры» = маршрут Bundled). Вариант «ввести вручную» доступен всегда. | FR-3, AC-3 | | BR-5 | **Безопасность повторного запуска и чужих сред:** повторный запуск идемпотентен (каскад skip, resume после manual-step); существующие `.env`/`.env.watchdog` не перетираются молча; скрипт не содержит delete-операций, не трогает `main`, не рестартит чужие контейнеры; каждая мутация — с явного согласия. | FR-1/FR-6, AC-5/AC-11 | | BR-6 | **Результат — работающий Lite-контур:** ровно два контейнера (`orchestrator`, `orchestrator-watchdog`), `/health`–`/queue`–`/metrics` зелёные, stateless-чистота подтверждена, проект заказчика зарегистрирован кирпичом onboarding, smoke-инструкция выдана; итог прогона — однозначный вердикт + exit-код `0/2/1`. | FR-8/FR-9/FR-10, AC-9/AC-10 | | BR-7 | **Канон не форкается:** скрипт автоматизирует маршрут LITE_SETUP.md и использует существующие кирпичи (`gen_secrets.py`, `onboard_project.py`); сам канон-знание (статусы, ключи, нормативы) не дублирует; изменение маршрута → обновление LITE_SETUP.md в том же PR (норматив NFR-5 ORCH-102); соответствие закреплено анти-дрейф тестами. | FR-11, AC-14 | | BR-8 | **Рантайм не тронут:** `src/**`, корневой compose, `Dockerfile`, `STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД — байт-в-байт; kill-switch не нужен (активация — только явный запуск оператором; паттерн ORCH-009/102/103). | NFR-7, AC-13 | --- ## 5. Нефункциональные требования (NFR) | ID | Требование | |----|------------| | NFR-1 | **stdlib-only:** скрипт работает на голом python3 целевого хоста ДО любого venv/`docker compose up`; модули платформы (`src/**`) не импортируются; закрепляется ast-сканом в тестах (паттерн `test_bootstrap_script.py`). | | NFR-2 | **Безопасность среды заказчика:** никаких delete/необратимых операций; мутации — только локальный хост и только с явного согласия; БД Plane заказчика мутируется (webhook, Path Б §5.4) исключительно по явному согласию с обязательной верификацией; branch protection скрипт сам НЕ удаляет (только честный FAIL с лечением). | | NFR-3 | **Секрет-гигиена:** значения секретов не печатаются и не логируются (только имена ключей/пути файлов); ввод — скрытый; секреты — только свежевыпущенные (stateless, REPLICATION §3); боевые значения исходного хоста не используются даже как подсказки-дефолты. | | NFR-4 | **Честность шагов:** каждый шаг — явный PASS/FAIL/MANUAL со ссылкой на соответствующий § LITE_SETUP; «молчаливый пропуск запрещён» (паттерн ORCH-103). | | NFR-5 | **Тестируемость без TTY/сети/docker:** вся решающая логика (вердикты предусловий, классификация discovery, когерентность портов, рендер env, аргументы onboard, step-движок) — чистые функции; интерактив — за инжектируемым I/O; pytest-прогон детерминирован. | | NFR-6 | **Идемпотентность/resume:** step-движок check→ensure; повторный запуск пропускает выполненное и продолжает с первого незавершённого шага; manual-step → exit 2 → «resume» = просто повторный запуск (паттерн bootstrap_bundle). | | NFR-7 | **Self-hosting безопасность:** случайный запуск на хосте с живым продом не ломает ничего: ранний отказ по существующему `.env` (без force), read-only режим диагностики, никакого воздействия на уже бегущие контейнеры без согласия. | | NFR-8 | **Поддержка канона:** LITE_SETUP.md, CHANGELOG.md, витрина `docs/overview/` (если затронуто описание тиража) обновляются в том же PR (правило агентов №2); анти-дрейф тесты дока остаются зелёными. | --- ## 6. Допущения и ограничения - Контур — как у Lite: Linux x86_64, Docker Engine + Compose v2; скрипт контур не расширяет. - Plane/Gitea — инсталляции заказчика, сетево доступны с хоста оркестратора; их установка — вне задачи. - Discovery гарантируется для **docker-инсталляций** (контейнеры с опознаваемыми образами/метками compose); native/k8s-инсталляции честно уходят в ручной ввод URL — это не FAIL discovery. - Запуск — из корня чекаута репо `orchestrator`; clone чекаута — ручной предшаг (канал дистрибуции — договорённость с поставщиком, §3 LITE_SETUP). - Язык UX скрипта — русский (паттерн `gen_secrets.py`/`bootstrap_bundle.py`); сообщения ссылаются на разделы LITE_SETUP.md как на полный канон. - Интерактивный OAuth-логин claude CLI принципиально не автоматизируем скриптом — верифицируется результат (читаемость кредов uid'ом контейнера). --- ## 7. Критерии успеха (резюме; детали — 03-acceptance-criteria.md) - AC-1 одна команда, режимы и идемпотентный повтор; - AC-2 скан предусловий с честными вердиктами и офером установки; - AC-3 discovery 0/1/много с выбором и ручным fallback; - AC-4 интерактивный сбор с немедленной верификацией и секрет-гигиеной; - AC-5/AC-6 корректная сборка `.env` (обязательные ключи, свежие секреты, когерентные порты, отказ перетирания); - AC-7/AC-8 нормативы C-1 (два бота) и §6.4 (branch protection) машинно охраняются; - AC-9 Lite-контур поднят и здоров; AC-10 проект зарегистрирован кирпичом; - AC-11/AC-12 exit-коды, resume, stdlib-only/no-delete гигиена; - AC-13 рантайм байт-в-байт; AC-14 документация обновлена синхронно. --- ## 8. Риски (кратко; детали — 10-tech-risks.md, заполняет архитектор) - **R-1 Запуск на боевом хосте по ошибке** → ранний guard существующего `.env`/живого инстанса; read-only диагностика дефолтно безопасна. - **R-2 Офер установки пакетов** — вмешательство в систему заказчика → только явное согласие, печать точной команды; неопределимый пакетный менеджер → manual-step. - **R-3 SQL-вставка webhook в Postgres Plane** (Path Б §5.4) инвазивна → только согласие + подтверждённый пользователем контейнер БД + верификация; UI-путь предпочтителен. - **R-4 Дрейф скрипта от канона LITE_SETUP** (двойной источник истины) → норматив same-PR + анти-дрейф тесты (ключи ⊂ `.env.example`, кирпичи, упоминание скрипта в доке). - **R-5 Интерактивность против автоматизации** (CI/non-TTY: риск зависания) → инжектируемый I/O, честный отказ/неинтерактивная альтернатива в non-TTY. - **R-6 Ложноположительный discovery** (чужой контейнер опознан как Plane) → выбор всегда за пользователем, ручной ввод всегда доступен, токен-верификация всё равно обязательна.