---
work_item: ORCH-104
stage: architecture
author_agent: architect
status: proposed
created_at: 2026-06-12
model_used: claude-opus-4-8
---

# adr-0040: Установщик Lite-тиража — `scripts/install_lite.py` (ORCH-104, 10a)

## Статус
Proposed

## Контекст

Эпик ORCH-10 (D5 «Масштаб»), тип **A — Lite**. adr-0037 (ORCH-102) дал golden-source инструкцию
`docs/deployment/LITE_SETUP.md` — надёжный, но **ручной** маршрут из 13 разделов («голый хост →
работающий конвейер»): ~6 предусловий, ~20 ключей `.env` по 4 группам, webhook'и Plane/Gitea
(иногда raw-SQL), два Telegram-бота, claude CLI, compose, онбординг, smoke. Долго; ошибки
(опечатка секрета → 401 HMAC; неверный uid; занятый порт) всплывают поздно — на `docker compose
up`, не в момент ввода. adr-0038 (ORCH-103) для **Bundled** уже доказал каркас установщика
`scripts/bootstrap_bundle.py`.

ORCH-104 автоматизирует happy-path §2–§11 одним интерактивным установщиком и добавляет раннюю
валидацию. Сквозной характер: вводится новый **операторский артефакт семьи тиража** и нормативная
**граница connect-only** (Lite *подключается* к чужой инфре, в отличие от Bundled, который её
*поднимает*) — это правило обязательно для будущих задач эпика ORCH-10. Детальный пакет решений
(D1…D15, исходы вопросов ТЗ OQ-1…OQ-7) — work-item ADR:
`docs/work-items/ORCH-104/06-adr/ADR-001-lite-installer.md`.

## Решение

1. **Новый установщик `scripts/install_lite.py`** — «connect-only»-сородич `bootstrap_bundle.py`.
   Имя `install_*` (не `bootstrap_*`) понятнее оператору и сигналит семантику «подключение к
   готовой инфре» против «bring-up» bundle (OQ-1).
2. **Самодостаточный один файл, stdlib-only** (OQ-4): проверенные примитивы эталона
   (`parse_env`/`render_env`/`manual_checkpoint`/`_write_private` 600/never-raise `_http`/
   `preflight_verdict`-стиль/exit-контракт `0/2/1`) **реплицированы**, а не вынесены в общий
   модуль. Так держится «один установочный файл» (BR-1) и `bootstrap_bundle.py` остаётся
   байт-в-байт (нулевой риск регресса Bundled). Вынос общего `scripts/_replication_lib.py` —
   позже, по rule-of-three (третий тиражный скрипт).
3. **Каноны не форкаются** (как adr-0037/0038): webhook-секреты — субпроцесс `gen_secrets.py`
   (никакого своего `token_hex`); регистрация проекта и 22 статуса — субпроцесс
   `onboard_project.py apply/verify` (`plane_sync._PLANE_NAME_TO_KEY`); `.env`/`.env.watchdog` —
   рендер из `.env.example`/`.env.watchdog.example`; стек — `docker-compose.yml`
   (ровно орк+watchdog, staging за профилем не поднимается).
4. **Граница connect-only — нормативно (OQ-5):** в Lite Plane/Gitea/Telegram — **чужие**
   инсталляции; установщик говорит с ними только по их API и **не мутирует** их. Webhook Plane =
   верифицируемый **manual-step** (печать инструкции пути A/Б, без исполнения raw-SQL в чужую БД —
   INV-5); истинный гейт webhook'а — smoke (§11). Контраст с bundle, который владеет Plane-БД и
   пробует INSERT.
5. **Управляемая установка зависимостей без тихого root (OQ-3):** точная команда под дистрибутив
   (`apt`/`dnf`), выполнение только при TTY + явном согласии + команда из allowlist; неизвестный
   дистрибутив/нет TTY/отказ → инструкция + `exit 2`. Логин claude CLI — manual-step с верификацией
   (OQ-6). Не-интерактив/CI — fail-closed `exit 2` с именем недостающего ключа; секреты из env, не
   из argv (OQ-7).
