architect(ET): auto-commit from architect run_id=654
All checks were successful
CI / test (push) Successful in 56s

This commit is contained in:
2026-06-12 10:42:03 +03:00
parent be90632068
commit a681d6e3f7
4 changed files with 526 additions and 0 deletions

View File

@@ -210,6 +210,29 @@ sidecar читает только `.env.watchdog`; C-1 ORCH-100 — отдель
(docs+tests). Подробнее: [adr-0037](adr/adr-0037-lite-replication-canon.md), детально —
`docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md`.
**Установщик Lite (ORCH-104 — design).** Поверх ручного канона ORCH-102 вводится интерактивный
**установщик `scripts/install_lite.py`** — «connect-only»-сородич `bootstrap_bundle.py` (тот
*поднимает* Plane/Gitea, этот *подключается* к уже существующим заказчика), автоматизирующий
happy-path `LITE_SETUP.md` §2§11. **Самодостаточный stdlib-only один файл** (примитивы эталона
реплицированы, не вынесены — `bootstrap_bundle.py` байт-в-байт; вынос общего модуля — по
rule-of-three): step-движок `check→ensure`, режимы `plan`/`apply`/`verify`, exit `0/2/1`, honest
`manual_checkpoint`. Скан предусловий хоста + управляемая установка зависимостей (точная команда
под `apt`/`dnf`, выполнение только с явным согласием при TTY; тихого root-инсталла нет, D-1);
best-effort детект существующих Plane/Gitea (`docker ps` + порт-пробы, ранжир unauth-liveness) с
ручным фолбэком; интерактивный сбор токенов/URL с **живой верификацией ДО записи** (`getpass`,
Plane `/projects/`, Gitea `/user`, Telegram `getMe`). **Каноны не форкаются:** webhook-секреты —
`gen_secrets.py`, регистрация проекта/22 статуса — `onboard_project.py`, env — рендер из
`.env.example`/`.env.watchdog.example`, стек — `docker-compose.yml`. **Граница connect-only
(нормативно):** webhook Plane — верифицируемый **manual-step** (печать инструкции пути A/Б, без
исполнения raw-SQL в чужую БД — INV-5; истинный гейт — smoke §11); логин claude CLI — manual-step
с верификацией. Гигиена секретов: `getpass`, права 600, без молчаливой перезаписи (NFR-2). Не-интерактив/CI
— fail-closed `exit 2` с именем недостающего ключа; секреты из env, не из argv. Анти-дрейф —
новый `tests/test_install_lite_script.py` (структурный + юнит + фейки) + ассерт ссылки на
установщик в `test_lite_setup_doc.py` (13 разделов сохранены). Рантайм/конвейер — байт-в-байт
(scripts+docs+tests); kill-switch не нужен (активация — явный запуск оператором). Подробнее:
[adr-0040](adr/adr-0040-lite-installer-canon.md), детально —
`docs/work-items/ORCH-104/06-adr/ADR-001-lite-installer.md`.
**Type B — Bundled (ORCH-103).** Закрывает эпик ORCH-10: весь стек одним комплектом
(орк + watchdog + Gitea + Plane CE ≈1314 контейнеров) для заказчика без собственной
инфраструктуры. Состав Plane — зеркало официального selfhost-référence v0.23.1

View File

@@ -0,0 +1,106 @@
---
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`.