Files
orchestrator/docs/work-items/ORCH-104/02-trz.md
claude-bot be90632068
All checks were successful
CI / test (push) Successful in 58s
analyst(ET): auto-commit from analyst run_id=653
2026-06-12 10:29:36 +03:00

194 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
work_item: ORCH-104
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-12
model_used: claude-opus-4-8
---
# 02 — ТЗ (TRZ): ORCH-104 — Установочный скрипт для Lite
Work Item: **ORCH-104** · Repo: **orchestrator** · Стадия: analysis
> ТЗ описывает **конкретные требования к реализации**, выведенные из BRD и фактического кода.
> Архитектурное обоснование (структура скрипта, точные эвристики детекта, общий код с
> `bootstrap_bundle.py`, финальное имя файла) — задача архитектора (`06-adr/`). Открытые вопросы —
> §12.
## 1. Сводка изменения
Добавляется **один интерактивный установщик** `scripts/install_lite.py`, автоматизирующий ручной
маршрут `docs/deployment/LITE_SETUP.md` §2§11 для Lite-тиража (Type A). Скрипт сканирует хост,
детектит/предлагает доустановить зависимости, обнаруживает существующие Plane/Gitea (выбор при
нескольких), интерактивно собирает и **живо верифицирует** токены/URL, выпускает секреты кирпичом
`gen_secrets.py`, собирает `.env`/`.env.watchdog` из канон-`.example`, поднимает `orchestrator` +
`orchestrator-watchdog`, регистрирует проект кирпичом `onboard_project.py` и проверяет health.
Изменение **аддитивно** и **вне рантайма**: `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД — не
трогаются; kill-switch не нужен (активация — только явный запуск). Каноны не форкаются.
## 2. Задействованные модули / пути
| Путь | Действие |
|------|----------|
| `scripts/install_lite.py` | **создать** — интерактивный установщик Lite (entry-point; имя — OQ-1) |
| `scripts/gen_secrets.py` | **переиспользовать** (subprocess-кирпич), без изменений |
| `scripts/onboard_project.py` | **переиспользовать** (subprocess-кирпич, `apply`/`verify`), без изменений |
| `.env.example`, `.env.watchdog.example` | **переиспользовать** как канон-источник рендера, без изменений |
| `docker-compose.yml` | **переиспользовать** (`docker compose up -d --build`), без изменений |
| `scripts/bootstrap_bundle.py` | **эталон-паттерн** (parse_env/render_env/preflight_verdict/manual_checkpoint/exit-коды) — не изменяется; общий код — OQ-4 |
| `docs/deployment/LITE_SETUP.md` | **обновить** — указатель на установщик как рекомендованный путь + синхронизация шагов |
| `tests/test_install_lite_script.py` | **создать** — unit (чистые функции) + структурные анти-дрейф тесты |
| `tests/test_lite_setup_doc.py` | **обновить** — ассерт ссылки на установщик; существующие ассерты зелёные |
| `CLAUDE.md`, `CHANGELOG.md`, `docs/architecture/README.md`, `docs/overview/` | **обновить** (docs golden-source, правило агентов №2) |
## 3. Функциональные требования
### FR-1 — Entry-point и режимы
Скрипт `scripts/install_lite.py`, запускаемый из корня чекаута: `argparse`, позиционный `mode ∈
{plan, apply, verify}` (дефолт `plan`). Exit-коды (контракт): **0** — успех; **2** — остановка на
manual-step / незавершённое предусловие / нет TTY; **1** — ошибка. `plan` — ноль мутаций (печать
плана + read-only preflight-диагностика). `apply` — полный прогон step-движком `check→ensure`
(повтор = каскад skip; «resume» = повторный запуск). `verify` — read-only пост-проверка
(health-контракты + `onboard_project.py verify`). Привязка: BR-1, BR-7.
### FR-2 — Скан предусловий хоста (preflight)
Read-only снимок хоста (по образцу `bootstrap_bundle.collect_facts`/`preflight_verdict`): наличие
`docker`, `docker compose` v2, `git`, `python3`, `node`, `claude` CLI + читаемость кред; свободность
портов (прод-порт `ORCH_DEPLOY_PROD_TARGET_PORT` дефолт 8500, при self-hosting-вилке staging 8501);
`uid`/`gid`/`docker-gid` и владелец каталога репозиториев; наличие ssh-каталога. Чистая функция-
вердикт возвращает `(blockers, warnings)`; человекочитаемый список «есть/нет». Привязка: BR-2.
### FR-3 — Управляемая установка зависимостей (D-1)
Для каждого блокера-зависимости: детект пакетного менеджера (`apt`/`dnf` по наличию бинаря /
`/etc/os-release`) → **точная команда** установки (чистая функция «дистрибутив+пакет → команда»);
печать команды; для безопасных — предложение выполнить с согласия (`y/N`, `input()`); отказ/нет
TTY/неизвестный дистрибутив → печать инструкции и `exit 2` (никакой молчаливой root-мутации).
Привязка: BR-3, AC-3.
### FR-4 — Детект существующих Plane/Gitea + выбор (D-2)
Best-effort обнаружение (never-raise, NFR-3): кандидаты из `docker ps` (имена/образы, похожие на
Plane: `plane-*`/`makeplane`/`proxy`; Gitea: `gitea/gitea`/`gitea-*`) и из слушающих портов
(типовые Plane 80/8080/443, Gitea 3000). По кандидату — проба живости (Plane: `GET /api/instances/`;
Gitea: `GET /api/v1/version`). Чистая функция формирует ранжированный список кандидатов. Поведение:
0 кандидатов → запрос ручного URL; ≥2 → нумерованный список + выбор (`input()` индекс), вне
диапазона → ручной ввод; 1 → предложить с подтверждением. Выбор наполняет `ORCH_PLANE_*` /
`ORCH_GITEA_*`. Привязка: BR-5, AC-4.
### FR-5 — Интерактивный сбор данных + живая верификация
Honest-checkpoint контракт (как `bootstrap_bundle.manual_checkpoint`): для каждого требуемого
секрета/параметра — печать откуда взять (ссылка на LITE_SETUP §5§8), скрытый ввод секрета
(`getpass`), **верификация ДО записи**: Plane — `GET /api/v1/workspaces/<slug>/projects/` с
`X-API-Key`; Gitea — `GET /api/v1/user` с `Authorization: token`; Telegram — `GET /bot<token>/getMe`.
Провал → повтор (до N) или `exit 2` с подсказкой, значение **не пишется**. Авто-детект и
пред-заполнение всего детектируемого (uid/gid/docker-gid/порты/пути/node/claude/выбранные URL) —
оператор только подтверждает. Привязка: BR-4, AC-5.
### FR-6 — Выпуск webhook-секретов кирпичом `gen_secrets.py`
`ORCH_PLANE_WEBHOOK_SECRET` / `ORCH_GITEA_WEBHOOK_SECRET` выпускаются **строго** субпроцессом
`gen_secrets.py` (никакого собственного `secrets.token_hex` в установщике — анти-форк, AC-7); если
уже присутствуют в `.env` и валидны — пропуск (не перетирать без `--force`). Привязка: BR-6, NFR-2.
### FR-7 — Сборка `.env` / `.env.watchdog`
Идемпотентный рендер из канона (`render_env`-паттерн): существующий файл — обновить ключи-
override, отсутствующий — отрендерить из `.env.example` / `.env.watchdog.example`; комментарии
канона сохранены; неизвестные ключи — управляемым блоком в конец; запись правами **600**; значения
секретов в stdout/лог **не попадают**; молчаливой перезаписи нет. Watchdog-ключи (`WATCHDOG_TG_*`)
кладутся **только** в `.env.watchdog` (файл-носитель, LITE_SETUP §4.3). Привязка: BR-6, NFR-2.
### FR-8 — Подъём стека + готовность
`docker compose up -d --build` ровно `orchestrator` + `orchestrator-watchdog` (staging НЕ
поднимается — за профилем). Ожидание готовности поллингом `GET /health` (таймаут). Перед записью
`ORCH_PROJECTS_JSON` стек уже жив. Привязка: BR-1.
### FR-9 — Регистрация проекта кирпичом `onboard_project.py`
Сбор параметров проекта (имя/repo/prefix/стек/тест-команда/порты/webhook-URL — флаги или интерактивно),
вызов `onboard_project.py apply` затем `verify` субпроцессом; парс merged-`ORCH_PROJECTS_JSON` из
отчёта и запись в `.env`; ручные пункты отчёта (manual-step) пробрасываются оператору. **Никакого
собственного создания статусов/лейблов/репо** (анти-форк, 22 статуса — только онбординг-кирпич).
Привязка: BR-6, AC-7.
### FR-10 — Health-верификация + сводка
После `apply` (и в `verify`): `GET /health` → 200, `GET /queue` / `GET /metrics` → валидный JSON.
Итоговая сводка по шагам (`ok`/`skipped`/`manual-step`) + общий вердикт; любой FAIL → `exit 1` с
диагностикой (хвост `docker logs` / снапшот). Привязка: BR-7, AC-11.
### FR-11 — Синхронизация документации
`LITE_SETUP.md` дополняется указателем «рекомендованный путь — `install_lite.py`; ручной маршрut
ниже как фолбэк/референс». Обновляются `CLAUDE.md` (раздел тиража), `docs/architecture/README.md`
(Type A), `docs/overview/` (если затронута витрина), `CHANGELOG.md`. Привязка: BR-9, AC-12.
## 4. Изменения API
**Нет.** Установщик — вне процесса орка; обращается только к существующим read-only эндпоинтам
(`/health`, `/queue`, `/metrics`) как HTTP-клиент и к собственным Plane/Gitea/Telegram заказчика.
Новых/изменённых эндпоинтов оркестратора не вводится.
## 5. Изменения схемы БД
**Нет.** Установщик не касается БД оркестратора (её создаёт сам орк пустой при первом старте,
stateless-инвариант LITE_SETUP §12).
## 6. Требования к новым/изменённым QG checks
**Нет.** `QG_CHECKS` / `check_*` / `STAGE_TRANSITIONS` / machine-verdict ключи — байт-в-байт не
трогаются (INV-1). Установщик не участвует в решении ни одного гейта.
## 7. Конфигурация
Новых **рантайм**-ключей `config.py` / kill-switch — **нет** (NFR-1; активация = явный запуск).
Установщик читает/пишет только `.env` / `.env.watchdog` (канон ключей — `.env.example` /
`.env.watchdog.example`, ORCH-101). CLI-флаги установщика (имена — OQ-7): режим + параметры проекта
для `onboard_project.py` (`--repo`/`--prefix`/`--stack`/…), возможный `--force` (перевыпуск
секретов), возможный `--non-interactive`/значения из env для CI.
## 8. Наблюдаемость
- Прогресс-лог по шагам (`ok`/`skipped`/`manual-step`/`error`) — **без значений секретов** (только
имена ключей/пути файлов, NFR-2).
- Итоговая сводка PASS/FAIL + код выхода `0/2/1`.
- `manual_checkpoint` печатает точную инструкцию и верифицирует результат (молчаливый пропуск
запрещён); без TTY → `exit 2` с той же инструкцией.
## 9. Артефакты pipeline (создаются/обновляются)
- `scripts/install_lite.py` (новый исполняемый артефакт).
- `tests/test_install_lite_script.py` (новый), `tests/test_lite_setup_doc.py` (обновление).
- `docs/work-items/ORCH-104/06-adr/ADR-001-<slug>.md` (архитектор) + опц. сквозной
`docs/architecture/adr/adr-NNNN-*.md`.
- `docs/deployment/LITE_SETUP.md`, `CLAUDE.md`, `docs/architecture/README.md`, `docs/overview/`,
`CHANGELOG.md` — обновления (BR-9).
## 10. Совместимость / регресс
Аддитивно: новый файл-скрипт + новый тест + правки docs. Существующие кирпичи (`gen_secrets.py`,
`onboard_project.py`) и compose — байт-в-байт. Полный регресс `pytest tests/ -q` остаётся зелёным.
Обратимость — тривиальная (удаление нового файла/теста). Область раската — только хосты заказчиков
Lite; **наш прод не затронут** (установщик исполняется на чужом хосте, говорит только с локальным
хостом и инфраструктурой заказчика).
## 11. Инварианты (не нарушать)
- **INV-1** — `src/**` / `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключи /
схема БД — байт-в-байт.
- **INV-2** — Каноны не форкаются: секреты — `gen_secrets.py`; статусы/лейблы/репо/webhook —
`onboard_project.py`; env — из `.env.example`/`.env.watchdog.example`; стек — из `docker-compose.yml`.
- **INV-3** — Нет delete-операций (никаких `docker … rm`, `rm -rf`, удаления веток, force-push).
- **INV-4** — Секреты не печатаются; `.env`/`.env.watchdog` — права 600; без молчаливой перезаписи.
- **INV-5** — Никогда не трогает наш прод / `main` / force-push; говорит только с локальным хостом и
собственными Plane/Gitea/Telegram заказчика.
- **INV-6** — Платформенные конвенции: репо `orchestrator`; имена compose-сервисов/профиля —
константы; staging за профилем; `ORCH_STAGING_PORT` ≠ прод-порт (guard ORCH-058) — установщик
уважает, не форкает.
- **INV-7** — stdlib-only (NFR-6).
## 12. Открытые вопросы для архитектора (OQ — не блокируют анализ)
- **OQ-1** — Финальное имя/путь: `scripts/install_lite.py` (понятно конечному оператору) vs
`scripts/bootstrap_lite.py` (симметрия с `bootstrap_bundle.py`). Рекомендация анализа —
`install_lite.py`.
- **OQ-2** — Точные эвристики детекта Plane/Gitea (паттерны имён/образов контейнеров, набор
портов/URL-проб, ранжирование уверенности).
- **OQ-3** — Какие зависимости считать «безопасными для авто-выполнения с согласия» (напр.
`pip install -r requirements.txt` в venv — да; `apt install docker` под sudo — только consent;
claude CLI через npm); владелец distro-команд-карты.
- **OQ-4** — Общий код с `bootstrap_bundle.py` (вынести `parse_env`/`render_env`/`manual_checkpoint`
в общий stdlib-модуль) vs самодостаточный один файл (ради «1 установочный файл» и stdlib-only).
Trade-off DRY ↔ простота/одно-файловость.
- **OQ-5** — Драйвить ли путь Б Plane-webhook (raw-SQL, LITE_SETUP §5.4) автоматически (как
`bootstrap_bundle.step_plane_webhook`) или всегда оставлять верифицируемым manual-step.
- **OQ-6** — Подтвердить: интерактивный логин claude CLI остаётся manual-step с верификацией
(`claude --version` + читаемость кред), не автоматизируется.
- **OQ-7** — Набор CLI-флагов/env для не-интерактивного (CI) прогона: какие входы принимают
флаги/env vs только prompt.