6. **Анти-дрейф — постоянная CI-гарантия:** новый `tests/test_install_lite_script.py` (структурный +
   юнит чистых функций + фейки HTTP/процессов; без сети/docker/LLM): ссылки на кирпичи, запрет
   собственной генерации секретов и захардкоженных 22 статусов, stdlib-only (AST), 0
   delete/force-операций, реюз `FORBIDDEN`/`find_violations` из `tests/test_no_host_hardcodes.py`,
   exit-контракт `{0,1,2}`. `LITE_SETUP.md` получает callout-указатель на установщик
   (рекомендованный путь; ручной маршрут — фолбэк), **13 `## N.`-разделов сохранены** →
   `test_lite_setup_doc.py` остаётся зелёным + ассертит ссылку. Норматив сопровождения (NFR-5):
   меняешь шаги установки → синхронизируй `LITE_SETUP.md` в том же PR.

### Что НЕ меняется
`src/**`, `docker-compose.yml`, `Dockerfile`, существующие `scripts/**` (включая
`bootstrap_bundle.py`/`gen_secrets.py`/`onboard_project.py`); `STAGE_TRANSITIONS`, состав
`QG_CHECKS`, семантика `check_*`, machine-verdict ключи, схема БД — байт-в-байт. Новый QG не
вводится (структурные тесты — в существующих гейтах). Kill-switch не нужен: активация — только явный
запуск оператором на его хосте (паттерн ORCH-009/101/102/103). Прод-контейнер в рамках задачи не
рестартуется. `07-infra-requirements.md`/`08-data-requirements.md` — N/A (топология нашего орка и
схема БД не меняются; предусловия хоста — golden source LITE_SETUP §2, не форкаются).

## Альтернативы
- **Общий модуль `_replication_lib.py` (DRY сейчас)** — отвергнуто: правка замороженного
  `bootstrap_bundle.py` = риск регресса Bundled + противоречит «одному файлу»; вынос по rule-of-three.
- **Имя `bootstrap_lite.py`** — отвергнуто: `install_*` понятнее оператору и верно сигналит
  connect-only.
- **Авто-драйв webhook Plane raw-SQL (как bundle)** — отвергнуто: в Lite Plane чужой; raw-SQL в
  чужую БД нарушает INV-5 и часто недостижим.
- **Тихий sudo-install / сторонний TUI (rich/click)** — отвергнуто: D-1 (только consent) и NFR-6
  (stdlib-only до первого `docker compose up`).
- **Замена `LITE_SETUP.md` установщиком** — отвергнуто: runbook остаётся golden source и фолбэком.

## Последствия
- Type A эпика ORCH-10 получает автоматизированный happy-path поверх ручного канона; ошибки
  секретов/URL ловятся в момент ввода (живая верификация), не на `up`. Полнота/анти-форк/отсутствие
  delete защищены CI.
- Цена: ~150 строк осознанного дублирования примитивов с `bootstrap_bundle.py` (rule-of-three
  триггер задокументирован); webhook Plane остаётся ручным (граница connect-only), верифицируется
  на smoke.
- Откат: удалить `scripts/install_lite.py` + `tests/test_install_lite_script.py`, вернуть
  callout-врезку `LITE_SETUP.md` и doc-правки — состояние 1:1 (scripts+docs+tests, без миграций и
  рантайм-изменений).

## Связи
adr-0037 (ORCH-102 — Lite-канон/`LITE_SETUP.md`; этот установщик автоматизирует его §2–§11),
adr-0038 (ORCH-103 — `bootstrap_bundle.py`, эталон каркаса; connect-only vs bring-up),
adr-0036 (ORCH-101 — фундамент 10-common: `gen_secrets.py`, `.env.example`, расхардкод хоста),
adr-0035 (ORCH-009 — `onboard_project.py`/22 статуса, переиспользуются субпроцессом; D10 — запрет
branch protection), adr-0033 (ORCH-100 — watchdog, C-1 отдельный бот в `.env.watchdog`),
adr-0008/INV-4 (ORCH-058 — staging-порт guard; merge-актор — основание connect-only-границы).
Детально — `docs/work-items/ORCH-104/06-adr/ADR-001-lite-installer.md`,
`docs/work-items/ORCH-104/10-tech-risks.md`.
