From 4050ccbfdeb8116a649eb4c426963814b7ac5b17 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Thu, 11 Jun 2026 01:02:40 +0300 Subject: [PATCH] analyst(ET): auto-commit from analyst run_id=625 --- docs/work-items/ORCH-103/01-brd.md | 199 +++++++++++++++ docs/work-items/ORCH-103/02-trz.md | 232 ++++++++++++++++++ .../ORCH-103/03-acceptance-criteria.md | 164 +++++++++++++ docs/work-items/ORCH-103/04-test-plan.yaml | 102 ++++++++ 4 files changed, 697 insertions(+) create mode 100644 docs/work-items/ORCH-103/01-brd.md create mode 100644 docs/work-items/ORCH-103/02-trz.md create mode 100644 docs/work-items/ORCH-103/03-acceptance-criteria.md create mode 100644 docs/work-items/ORCH-103/04-test-plan.yaml diff --git a/docs/work-items/ORCH-103/01-brd.md b/docs/work-items/ORCH-103/01-brd.md new file mode 100644 index 0000000..c8c60f1 --- /dev/null +++ b/docs/work-items/ORCH-103/01-brd.md @@ -0,0 +1,199 @@ +--- +work_item: ORCH-103 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-11 +model_used: claude-opus-4-8 +--- + +# 01 — BRD: ORCH-103 — ORCH-10b Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт + +Work Item: **ORCH-103** · Repo: **orchestrator** (self-hosting) · Стадия: analysis +Заказчик: Слава · Эпик: **ORCH-10** (домен D5 «Масштаб», `docs/epics/self-evolution.md`) · Тип: **B — Bundled** + +--- + +## 1. Бизнес-контекст и проблема + +### 1.1. Цель эпика ORCH-10 +Тираж платформы — РАЗДАЧА текущей функциональности нескольким заказчикам **на тест**. +Решения Владельца 10.06 (приняты как требования): ДВА типа тиража, ОБА **stateless** +(наши задачи/данные/секреты НЕ переносим — чистый старт): + +- **Тип A (Lite, ORCH-102 ✅)** — переносится ТОЛЬКО орк+watchdog; Plane/Gitea/LLM/Telegram + заказчик донастраивает сам по инструкции `docs/deployment/LITE_SETUP.md`. +- **Тип B (Bundled, эта задача)** — **весь стек одним комплектом** + (орк + watchdog + Gitea + Plane + рантайм-обвязка агентов) — «под ключ». + +### 1.2. Проблема, которую закрывает ORCH-103 +Lite предполагает, что у заказчика **уже есть** (или он сам поднимет) свои Plane и Gitea. +Для заказчика без собственной инфраструктуры это барьер: Plane CE self-hosted — это ~14 +контейнеров со своей БД/брокером/хранилищем, Gitea — отдельная установка, и поверх всего — +первичная инициализация (админы, токены, workspace, 22 статуса, лейблы, вебхуки в обе стороны, +git-доступ агентов). Сегодня репо не содержит ни compose-описания этого стека, ни автоматизации +его доводки: разворачивание «с нуля до работающего конвейера» = многочасовая ручная работа по +сторонним докам с рисками дефолтных паролей и дрейфа от канона платформы. + +ORCH-103 должен дать: **один compose-комплект** всего стека + **bootstrap-скрипт**, доводящий +свежеподнятый стек до рабочего состояния одной командой/визардом, + **новые секреты** на каждую +инсталляцию + **инструкцию `docs/deployment/BUNDLED_SETUP.md`** с требованиями к хосту. + +### 1.3. Установленные факты (проверено по репо — не изобретать) +- **Корневой `docker-compose.yml` защищён анти-дрейфом:** ровно 3 сервиса + (`orchestrator`, `orchestrator-watchdog`, `orchestrator-staging` за `profiles: ["staging"]`); + `tests/test_lite_setup_doc.py` (TC-04) проверяет точное множество сервисов и **запрещает** + появление в нём имён/образов с подстроками `plane`/`gitea` → bundle-компоуз обязан быть + **отдельным файлом**, корневой compose не форкается и не расширяется. +- **Кирпичи уже в `main` (переиспользовать, не дублировать):** + - `scripts/gen_secrets.py` (ORCH-101) — криптослучайные webhook-секреты + (`ORCH_PLANE_WEBHOOK_SECRET`/`ORCH_GITEA_WEBHOOK_SECRET`), печать по умолчанию, + `--write` отказывает при существующем `.env`, `--force` — перезапись; exit 0/2. + - `scripts/onboard_project.py` (ORCH-009) — `plan` (GET-only) / `apply` (идемпотентный ensure, + без delete) / `verify`: Plane-проект + **22 статуса** (read-only импорт + `plane_sync._PLANE_NAME_TO_KEY`, fail-closed имена `Confirm Deploy`/`STOP`) + лейблы + `autoApprove`/`autoDeploy`/`Bug`; Gitea-репо + per-repo webhook (`push`/`pull_request`/`status`, + ОДИН глобальный `ORCH_GITEA_WEBHOOK_SECRET`); недоступное в Plane CE API → `manual-step` + (fail-safe); exit 0/2/1. + - `docs/operations/REPLICATION.md` (ORCH-101) — карта env (§2), чек-лист секретов (§3), + **smoke §4** (шаги 0–6 с PASS/FAIL: config-резолв → `/health` → `/queue`+`/metrics` → + onboard plan/apply/verify → тестовая задача → артефакты `01–04` → опц. до `done`); §1 — + таблица границ, где Type B помечен «отдельная задача». + - `docs/deployment/LITE_SETUP.md` (ORCH-102) — канон тиражной инструкции: 13 нормативных + разделов, каждый шаг = fenced-команда + явная «Проверка:» PASS/FAIL, хост-специфика только + плейсхолдерами; канон не форкается — общие шаги ссылками. + - `.env.example` — канон 100% ключей орка; `.env.watchdog.example` — канон watchdog + (key-set-sync тестом, D5 ORCH-102). +- **Хост-параметризация завершена (ORCH-101):** платформа разворачивается без правки кода — + только env (`${VAR:-default}`-интерполяция compose, `ARG APP_*` Dockerfile); анти-регресс + `tests/test_no_host_hardcodes.py` (FORBIDDEN-литералы: IP/`/home/slin`/`mva154`/`duckdns`). +- **Claude CLI НЕ запечён в образ орка:** монтируется с хоста + (`ORCH_HOST_CLAUDE_CODE_DIR`/`ORCH_HOST_NODE_BIN`/`ORCH_HOST_CLAUDE_DIR`/`ORCH_HOST_CLAUDE_JSON`). + «Агенты» в комплекте = рантайм-обвязка запуска; **инсталляция Claude CLI и LLM-ключ — внешнее + предусловие хоста заказчика** (как Lite §7), bundle их не содержит и не генерирует. +- **Нормативы тиражной Gitea:** branch protection на `main` НЕ включать (D10 ORCH-009 / INV-4 — + мерж только через Gitea PR-merge API); pre-receive не вводится. +- **Plane CE self-hosted ≈ 14 контейнеров** (web/admin/space/api/worker/beat/live/migrator + + postgres/redis/mq/minio/proxy) — ресурсоёмко; часть первичной инициализации в CE недоступна + по API → честные ручные чекпоинты (паттерн `manual-step` ORCH-009). + +--- + +## 2. Объём (scope) + +### 2.1. В объёме +- **Bundle-compose** — отдельный compose-комплект всего стека: орк + watchdog + Gitea + + Plane-стек (~14 контейнеров); пиннинг версий; чистые именованные тома; согласованная + сетевая достижимость (вебхуки в обе стороны). +- **Bootstrap-скрипт** — один запуск (команда/визард): поднять всё → дождаться + готовности/миграций → инициализация Gitea (админ/токен) → инициализация Plane + (instance/workspace/API-токен; CE-ограничения → явные manual-step чекпоинты) → + онбординг sandbox-проекта (22 статуса/3 лейбла/репо/вебхуки — через `onboard_project.py`) → + git-доступ агентов → сборка `.env`/`.env.watchdog` орка → health → smoke-подсказка. +- **Инициализация секретов** — генерация НОВЫХ на каждую инсталляцию (reuse `gen_secrets.py` + + bundle-внутренние креды: пароли БД/брокера/хранилища Plane, админ Gitea); дефолтных паролей + в репо нет. +- **`docs/deployment/BUNDLED_SETUP.md`** — инструкция запуска bundle по канону LITE_SETUP, + включая **требования к хосту (RAM/диск/CPU/порты)**. +- **Структурные анти-дрейф тесты** (без docker/сети/LLM в CI) + полный зелёный pytest + CHANGELOG. +- Отметка Type B в `docs/operations/REPLICATION.md` §1 (границы трёх задач эпика). + +### 2.2. Вне объёма (явно, не делать) +- Изменения рантайма: `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `.gitea/workflows/`, + `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict/схема БД — байт-в-байт. +- Перенос наших задач/данных/секретов (stateless — решение Владельца 10.06). +- Автоматическая установка Claude CLI / выдача LLM-ключей / создание Telegram-ботов — + внешние предусловия заказчика (документируются, не автоматизируются). +- HTTPS/домены/публичный reverse-proxy заказчика — за рамками bundle (документируется + как ручной шаг при необходимости). +- Процедура обновления (upgrade) развёрнутого bundle; миграция Lite→Bundled; кластерные/ + multi-host топологии; мультитенантность (D5.6) и горизонтальный воркер-пул (D5.4). +- Какая-либо активация bundle на НАШЕМ боевом хосте. + +--- + +## 3. Заинтересованные стороны +- **Владелец (Слава)** — раздаёт платформу заказчикам на тест; принимает результат. +- **Оператор заказчика** — целевой читатель BUNDLED_SETUP.md: чистый Linux-хост, + docker+compose, без знания внутренностей платформы. +- **Self-hosting прод** (`orchestrator`, общий для всех проектов) — не должен быть затронут: + задача — артефакты репо (compose/скрипт/доки/тесты), активируемые только явным запуском + на ЦЕЛЕВОМ хосте. + +--- + +## 4. Бизнес-требования (BR) + +| ID | Требование | Связь | +|----|------------|-------| +| BR-1 | Единый bundle-compose (отдельный файл) поднимает ВЕСЬ стек одной командой: орк, watchdog, Gitea, Plane-стек. Корневой `docker-compose.yml` не форкается и не меняется. | AC-1, AC-6, FR-1 | +| BR-2 | Bootstrap-скрипт ОДНИМ запуском (команда/визард) доводит свежеподнятый стек до рабочего состояния: готовность/миграции → init Gitea → init Plane → онбординг sandbox-проекта → git-доступ агентов → конфиг орка → health. Шаги, физически недоступные через Plane CE API, оформляются явными интерактивными manual-step чекпоинтами (fail-safe, паттерн ORCH-009) — без молчаливых пропусков. | AC-1, FR-2 | +| BR-3 | После bootstrap smoke проходит: тестовый проект создан, тестовая задача доезжает минимум до артефактов `01–04` в ветке (минимальный сигнал REPLICATION §4 шаг 5); расширенно — до `done`. Вебхуки работают в ОБЕ стороны (Plane→орк, Gitea→орк, орк→Plane/Gitea API). | AC-2, FR-2/FR-6 | +| BR-4 | Stateless: каждая инсталляция стартует с чистых томов/БД (Plane, Gitea, орк) и НОВЫХ секретов (`gen_secrets.py` + bundle-внутренние креды). Боевые данные/секреты не используются ни на одном шаге; в репо нет ни одного реального секрета/дефолтного пароля. | AC-3, FR-3 | +| BR-5 | `docs/deployment/BUNDLED_SETUP.md` написан по канону LITE_SETUP (fenced-команды + «Проверка:» PASS/FAIL, плейсхолдеры вместо хост-специфики, канон не форкается — общие шаги ссылками на LITE_SETUP/ONBOARDING/REPLICATION) и фиксирует требования к хосту: RAM/диск/CPU/занимаемые порты (Plane ~14 контейнеров — ресурсоёмко). | AC-4, FR-4 | +| BR-6 | Переиспользование кирпичей без дублирования: секреты — `gen_secrets.py`; статусы/лейблы/репо/вебхуки — `onboard_project.py` (22 статуса — из `plane_sync._PLANE_NAME_TO_KEY`, нулевой дрейф); smoke — шаги REPLICATION §4. Bootstrap не реализует собственную копию этих канонов. | FR-2/FR-3, AC-7 | +| BR-7 | Идемпотентность/fail-safe: повторный запуск bootstrap безопасен (ensure/skip, без delete-операций); запуск на «грязном» хосте (существующие тома/занятые порты/нехватка ресурсов) → явный отказ preflight с понятной подсказкой, а не молчаливое переиспользование чужого состояния. | FR-2, AC-8 | +| BR-8 | Наш прод не затрагивается: вся задача — вне рантайма и вне конвейера; kill-switch не требуется (активация — только явный запуск человеком на целевом хосте, паттерн ORCH-009). | NFR-1/NFR-2, AC-6 | +| BR-9 | Анти-дрейф: структурные тесты держат bundle-канон (compose-структура, док-канон, env-ключи, FORBIDDEN-литералы, секрет-эвристика, кросс-ссылки); существующие `test_lite_setup_doc.py`/`test_no_host_hardcodes.py` остаются зелёными; полный `pytest tests/ -q` зелёный; CHANGELOG обновлён. | AC-5, AC-6, AC-7, FR-5 | + +--- + +## 5. Нефункциональные требования (NFR) + +| ID | Требование | +|----|------------| +| NFR-1 | **Рантайм/конвейер байт-в-байт:** `src/**`, корневой `docker-compose.yml`, `Dockerfile`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, machine-verdict ключи, схема БД орка — не тронуты. Задача — docs+scripts+compose-bundle+tests. | +| NFR-2 | **Self-hosting безопасность:** ни один артефакт задачи не рестартит/не деплоит/не конфигурирует наш прод-контейнер; bundle-артефакты в нашем контуре инертны (никто их не исполняет). | +| NFR-3 | **Секрет-гигиена:** в репо не попадают реальные секреты, высокоэнтропийные литералы и хост-литералы (FORBIDDEN-скан `test_no_host_hardcodes.py` распространяется на новые артефакты); bootstrap не печатает секреты в лог; сгенерированные файлы конфигов — только на целевом хосте, в `.gitignore`. | +| NFR-4 | **Переносимость:** bundle не зависит от нашей инфраструктуры; вся хост-специфика — переменные/плейсхолдеры; целевая платформа — одиночный Linux x86_64 хост с docker+compose. | +| NFR-5 | **Норматив сопровождения** (зеркало NFR-5 ORCH-102): изменение шагов тиража в будущих задачах → обновление `BUNDLED_SETUP.md` в том же PR. | +| NFR-6 | **Воспроизводимость:** версии образов Gitea/Plane-стека зафиксированы (пиннинг тегов/digest, не `latest`); состав bundle детерминирован. | +| NFR-7 | **Без новых тяжёлых зависимостей:** bootstrap — в духе существующих скриптов (stdlib-инструментарий `gen_secrets.py`/`onboard_project.py`); точный стек (bash/python) — решение архитектора. | + +--- + +## 6. Допущения и ограничения +- Целевой хост: чистый одиночный Linux x86_64 с установленными docker + docker compose; + оператор имеет sudo. Прочие ОС — вне целевой платформы (best-effort). +- Ресурсы: Plane-стек ресурсоёмок; ориентир для проверки — **не менее 4 vCPU / 8 GB RAM / + 40 GB диска** (финальные минимумы УТОЧНЯЮТСЯ при реализации замером на тестовом + развёртывании и фиксируются в BUNDLED_SETUP.md — см. AC-4; цифры выше — гипотеза, не факт). +- Внешние предусловия заказчика (bundle не поставляет): инсталляция/аутентификация Claude CLI + + LLM-доступ Anthropic; Telegram-боты (трекер + watchdog) — опциональны, их отсутствие + деградирует только нотификации (never-raise), не конвейер. +- Часть инициализации Plane CE недоступна по API (instance-setup/workspace/API-токен) — + допускаются документированные интерактивные шаги внутри визарда; «одной командой» означает + «один запуск bootstrap с явными чекпоинтами», а не «ноль действий человека». +- Версии upstream-образов (Plane CE/Gitea) фиксируются на момент реализации; их обновление — + отдельные будущие задачи (NFR-6). + +--- + +## 7. Критерии успеха (резюме; детали — 03-acceptance-criteria.md) +Пять AC из постановки Владельца (сохранены 1:1 как AC-1…AC-5) + производные проверяемые: +- AC-1 единый bundle-compose поднимает ВЕСЬ стек; bootstrap доводит до рабочего состояния + одной командой/визардом. +- AC-2 после bootstrap smoke проходит (тестовый проект + задача доезжает). +- AC-3 stateless (чистые Plane/Gitea/БД, новые секреты). +- AC-4 BUNDLED_SETUP.md + требования к хосту (RAM/диск) задокументированы. +- AC-5 pytest зелёный; CHANGELOG. +- AC-6 корневой compose/рантайм не тронуты (анти-дрейф зелёный); AC-7 нулевой дрейф канонов + (22 статуса/лейблы/секреты — через существующие кирпичи); AC-8 идемпотентность/fail-safe + bootstrap; AC-9 секрет-гигиена новых артефактов. + +--- + +## 8. Риски (детали — 10-tech-risks.md, заполняет архитектор) +- **R-1 Ресурсоёмкость Plane:** ~14 контейнеров → OOM/медленный старт на слабом хосте; + смягчение — preflight-проверка ресурсов + честные требования в доке (AC-4). +- **R-2 Дыры Plane CE API:** первичная инициализация частично UI-only → ручные чекпоинты; + риск — UX «одной команды» размывается; смягчение — явные manual-step с проверкой результата + (паттерн ORCH-009), минимизация числа ручных шагов. +- **R-3 Дрейф upstream-образов:** «плавающие» теги ломают воспроизводимость → пиннинг (NFR-6). +- **R-4 Сетевая достижимость вебхуков:** орк (host network) ⟷ Plane/Gitea (bridge-сеть bundle) + — двунаправленные URL должны быть согласованы bootstrap'ом; ошибка = «задача не появилась» + (труднодиагностируемо); смягчение — smoke проверяет оба направления. +- **R-5 Соблазн форкнуть корневой compose** (анти-дрейф TC-04 `test_lite_setup_doc.py` упадёт) + → bundle строго отдельным файлом. +- **R-6 Утечка секретов в логи/репо** при генерации bundle-кред → секрет-эвристика в тестах, + запрет печати секретов (NFR-3). diff --git a/docs/work-items/ORCH-103/02-trz.md b/docs/work-items/ORCH-103/02-trz.md new file mode 100644 index 0000000..3adfbf5 --- /dev/null +++ b/docs/work-items/ORCH-103/02-trz.md @@ -0,0 +1,232 @@ +--- +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-.md` — решения архитектора (см. §9 OQ); + при сквозном значении — зеркало в `docs/architecture/adr/adr-NNNN-.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. diff --git a/docs/work-items/ORCH-103/03-acceptance-criteria.md b/docs/work-items/ORCH-103/03-acceptance-criteria.md new file mode 100644 index 0000000..e60c1c2 --- /dev/null +++ b/docs/work-items/ORCH-103/03-acceptance-criteria.md @@ -0,0 +1,164 @@ +--- +work_item: ORCH-103 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-11 +model_used: claude-opus-4-8 +--- + +# 03 — Критерии приёмки (Acceptance Criteria): ORCH-103 — Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт + +Work Item: **ORCH-103** · Repo: **orchestrator** · Стадия: analysis + +Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** +(что считается провалом). AC-1…AC-5 — из постановки Владельца (сохранены 1:1 по смыслу); +AC-6…AC-9 — производные обязательные. AC-1/AC-2/AC-3 в части e2e — **ручная приёмка** на +чистом тестовом хосте/VM по BUNDLED_SETUP.md (в CI docker/LLM не гоняются); остальное +проверяется по файлам репозитория и структурным тестам. + +--- + +## AC-1 — Единый bundle поднимает ВЕСЬ стек; bootstrap доводит одной командой/визардом + +**Условие:** на чистом Linux-хосте с docker+compose по шагам BUNDLED_SETUP.md: одна команда +`docker compose -f … up -d` поднимает все сервисы стека (орк + watchdog + +Gitea + Plane-стек); затем ОДИН запуск bootstrap-скрипта доводит инсталляцию до рабочего +состояния (init Gitea/Plane → онбординг sandbox-проекта → git-доступ агентов → конфиг орка → +health). Интерактивные manual-step чекпоинты допустимы только там, где Plane CE API не +позволяет автоматизацию, каждый — с инструкцией и проверкой результата. +- **PASS:** все контейнеры bundle в состоянии Up/healthy; bootstrap завершается `exit 0`; + `GET /health` орка — 200/ok, `GET /queue` и `GET /metrics` отдают валидный JSON; + `onboard_project.py verify` зелёный (22 статуса, лейблы, репо, webhook); ни одного + НЕдокументированного ручного действия (правка compose/конфигов руками сверх инструкции). +- **FAIL:** хотя бы один сервис не поднялся/в рестарт-цикле; bootstrap падает или завершается + с нерабочим конвейером; для доводки потребовались действия, отсутствующие в BUNDLED_SETUP.md; + manual-step пропускается молча без проверки результата. + +--- + +## AC-2 — После bootstrap smoke проходит (тестовый проект + задача доезжает) + +**Условие:** smoke-процедура BUNDLED_SETUP §smoke (шаги REPLICATION.md §4 поверх +bundle-инсталляции): создать issue в sandbox-проекте Plane → перевести в «To Analyse». +- **PASS:** webhook доезжает (job появляется в `GET /queue`); конвейер запускает analyst; + в рабочей ветке Gitea появляются артефакты `01-brd.md`/`02-trz.md`/`03-acceptance-criteria.md`/ + `04-test-plan.yaml` (минимальный сигнал — шаг 5 REPLICATION §4); обратное направление + работает (орк пишет статус/коммент в Plane). Опционально-расширенно: задача доводится до + `done` (шаг 6). +- **FAIL:** webhook не доходит (нет job); analyst не стартует; артефакты `01–04` не появляются; + орк не может писать в Plane/Gitea API (одностороння связность — R-4). + +--- + +## AC-3 — Stateless: чистые Plane/Gitea/БД, новые секреты + +**Условие:** инсталляция стартует с нуля и не содержит ничего нашего. +- **PASS:** все тома bundle созданы заново при первом `up` (чистые БД Plane/Gitea/орка: + `GET /queue` — нулевые счётчики, в Plane/Gitea нет наших задач/репо/пользователей); ВСЕ + секреты инсталляции сгенерированы на месте (`gen_secrets.py` + bundle-креды bootstrap); + в репо нет ни одного реального секрета/дефолтного пароля (структурный тест: секрет-эвристика + + плейсхолдеры в bundle-конфиг-каноне); боевые данные/секреты/БД не копируются ни одним шагом + инструкции. +- **FAIL:** инструкция/скрипт предлагает перенос наших данных или переиспользование боевых + секретов; в репо обнаружен реальный секрет/дефолтный пароль/высокоэнтропийный литерал; + на свежей инсталляции видны чужие задачи/счётчики. + +--- + +## AC-4 — BUNDLED_SETUP.md + требования к хосту задокументированы + +**Условие:** `docs/deployment/BUNDLED_SETUP.md` существует и написан по канону тиражных доков +(ORCH-102). +- **PASS:** док несёт обязательные разделы FR-4 (рамка, **требования к хосту с явными цифрами + RAM/диск/CPU и картой портов**, предусловия, секреты, запуск, bootstrap с перечнем + manual-step, LLM, Telegram, онбординг, smoke, stateless-проверка, остановка/сброс, + траблшутинг); каждый исполняемый шаг = fenced-команда + «Проверка:» PASS/FAIL; явно указано + «Plane ≈ 14 контейнеров — ресурсоёмко»; цифры требований подтверждены замером на тестовом + развёртывании (не «с потолка»); хост-специфика — только плейсхолдеры; общие шаги — ссылками + на LITE_SETUP/ONBOARDING/REPLICATION (без копипасты канона). +- **FAIL:** дока нет/раздел «Требования к хосту» отсутствует или без цифр; шаги без + команд/проверок; FORBIDDEN-литералы (IP/`/home/slin`/`mva154`/`duckdns`) или секреты в + тексте/fenced-блоках; канон LITE_SETUP скопирован вместо ссылок. + +--- + +## AC-5 — pytest зелёный; CHANGELOG + +**Условие:** полный регресс и журнал изменений. +- **PASS:** `pytest tests/ -q` — 0 failed (включая существующие анти-дрейф + `test_lite_setup_doc.py`, `test_no_host_hardcodes.py`, канон-тесты ORCH-009 — без правки их + ассертов); `CHANGELOG.md` содержит запись `ORCH-103`. +- **FAIL:** хотя бы один тест красный; существующий анти-дрейф тест «починен» ослаблением + ассертов; CHANGELOG не обновлён. + +--- + +## AC-6 — Корневой compose и рантайм не тронуты + +**Условие:** изоляция от боевого контура (NFR-1/NFR-2, BR-1/BR-8). +- **PASS:** `git diff main` НЕ содержит изменений `src/**`, корневого `docker-compose.yml`, + `Dockerfile`, `.gitea/workflows/**`; bundle-compose — отдельный файл; множество сервисов + корневого compose неизменно (`orchestrator`/`orchestrator-watchdog`/`orchestrator-staging`); + ни один артефакт задачи не исполняется в нашем контуре автоматически (нет правок + деплой-хука/CI, нет cron/врезок). +- **FAIL:** любая правка рантайма/корневого compose/Dockerfile; сервисы `plane*`/`gitea*` + добавлены в корневой compose; артефакт bundle задействован в нашем прод/staging-контуре. + +--- + +## AC-7 — Нулевой дрейф канонов: кирпичи переиспользованы + +**Условие:** BR-6 — единственный источник истины для статусов/лейблов/секретов/smoke. +- **PASS:** bootstrap вызывает `scripts/gen_secrets.py` (webhook-секреты) и + `scripts/onboard_project.py` (статусы/лейблы/репо/вебхуки) — структурный тест подтверждает + ссылки; собственного списка статусов/лейблов в bundle-артефактах нет (упоминание числа + статусов в доке сверяется импортом `plane_sync._PLANE_NAME_TO_KEY` в тесте, не литералом); + smoke-раздел ссылается на REPLICATION §4. +- **FAIL:** bootstrap/док несут собственную копию канона (свой список статусов, свой генератор + webhook-секретов, свой smoke-чеклист с нуля) — дрейф при будущих изменениях канона. + +--- + +## AC-8 — Идемпотентность и fail-safe bootstrap + +**Условие:** BR-7 — повторный запуск и грязный хост. +- **PASS:** повторный запуск bootstrap на уже-инициализированном bundle завершается успешно + (ensure/skip, без дублей и без разрушения состояния); preflight на грязном/непригодном хосте + (существующие тома bundle, занятый порт, нехватка RAM/диска) → явный отказ с понятной + подсказкой ДО любых мутаций; delete-операций нет (teardown — только отдельный + явный режим/процедура, не часть обычного прогона); exit-коды: 0 — успех, 2 — manual-step/ + предусловие, 1 — ошибка; секреты в логи не печатаются; повторный запуск не перетирает + существующие секреты без явного флага. +- **FAIL:** повторный запуск ломает/дублирует состояние; bootstrap молча переиспользует чужие + тома или продолжает после провального preflight; обычный прогон удаляет данные; секрет виден + в stdout/логе. + +--- + +## AC-9 — Секрет-гигиена и переносимость новых артефактов + +**Условие:** NFR-3/NFR-4/NFR-6 по файлам репо. +- **PASS:** структурные тесты подтверждают: в bundle-compose/доке/скрипте нет + FORBIDDEN-литералов (список — импорт из `test_no_host_hardcodes.py`) и высокоэнтропийных + литералов (hex ≥32 / alnum ≥40); все сторонние образы bundle-compose пиннованы (не `latest`); + все env-ключи, упомянутые в BUNDLED_SETUP.md, существуют в канонах (`.env.example` ∪ + bundle-конфиг-канон); сгенерированные на хосте конфиги — в `.gitignore`. +- **FAIL:** найден хост-литерал/секрет; образ без пина; ключ-фантом в доке (нет в канонах); + сгенерированный конфиг коммитится. + +--- + +## Сводная матрица AC ↔ BR/FR + +| AC | Покрывает | Способ проверки | +|----|-----------|-----------------| +| AC-1 | BR-1, BR-2 / FR-1, FR-2 | ручной e2e на тестовом хосте + структурные тесты (TC-01..04, TC-08) | +| AC-2 | BR-3 / FR-2, FR-6 | ручной e2e (smoke REPLICATION §4) | +| AC-3 | BR-4 / FR-3 | ручной e2e + структурные тесты (TC-06, TC-09) | +| AC-4 | BR-5 / FR-4 | структурный тест дока (TC-05) + ревью | +| AC-5 | BR-9 / FR-5, FR-6 | `pytest tests/ -q` (TC-12) + CHANGELOG (TC-11) | +| AC-6 | BR-1, BR-8 / NFR-1, NFR-2 | git diff + существующий анти-дрейф (TC-02) | +| AC-7 | BR-6 / FR-2, FR-3 | структурный тест bootstrap/дока (TC-07, TC-10) | +| AC-8 | BR-7 / FR-2 | unit-тесты чистых функций preflight/плана (TC-08) + ручной повторный прогон | +| AC-9 | NFR-3, NFR-4, NFR-6 / FR-1, FR-4 | структурные тесты гигиены (TC-03, TC-06, TC-09) | diff --git a/docs/work-items/ORCH-103/04-test-plan.yaml b/docs/work-items/ORCH-103/04-test-plan.yaml new file mode 100644 index 0000000..2c8d7a1 --- /dev/null +++ b/docs/work-items/ORCH-103/04-test-plan.yaml @@ -0,0 +1,102 @@ +work_item: ORCH-103 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-11 +model_used: claude-opus-4-8 +title: "Bundled-тираж: bundle-compose + bootstrap + BUNDLED_SETUP.md (структурные анти-дрейф тесты)" +framework: pytest +scope: > + Автоматическое покрытие — СТРУКТУРНЫЕ инварианты артефактов Bundled-тиража + (bundle-compose, bootstrap-скрипт, docs/deployment/BUNDLED_SETUP.md) без docker/сети/LLM + в CI (паттерн tests/test_lite_setup_doc.py). Вне автоматического покрытия — фактический + e2e-подъём стека: он принимается ВРУЧНУЮ по чек-листу BUNDLED_SETUP §smoke + (шаги REPLICATION.md §4) на чистом тестовом хосте/VM — см. notes (AC-1/AC-2/AC-3/AC-8). +notes: > + Ручная приёмка (вне CI): чистый Linux-хост/VM -> docker compose -f up -d -> + один запуск bootstrap (manual-step чекпоинты Plane CE допустимы и проверяются) -> + health/queue/metrics зелёные -> onboard verify -> тестовая задача доезжает до артефактов + 01-04 (минимальный сигнал), опционально до done; повторный запуск bootstrap безопасен; + тома чистые, секреты новые. Имена модулей tests/test_bundle_compose.py / + tests/test_bundled_setup_doc.py / tests/test_bootstrap_script.py — норматив тест-плана; + имена bundle-каталога/скрипта внутри ассертов следуют ADR-001 архитектора. + Полный регресс tests/ обязан остаться зелёным БЕЗ ослабления ассертов существующих + анти-дрейф тестов (test_lite_setup_doc.py, test_no_host_hardcodes.py, канон ORCH-009). + +tests: + # ---------- FR-1 / AC-1: bundle-compose ---------- + - id: TC-01 + type: unit + description: "Bundle-compose существует и валидно парсится (yaml.safe_load); содержит обязательные сервисы: orchestrator, orchestrator-watchdog, Gitea и Plane-стек (имена — по ADR-001); staging-контур орка не входит в дефолтный up" + module: tests/test_bundle_compose.py + expected: PASS + + - id: TC-02 + type: unit + description: "Корневой docker-compose.yml НЕ изменён: множество сервисов == {orchestrator, orchestrator-watchdog, orchestrator-staging}; в его сервисах/образах/container_name нет подстрок plane/gitea (зеркало TC-04 test_lite_setup_doc.py — существующий анти-дрейф остаётся зелёным)" + module: tests/test_bundle_compose.py + expected: PASS + + - id: TC-03 + type: unit + description: "Все сторонние образы bundle-compose пиннованы: ни одного image с тегом latest или без тега/digest (NFR-6, воспроизводимость)" + module: tests/test_bundle_compose.py + expected: PASS + + - id: TC-04 + type: unit + description: "Изоляция и конфиг-канон bundle: тома — именованные с узнаваемым bundle-префиксом, без bind-путей нашего прод-контура; bundle-конфиг-канон (example-файл) существует, и каждая ${VAR}-интерполяция bundle-compose имеет ключ в каноне (key-set-sync, паттерн .env.watchdog.example)" + module: tests/test_bundle_compose.py + expected: PASS + + # ---------- FR-4 / AC-4: BUNDLED_SETUP.md ---------- + - id: TC-05 + type: unit + description: "docs/deployment/BUNDLED_SETUP.md существует и несёт обязательные разделы FR-4 (включая 'Требования к хосту' с цифрами RAM/диск/CPU, картой портов и упоминанием ~14 контейнеров Plane; bootstrap; smoke; stateless-проверка; остановка/сброс; траблшутинг); исполняемые шаги оформлены fenced-блоками с явной 'Проверка:'" + module: tests/test_bundled_setup_doc.py + expected: PASS + + - id: TC-06 + type: unit + description: "Гигиена новых артефактов (док + bundle-compose + bootstrap): нет FORBIDDEN-литералов (список — импорт из tests/test_no_host_hardcodes.py) и нет высокоэнтропийных секрет-литералов (hex >=32 / alnum >=40, эвристика D8 ORCH-102)" + module: tests/test_bundled_setup_doc.py + expected: PASS + + # ---------- FR-2/FR-3 / AC-7: bootstrap переиспользует кирпичи ---------- + - id: TC-07 + type: unit + description: "Bootstrap-скрипт существует и структурно переиспользует канон: ссылается на scripts/gen_secrets.py и scripts/onboard_project.py; НЕ несёт собственного списка Plane-статусов/лейблов; в обычном прогоне нет delete-операций (docker volume rm / rm -rf допустимы только в отдельном явном reset-режиме, если введён ADR)" + module: tests/test_bootstrap_script.py + expected: PASS + + - id: TC-08 + type: unit + description: "Чистые функции bootstrap (preflight/план шагов): грязное состояние (существующие bundle-тома, занятый порт, нехватка RAM/диска) -> отказ с диагностикой ДО мутаций; чистое -> план полного прогона; контракт exit-кодов 0/2/1 (успех / manual-step-остановка / ошибка)" + module: tests/test_bootstrap_script.py + expected: PASS + + # ---------- FR-4/FR-5 / AC-9: env-канон и нулевой дрейф ---------- + - id: TC-09 + type: unit + description: "Каждый env-ключ ORCH_*/WATCHDOG_*, упомянутый в BUNDLED_SETUP.md, существует в .env.example либо в bundle-конфиг-каноне (нет ключей-фантомов); упоминание ЧИСЛА статусов Plane сверяется импортом len(plane_sync._PLANE_NAME_TO_KEY), а не зашитым литералом" + module: tests/test_bundled_setup_doc.py + expected: PASS + + - id: TC-10 + type: unit + description: "Кросс-ссылки канона: BUNDLED_SETUP.md ссылается на LITE_SETUP.md, ONBOARDING.md и REPLICATION.md (канон не форкается); docs/operations/REPLICATION.md §1 несёт отметку Type B -> BUNDLED_SETUP.md / ORCH-103" + module: tests/test_bundled_setup_doc.py + expected: PASS + + # ---------- AC-5: журнал и полный регресс ---------- + - id: TC-11 + type: unit + description: "CHANGELOG.md содержит запись ORCH-103" + module: tests/test_bundled_setup_doc.py + expected: PASS + + - id: TC-12 + type: integration + description: "Полный регресс pytest tests/ -q зелёный: новые тесты добавлены, существующие анти-дрейф (test_lite_setup_doc.py, test_no_host_hardcodes.py, канон-тесты ORCH-009) проходят БЕЗ изменения их ассертов" + module: tests/ + expected: PASS