233 lines
22 KiB
Markdown
233 lines
22 KiB
Markdown
---
|
||
work_item: ORCH-103
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-11
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 02 — ТЗ (TRZ): ORCH-103 — ORCH-10b Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт
|
||
|
||
Work Item: **ORCH-103** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
> ТЗ фиксирует **что** должно измениться и **где** (артефакты/контракты/границы). **Как**
|
||
> (расположение bundle-каталога, состав сервисов Plane-стека, язык/режимы bootstrap, механизм
|
||
> сетевой связности) — решает архитектор в `06-adr/`. Архитектурные решения здесь не принимаются.
|
||
|
||
---
|
||
|
||
## 1. Сводка изменения
|
||
|
||
Добавить в репо **Bundled-комплект тиража (Type B эпика ORCH-10)**: (1) отдельный
|
||
**bundle-compose** всего стека (орк + watchdog + Gitea + Plane-стек ~14 контейнеров),
|
||
(2) **bootstrap-скрипт**, доводящий свежеподнятый стек до рабочего конвейера одним запуском
|
||
(с явными manual-step чекпоинтами там, где Plane CE API не позволяет автоматизацию),
|
||
(3) **генерацию новых секретов** на инсталляцию (reuse `gen_secrets.py` + bundle-внутренние
|
||
креды), (4) инструкцию **`docs/deployment/BUNDLED_SETUP.md`** с требованиями к хосту,
|
||
(5) **структурные анти-дрейф тесты**. Всё — вне рантайма и вне конвейера: `src/**`, корневой
|
||
`docker-compose.yml`, `Dockerfile`, `STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД — байт-в-байт
|
||
(паттерн ORCH-009/ORCH-102). Kill-switch не требуется: активация — только явный запуск
|
||
оператором на целевом хосте.
|
||
|
||
---
|
||
|
||
## 2. Задействованные модули / пути
|
||
|
||
| Путь | Действие | Назначение |
|
||
|------|----------|------------|
|
||
| `deploy/bundled/docker-compose.yml` *(рабочая гипотеза; финальное расположение/имя — ADR; далее «bundle-compose»)* | **создать** | Compose-комплект всего стека (FR-1) |
|
||
| `deploy/bundled/.env.bundled.example` *(имя — ADR; далее «bundle-конфиг-канон»)* | **создать** | Канон 100% переменных bundle (порты/версии/плейсхолдеры кред), паттерн `.env.watchdog.example` |
|
||
| `scripts/bootstrap_bundle.py` *(имя/язык — ADR)* | **создать** | Bootstrap-скрипт (FR-2) |
|
||
| `docs/deployment/BUNDLED_SETUP.md` | **создать** | Golden source инструкции Bundled-тиража (FR-4) |
|
||
| `docs/operations/REPLICATION.md` | **изменить (точечно)** | §1: строка Type B → ✅ ORCH-103 + ссылка на BUNDLED_SETUP.md (FR-6) |
|
||
| `tests/test_bundle_compose.py` | **создать** | Структура bundle-compose + изоляция корневого compose (FR-5) |
|
||
| `tests/test_bundled_setup_doc.py` | **создать** | Канон дока, env-ключи, FORBIDDEN/секрет-эвристика, кросс-рефы (FR-5) |
|
||
| `tests/test_bootstrap_script.py` | **создать** | Структурные/unit-ассерты bootstrap (FR-5) |
|
||
| `CHANGELOG.md` | **изменить** | Запись `feat: ORCH-103` |
|
||
| `.gitignore` | **изменить (при необходимости)** | Сгенерированные на хосте конфиги bundle не коммитятся (NFR-3) |
|
||
| **НЕ трогать:** `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `.gitea/workflows/**`, `.env.example` (кроме явно обоснованного в ADR аддитива), `onboarding/**`, промпты `.openclaw/agents/**` | — | NFR-1; анти-дрейф `test_lite_setup_doc.py` (TC-04: ровно 3 сервиса, нет `plane*`/`gitea*`) и `test_no_host_hardcodes.py` остаются зелёными |
|
||
|
||
---
|
||
|
||
## 3. Функциональные требования
|
||
|
||
### FR-1 — Bundle-compose всего стека (BR-1)
|
||
- **Отдельный файл**, корневой `docker-compose.yml` не изменяется (жёсткое ограничение:
|
||
анти-дрейф TC-04 `tests/test_lite_setup_doc.py` проверяет точное множество сервисов корневого
|
||
compose и запрещает подстроки `plane`/`gitea` в именах сервисов/образов/контейнеров).
|
||
- **Состав стека:** `orchestrator` (образ собирается из существующего корневого `Dockerfile` —
|
||
без его правки), `orchestrator-watchdog` (существующий `watchdog/Dockerfile`), Gitea
|
||
(+ её хранилище), полный Plane CE-стек (~14 контейнеров: web/admin/space/api/worker/beat/
|
||
live/migrator + postgres/redis/mq/minio/proxy — точный состав и версии пиннит архитектор по
|
||
upstream-référence). Staging-контур орка (8501) — НЕ в дефолтном `up` (вне скоупа заказчика;
|
||
включать ли за профилем — ADR).
|
||
- **Пиннинг версий** всех сторонних образов (тег или digest; не `latest`) — NFR-6.
|
||
- **Тома:** только именованные/каталожные тома bundle (узнаваемый префикс); чистый первый старт;
|
||
пересечений с томами/путями нашего прод-контура нет.
|
||
- **Сеть и достижимость (двунаправленно):** (a) Plane→орк и Gitea→орк webhooks доставляются;
|
||
(b) орк→Plane API и орк→Gitea API доступны; (c) git push/fetch агентов в Gitea работает.
|
||
Механизм (bridge-сеть + публикация портов, `extra_hosts: host-gateway`, host-network — что
|
||
выбрать) — ADR; ТЗ фиксирует только инвариант достижимости, проверяемый smoke (FR-6).
|
||
- **Порты:** карта портов по умолчанию задокументирована (BUNDLED_SETUP «Требования к хосту»);
|
||
порты конфигурируемы через bundle-конфиг; конфликт порта → отказ preflight bootstrap (FR-2),
|
||
не молчаливый сбой. Дефолт порта орка — существующий 8500 (`ORCH_DEPLOY_PROD_TARGET_PORT`).
|
||
- **Конфиг-канон:** все параметры bundle (порты/версии/пути/плейсхолдеры кред) — в
|
||
bundle-конфиг-каноне; key-set синхронизируется структурным тестом (паттерн key-sync
|
||
`.env.watchdog.example`, D5 ORCH-102). Ключи орка НЕ дублируются — `.env` орка собирается
|
||
bootstrap'ом из существующего канона `.env.example`.
|
||
|
||
### FR-2 — Bootstrap-скрипт: один запуск до рабочего состояния (BR-2, BR-6, BR-7)
|
||
Последовательность (нумерация — норматив поведения; механика шагов — ADR):
|
||
1. **Preflight (fail-fast, до любых мутаций):** docker+compose присутствуют; свободные
|
||
RAM/диск ≥ задокументированных минимумов; целевые порты свободны; тома bundle отсутствуют
|
||
(чистый хост) — иначе явный отказ с подсказкой (BR-7); наличие Claude CLI/кред — warning
|
||
(не блокер: конвейер без LLM не поедет, но стек поднимется).
|
||
2. **Секреты (FR-3):** генерация полного набора НОВЫХ секретов инсталляции.
|
||
3. **Up:** подъём bundle-compose; ожидание готовности каждого сервиса (healthcheck/готовность
|
||
БД/завершение миграций Plane и Gitea) с таймаутами и внятной диагностикой.
|
||
4. **Init Gitea:** административная учётка + API-токен (через официальные механизмы Gitea —
|
||
CLI/env/API; конкретика — ADR); branch protection НЕ настраивается (норматив D10 ORCH-009).
|
||
5. **Init Plane:** instance-setup/workspace/API-токен. Всё, что недоступно в CE по API, —
|
||
**интерактивный manual-step чекпоинт**: скрипт печатает точную инструкцию (URL/что нажать/
|
||
что ввести), ждёт подтверждения, **проверяет результат** (например, валидность введённого
|
||
API-токена запросом) и только тогда продолжает (fail-safe; молчаливый пропуск запрещён).
|
||
6. **Онбординг sandbox-проекта:** вызов `scripts/onboard_project.py apply` + `verify`
|
||
(22 статуса из `plane_sync._PLANE_NAME_TO_KEY`, лейблы `autoApprove`/`autoDeploy`/`Bug`,
|
||
Gitea-репо, per-repo webhook под глобальным секретом). Собственная реализация этих шагов
|
||
в bootstrap **запрещена** (BR-6, нулевой дрейф канона).
|
||
7. **Git-доступ агентов:** обеспечить push/fetch созданного репо из контейнера орка
|
||
(ssh-ключ + регистрация в Gitea ИЛИ токен-remote — механизм ADR); клон репо в repos-каталог
|
||
орка (`ORCH_HOST_REPOS_DIR`).
|
||
8. **Конфиг орка:** собрать `.env` (на базе `.env.example`: URL'ы Plane/Gitea bundle-инсталляции,
|
||
токены, webhook-секреты, `ORCH_PROJECTS_JSON` из вывода onboard) и `.env.watchdog`
|
||
(из `.env.watchdog.example`); файлы остаются только на целевом хосте.
|
||
9. **Health + итог:** `GET /health`, `GET /queue`, `GET /metrics` зелёные; финальная сводка
|
||
PASS/FAIL по всем шагам + следующая команда оператора (smoke FR-6).
|
||
|
||
Требования к скрипту:
|
||
- **Идемпотентность:** повторный запуск на уже-инициализированном bundle безопасен
|
||
(ensure/skip-семантика, как `onboard_project.py apply`); никаких delete-операций.
|
||
- **Exit-коды:** `0` — успех; `2` — остановка на manual-step/незавершённое предусловие;
|
||
`1` — ошибка (паттерн `onboard_project.py`).
|
||
- **Логи без секретов** (NFR-3): значения кред не печатаются (только имена ключей/пути файлов).
|
||
- **Никогда не адресует наш прод:** в скрипте нет боевых хостов/путей (FORBIDDEN-скан),
|
||
работает только с локальным docker целевого хоста.
|
||
- Желателен режим `plan` (печать шагов без мутаций, паттерн ORCH-009) — финально ADR.
|
||
|
||
### FR-3 — Инициализация секретов: новые на каждую инсталляцию (BR-4)
|
||
- **Webhook-секреты орка** — строго через существующий `scripts/gen_secrets.py`
|
||
(не реализовывать заново).
|
||
- **Bundle-внутренние креды** (генерирует bootstrap, криптослучайно, stdlib `secrets`):
|
||
пароли postgres/redis*/mq/minio Plane-стека, секрет-ключи Plane, админ-пароль и API-токен
|
||
Gitea. В репо — только плейсхолдеры в bundle-конфиг-каноне; **ни одного дефолтного пароля**.
|
||
- **Внешние секреты заказчика** (не генерятся, чек-лист в доке): Anthropic/Claude CLI доступ,
|
||
Telegram-токены (опционально), `ORCH_PLANE_API_TOKEN` (если выдаётся вручную на manual-step).
|
||
- Сгенерированные файлы: только на целевом хосте, права `600`, в `.gitignore`; повторный
|
||
запуск НЕ перетирает существующие секреты без явного флага (паттерн `--force` gen_secrets).
|
||
|
||
### FR-4 — `docs/deployment/BUNDLED_SETUP.md` (BR-5)
|
||
- **Канон LITE_SETUP** (ORCH-102): нормативные разделы в порядке маршрута оператора; каждый
|
||
исполняемый шаг = fenced-команда + явная «Проверка:» с PASS/FAIL; хост-специфика — только
|
||
плейсхолдеры; запрещены FORBIDDEN-литералы и реальные секреты (структурный тест).
|
||
- **Обязательные разделы** (минимум; точные заголовки — автор дока, проверяемость — тест):
|
||
(1) рамка Bundled (что входит/что НЕ входит: Claude CLI, Telegram, HTTPS; границы vs Lite);
|
||
(2) **требования к хосту** — RAM/диск/CPU/порты, явно «Plane ≈ 14 контейнеров», финальные
|
||
цифры — по замеру на тестовом развёртывании; (3) предусловия (docker/compose/sudo);
|
||
(4) получение кода; (5) секреты; (6) запуск bundle-compose; (7) bootstrap (включая перечень
|
||
manual-step чекпоинтов Plane); (8) LLM/Claude CLI (ссылкой на канон LITE_SETUP §7);
|
||
(9) Telegram (ссылкой на LITE_SETUP §8); (10) онбординг следующих проектов
|
||
(ссылкой на ONBOARDING.md); (11) smoke (шаги REPLICATION §4); (12) stateless-проверка;
|
||
(13) остановка/полный сброс инсталляции; (14) траблшутинг (минимум: webhook не доходит,
|
||
не хватает RAM/OOM, порт занят, claude не найден, Plane-миграции не завершились).
|
||
- **Канон не форкается:** общие с Lite шаги — ссылками (LITE_SETUP §5–§8, ONBOARDING §1,
|
||
REPLICATION §2–§4), не копипастой; fail-closed имена `Confirm Deploy`/`STOP` и «22 статуса» —
|
||
согласованы с `plane_sync._PLANE_NAME_TO_KEY` (число — сверкой импорта в тесте, не литералом).
|
||
|
||
### FR-5 — Структурные анти-дрейф тесты (BR-9)
|
||
Все тесты — без docker/сети/LLM/subprocess-мутаций (CI-безопасные; паттерн
|
||
`test_lite_setup_doc.py`):
|
||
- **bundle-compose:** файл существует, валидный YAML; обязательные сервисы присутствуют
|
||
(`orchestrator`, `orchestrator-watchdog`, Gitea, Plane-стек — по списку из ADR); все
|
||
сторонние образы пиннованы (нет `:latest`/безтегового образа); корневой
|
||
`docker-compose.yml` НЕ изменён (множество сервисов == текущему эталону);
|
||
- **док:** BUNDLED_SETUP.md существует, несёт обязательные разделы (включая «Требования к
|
||
хосту»), каждый env-ключ из дока существует в канонах (`.env.example` ∪ bundle-конфиг-канон),
|
||
кросс-ссылки на LITE_SETUP/ONBOARDING/REPLICATION присутствуют;
|
||
- **гигиена:** FORBIDDEN-литералы (импорт списка из `test_no_host_hardcodes.py`) отсутствуют
|
||
в bundle-compose/доке/bootstrap; секрет-эвристика (hex ≥32 / alnum ≥40, паттерн D8 ORCH-102)
|
||
по новым файлам;
|
||
- **bootstrap:** скрипт существует; структурно ссылается на `gen_secrets`/`onboard_project`
|
||
(не дублирует канон); не содержит delete-операций уровня `docker volume rm`/`rm -rf` вне
|
||
явного отдельного «сброс»-режима; чистые функции (preflight-решение, сборка плана шагов,
|
||
рендер `.env`) покрыты unit-тестами;
|
||
- **кросс-рефы:** REPLICATION.md §1 несёт отметку Type B → BUNDLED_SETUP.md; CHANGELOG
|
||
содержит `ORCH-103`.
|
||
|
||
### FR-6 — Smoke и наблюдаемость результата (BR-3)
|
||
- Smoke Bundled = шаги REPLICATION §4 (0–6) поверх bundle-инсталляции, зафиксированные в
|
||
BUNDLED_SETUP §smoke: config-резолв → `/health` → `/queue`+`/metrics` → onboard verify →
|
||
тестовая задача (Plane issue → «To Analyse» → job в очереди) → **минимальный сигнал:
|
||
артефакты `01–04` в ветке** → опционально полный цикл до `done`.
|
||
- Прохождение фиксируется оператором по PASS/FAIL каждого шага; это ручная приёмка AC-2
|
||
(e2e в CI не гоняется — нет docker/LLM).
|
||
|
||
---
|
||
|
||
## 4. Изменения API
|
||
**Нет.** Эндпоинты орка не добавляются/не меняются; bundle использует существующие
|
||
`/health`, `/queue`, `/metrics`, вебхуки `/webhook/plane`, `/webhook/gitea`.
|
||
|
||
## 5. Изменения схемы БД
|
||
**Нет** (схема БД орка не тронута). БД Plane/Gitea внутри bundle — их собственные, на чистых
|
||
томах инсталляции; к схеме орка отношения не имеют.
|
||
|
||
## 6. Требования к новым/изменённым QG checks
|
||
**Нет.** Реестр `QG_CHECKS`/`check_*`/`STAGE_TRANSITIONS` — байт-в-байт. Bundled-тираж — это
|
||
артефакты дистрибуции, а не гейты конвейера.
|
||
|
||
---
|
||
|
||
## 7. Совместимость / регресс
|
||
- **Kill-switch не требуется** (паттерн ORCH-009): артефакты вне рантайма; в нашем контуре
|
||
ничего их не исполняет; активация — явный запуск оператором на целевом хосте.
|
||
- **Нулевая регрессия:** корневой compose/`Dockerfile`/`src/**` не изменены ⇒ наш прод,
|
||
staging-контур и enduro-trails не затронуты по построению; существующие анти-дрейф тесты
|
||
(`test_lite_setup_doc.py`, `test_no_host_hardcodes.py`, канон-тесты ORCH-009) остаются
|
||
зелёными без правки их ассертов.
|
||
- **Обратимость:** удаление bundle-каталога/скрипта/дока возвращает репо в текущее состояние;
|
||
на целевом хосте полный сброс = задокументированная процедура (FR-4 §13).
|
||
- **Эскалация:** если при реализации выяснится необходимость править `src/**`/корневой compose
|
||
(например, недостающая параметризация, не закрытая ORCH-101) — это выход за рамки ТЗ:
|
||
остановиться и вернуть задачу с обоснованием (CLAUDE.md правило 4), не «дотачивать молча».
|
||
|
||
---
|
||
|
||
## 8. Артефакты pipeline (создать/обновить в ТОМ ЖЕ PR)
|
||
- `docs/work-items/ORCH-103/06-adr/ADR-001-<slug>.md` — решения архитектора (см. §9 OQ);
|
||
при сквозном значении — зеркало в `docs/architecture/adr/adr-NNNN-<slug>.md`.
|
||
- `docs/architecture/README.md` — раздел «Bundled-тираж (ORCH-103)» рядом с 10-common/Lite.
|
||
- `CLAUDE.md` — краткий абзац Type B (паттерн абзацев ORCH-101/102).
|
||
- `docs/operations/REPLICATION.md` §1 — отметка Type B (FR-6).
|
||
- `CHANGELOG.md` — `feat: ORCH-103 …`.
|
||
- При выявлении инфра-предусловий целевого хоста — `07-infra-requirements.md` (архитектор).
|
||
|
||
---
|
||
|
||
## 9. Открытые вопросы для архитектора (не блокируют анализ)
|
||
- **OQ-1** Расположение/имя bundle-каталога и compose-файла (`deploy/bundled/` vs `bundle/`;
|
||
один compose vs include-композиция); судьба staging-контура орка в bundle (исключить vs
|
||
профиль).
|
||
- **OQ-2** Точный состав/версии Plane CE-стека (по upstream selfhost-référence) и Gitea;
|
||
стратегия пиннинга (тег vs digest).
|
||
- **OQ-3** Перечень физически автоматизируемых шагов инициализации Plane CE (instance-setup/
|
||
workspace/API-токен): что через API/CLI/seed, что — manual-step чекпоинт.
|
||
- **OQ-4** Язык и режимы bootstrap (python stdlib vs bash; `plan`/`apply` vs линейный визард);
|
||
способ ожидания готовности (healthchecks vs poll).
|
||
- **OQ-5** Механизм сетевой связности орк (host network?) ⟷ bundle bridge-сеть: публикация
|
||
портов, `host-gateway`, либо весь bundle в host-network — и согласование URL вебхуков.
|
||
- **OQ-6** Механизм git-доступа агентов к bundle-Gitea (ssh-ключ vs http-токен) и наполнение
|
||
repos-каталога.
|
||
- **OQ-7** Делать ли отдельный явный «сброс»-режим (teardown) частью скрипта или только
|
||
документированной процедурой в BUNDLED_SETUP §13.
|