From eea3553d64bab3d0e8c9962056bfd4a540c217da Mon Sep 17 00:00:00 2001 From: claude-bot Date: Thu, 11 Jun 2026 01:23:18 +0300 Subject: [PATCH] architect(ET): auto-commit from architect run_id=626 --- docs/architecture/README.md | 32 ++ .../adr/adr-0038-bundled-replication-canon.md | 114 ++++++ ...001-bundled-stack-compose-and-bootstrap.md | 362 ++++++++++++++++++ .../ORCH-103/07-infra-requirements.md | 73 ++++ docs/work-items/ORCH-103/10-tech-risks.md | 42 ++ 5 files changed, 623 insertions(+) create mode 100644 docs/architecture/adr/adr-0038-bundled-replication-canon.md create mode 100644 docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md create mode 100644 docs/work-items/ORCH-103/07-infra-requirements.md create mode 100644 docs/work-items/ORCH-103/10-tech-risks.md diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 7212b1c..ba7915e 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -210,6 +210,38 @@ 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`. +**Type B — Bundled (ORCH-103 — design).** Закрывает эпик ORCH-10: весь стек одним комплектом +(орк + watchdog + Gitea + Plane CE ≈13–14 контейнеров) для заказчика без собственной +инфраструктуры. Новый top-level каталог **`deploy/`** (исполняемые дистрибутивы; дополняет +`docs/deployment/` — инструкции): `deploy/bundled/docker-compose.yml` — один самодостаточный +compose с `name: orchestrator-bundle` (узнаваемый префикс томов/контейнеров; `container_name` +не пиннится — нет коллизий с корневым compose на одном хосте), пиннинг сторонних образов +неподвижными тегами литералом (не `latest`); корневой compose не форкается (заморожен +анти-дрейфом ORCH-102); staging-контур орка в bundle отсутствует, репо `orchestrator` не +регистрируется → self-deploy-машинерия структурно спит (`SELF_HOSTING_REPO`-леафы не матчатся). +Сеть — одна bridge: машинный трафик строго сервис-DNS (webhooks в обе стороны, API, /metrics), +наружу — только человеческие порты (Plane 8080 / Gitea 3000 / орк 8500; явный +`GITEA__webhook__ALLOWED_HOST_LIST=orchestrator` против дефолтного запрета приватных таргетов). +Конфиг-слои: `deploy/bundled/.env.example` (канон bundle-инфры, key-set-sync тест) → live +`deploy/bundled/.env` (авто-чтение compose из project dir, без `--env-file`-футгана); runtime +орка/watchdog — корневые `.env`/`.env.watchdog` ровно по канону Lite (`env_file: required: +false` до сборки); **единственный писатель live-файлов — bootstrap**. +`scripts/bootstrap_bundle.py` (python stdlib-only, `plan`-дефолт/`apply`/`verify`, step-движок +check→ensure, exit 0/2/1): preflight fail-fast до мутаций → секреты (`gen_secrets.py` + +stdlib-креды стека, в логи не печатаются) → up+ожидание готовности → init Gitea (полностью +автоматом через CLI; branch protection НЕ включать — D10 ORCH-009) → init Plane CE (честные +manual-step: инструкция → подтверждение → API-верификация результата) → онбординг +sandbox-проекта строго `onboard_project.py apply`/`verify` (host-venv, канон ONBOARDING) → +git-доступ агентов token-remote (`_push_url`-паттерн; ssh-контур не вводится) → сборка env +орка → health/итог; delete-операций в скрипте нет — teardown только документированной +процедурой (§13). Golden source — `docs/deployment/BUNDLED_SETUP.md` (14 разделов по канону +LITE_SETUP, требования к хосту по замеру тестового развёртывания; REPLICATION §1 — отметка +Type B). Анти-дрейф — `tests/test_bundle_compose.py` / `test_bundled_setup_doc.py` / +`test_bootstrap_script.py`. Рантайм/конвейер — байт-в-байт; kill-switch не нужен (активация — +только явный запуск оператора на целевом хосте, паттерн ORCH-009). Подробнее: +[adr-0038](adr/adr-0038-bundled-replication-canon.md), детально — +`docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md`. + ## Конвейер и Quality Gates ``` diff --git a/docs/architecture/adr/adr-0038-bundled-replication-canon.md b/docs/architecture/adr/adr-0038-bundled-replication-canon.md new file mode 100644 index 0000000..76a3494 --- /dev/null +++ b/docs/architecture/adr/adr-0038-bundled-replication-canon.md @@ -0,0 +1,114 @@ +--- +work_item: ORCH-103 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-11 +model_used: claude-opus-4-8 +--- + +# adr-0038: Канон Bundled-тиража — `deploy/bundled/` + bootstrap + `BUNDLED_SETUP.md` (ORCH-103, 10b) + +## Статус +Proposed + +## Контекст + +Эпик ORCH-10 (D5 «Масштаб»), тип **B — Bundled**: заказчик без собственной инфраструктуры +получает **весь стек одним комплектом** (орк + watchdog + Gitea + Plane CE ≈13–14 контейнеров) и +bootstrap, доводящий его до рабочего конвейера одним запуском. Фундамент готов: 10-common +(ORCH-101, adr-0036 — хост-параметризация/секреты/smoke) и Lite (ORCH-102, adr-0037 — док-канон +`docs/deployment/`). Корневой `docker-compose.yml` заморожен анти-дрейфом ORCH-102 (ровно 3 +сервиса, запрет подстрок `plane`/`gitea`) → комплект обязан жить отдельным файлом. + +Сквозной характер: вводится новый top-level каталог `deploy/` (дистрибутивы развёртывания), +новый канонический env-example и нормативы, обязательные для будущих задач эпика ORCH-10 и +любого агента, меняющего шаги тиража. Детальный пакет решений (D1…D11, исходы OQ-1…OQ-7 ТЗ) — +work-item ADR: `docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md`. + +## Решение + +1. **Новый top-level каталог `deploy/` — исполняемые дистрибутивы развёртывания** (дополняет + `docs/deployment/` — инструкции). Bundled-комплект: **`deploy/bundled/docker-compose.yml`** — + один самодостаточный compose всего стека с top-level `name: orchestrator-bundle` (project + name = узнаваемый префикс томов/контейнеров; `container_name` не пиннится — нет коллизий с + корневым compose на одном хосте). Staging-контур орка в bundle **отсутствует вовсе**; репо + `orchestrator` в bundle-инсталляции не регистрируется → self-deploy-машинерия структурно спит + (`SELF_HOSTING_REPO`-леафы не матчатся). +2. **Конфиг-слои:** `deploy/bundled/.env.example` — канон bundle-инфры (committed, плейсхолдеры; + key-set-sync тест: каждая `${VAR}`-интерполяция bundle-compose имеет ключ в каноне) → live + `deploy/bundled/.env` (авто-чтение compose из project dir — без `--env-file`-футгана; покрыт + неякорным `.env` в `.gitignore`); runtime орка/watchdog — **корневые `.env`/`.env.watchdog` + ровно по канону Lite** (REPLICATION §2 применим 1:1), в bundle-compose — `env_file: + required: false` (первый `up` жив до сборки конфига). **Bootstrap — единственный писатель** + всех трёх live-файлов (когерентность дублируемых ключей — механическая). Один факт = одно имя + (ORCH-101 D1): существующие факты — существующие `ORCH_*`-имена; bundle-only — `BUNDLE_*`; + внутренние креды Plane — upstream-имена. +3. **Состав/пиннинг:** Plane CE — зеркало официального selfhost-référence (upstream-имена + сервисов/env); Gitea — `gitea/gitea` (не rootless). Пиннинг — **точный неподвижный тег + литералом** (не `latest`, не интерполяция; digest не требуется); точные теги фиксирует + developer по проверенному стенду; форму держит структурный тест. +4. **Сеть:** одна именованная bridge-сеть; машинный трафик — строго сервис-DNS + (`http://orchestrator:8500/webhook/*`, `http://gitea:3000`, plane-proxy); `network_mode: host` + в bundle не используется (ssh-деплой-пути неактивны: `ORCH_DEPLOY_SSH_HOST` пуст). Наружу — + только человеческие порты (Plane proxy 8080 / Gitea 3000 / орк 8500; конфигурируемы); + БД/брокер/minio не публикуются. Публичные URL — от `BUNDLE_PUBLIC_HOST` (split internal/public + уже в конфиге орка). Мина Gitea закрывается явно: `GITEA__webhook__ALLOWED_HOST_LIST=orchestrator`. +5. **Bootstrap `scripts/bootstrap_bundle.py`:** python stdlib-only, без импортов из `src/**`; + режимы `plan` (дефолт, ноль мутаций) / `apply` / `verify`; step-движок check→ensure + (идемпотентность, resume = повторный запуск); exit `0/2/1`. Preflight fail-fast до мутаций + (docker/порты/чистота томов по префиксу/RAM/диск; Claude CLI — warning). **Кирпичи не + дублируются:** секреты — субпроцесс `gen_secrets.py`; статусы/лейблы/репо/вебхуки — строго + `onboard_project.py apply`+`verify` (host-venv, канон ONBOARDING). Init Gitea — полностью + автоматом (CLI в контейнере; branch protection НЕ настраивается — D10 ORCH-009/adr-0037 п.4); + init Plane CE — честные **manual-step чекпоинты** (инструкция → подтверждение → + API-верификация; прогрессивная автоматизация разрешена без смены контракта). Git-доступ + агентов — HTTP token-remote (паттерн `_push_url`); ssh-контур не вводится. Секреты в + логи не печатаются; delete-операций в скрипте нет вообще — teardown только документированной + процедурой (`BUNDLED_SETUP` §13). +6. **Док-канон:** `docs/deployment/BUNDLED_SETUP.md` — 14 нормативных разделов по форме + LITE_SETUP (fenced-команда + «Проверка:» PASS/FAIL, плейсхолдеры, общие шаги ссылками на + LITE_SETUP/ONBOARDING/REPLICATION — канон не форкается), включая «Требования к хосту» с + цифрами **по замеру** тестового развёртывания. REPLICATION §1: Type B → ✅ ORCH-103. + **Норматив сопровождения:** изменил шаги Bundled-тиража → обнови BUNDLED_SETUP.md в том же PR. +7. **Анти-дрейф — постоянная CI-гарантия:** `tests/test_bundle_compose.py` / + `test_bundled_setup_doc.py` / `test_bootstrap_script.py` (структурные, без docker/сети/LLM: + состав сервисов, заморозка корневого compose, пины, key-set-sync, разделы дока, FORBIDDEN — + импортом из `test_no_host_hardcodes.py`, секрет-эвристика, ссылки на кирпичи, отсутствие + delete-операций, unit чистых функций preflight/плана, exit-контракт). + +### Что НЕ меняется +`src/**`, корневой `docker-compose.yml`, `Dockerfile`, `.gitea/workflows/**`, `onboarding/**`, +промпты `.openclaw/agents/**`; `STAGE_TRANSITIONS`, состав `QG_CHECKS`, семантика `check_*`, +machine-verdict ключи, схема БД — байт-в-байт. Kill-switch не вводится (активация — только явный +запуск оператора на целевом хосте, паттерн ORCH-009). Прод-контейнер в рамках задачи не +рестартуется; наши данные/секреты не переносятся (stateless, решение Владельца 10.06). + +## Альтернативы +- **Расширение корневого compose (профиль `bundled`)** — отвергнуто: заморожен анти-дрейфом + ORCH-102/нормативом «compose не форкается»; смешение дистрибутива с боевым контуром. +- **Include-композиция / live-env через `--env-file`** — отвергнуто: лишние степени свободы + запуска, молчаливые дефолты при забытом флаге. +- **Орк в bundle на host-network + `host-gateway`** — отвергнуто: хост-сеть нужна была + ssh-деплой-контуру нашего хоста, который в bundle спит; bridge даёт чистые двунаправленные + сервис-DNS-URL. +- **Digest-пиннинг / rootless-Gitea / ssh-доступ агентов / bash-bootstrap / reset-режим + скрипта** — отвергнуты (см. work-item ADR-001, «Альтернативы»). + +## Последствия +- Эпик ORCH-10 закрыт по обоим типам: A (Lite, инструкция) + B (Bundled, комплект); заказчик + без инфраструктуры разворачивает конвейер «под ключ». +- Цена: пиннованные версии Plane/Gitea стареют (апгрейд — отдельные задачи); manual-step Plane CE + размывают «одну команду» — неустранимо честно (нет API), митигировано контрактом чекпоинта; + двойной `.env`-слой — под единственным писателем-bootstrap и key-sync тестом. +- Откат: удалить `deploy/`, bootstrap, BUNDLED_SETUP.md, три тест-модуля, строку REPLICATION §1 — + состояние 1:1 (docs+scripts+tests, без миграций). + +## Связи +adr-0036 (ORCH-101 — фундамент 10-common: параметризация, gen_secrets, REPLICATION/smoke), +adr-0037 (ORCH-102 — док-канон `docs/deployment/`, compose-подмножество, запрет branch +protection), adr-0035 (ORCH-009 — onboarding-CLI: 22 статуса, manual-step паттерн, `_push_url`, +D10), adr-0027/INV-4 (merge-актор — основание норматива Gitea), adr-0001 +(`SELF_HOSTING_REPO`-конвенция — почему self-гейты в bundle спят). Детально — +`docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md`, +`07-infra-requirements.md`, `10-tech-risks.md`. diff --git a/docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md b/docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md new file mode 100644 index 0000000..3894cac --- /dev/null +++ b/docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md @@ -0,0 +1,362 @@ +--- +work_item: ORCH-103 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-11 +model_used: claude-opus-4-8 +--- + +# ADR-001: Bundled-тираж (Type B) — bundle-compose всего стека + bootstrap-канон + +Work Item: **ORCH-103** — ORCH-10b Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт +Стадия: **architecture** +Сквозная регистрация: **`docs/architecture/adr/adr-0038-bundled-replication-canon.md`** +(закрывает Type B эпика ORCH-10; вводит новый top-level каталог `deploy/` и нормативы, +обязательные для будущих задач тиража). + +## Статус +Proposed + +## Контекст + +Эпик ORCH-10 (D5 «Масштаб»), тип **B — Bundled**: заказчик без собственной инфраструктуры +получает **весь стек одним комплектом** (орк + watchdog + Gitea + Plane CE) + bootstrap, +доводящий стек до рабочего конвейера одним запуском. Факты, сверенные с репо: + +- **Корневой `docker-compose.yml` заморожен анти-дрейфом** (`tests/test_lite_setup_doc.py:: + test_compose_services_are_exactly_the_lite_set` + `test_compose_has_no_plane_or_gitea_services`): + ровно 3 сервиса, подстроки `plane`/`gitea` запрещены → bundle обязан быть **отдельным файлом**. +- **Кирпичи уже в `main`:** `scripts/gen_secrets.py` (webhook-секреты, `--write PATH`, exit 0/2), + `scripts/onboard_project.py` (`plan`/`apply`/`verify`, 22 статуса из + `plane_sync._PLANE_NAME_TO_KEY`, шаг `plane.workspace-webhook` — уже **MANUAL** в его отчёте, + token-push `_push_url`, exit 0/2/1; запуск — host-venv, `docs/operations/ONBOARDING.md`), + smoke `docs/operations/REPLICATION.md` §4, док-канон `docs/deployment/LITE_SETUP.md` (ORCH-102). +- **Хост-параметризация закрыта ORCH-101** (adr-0036): `src/**` без хост-литералов + (`tests/test_no_host_hardcodes.py`, FORBIDDEN = `82.22.50.71`/`/home/slin`/`mva154`/`duckdns`); + internal/public split URL уже в конфиге (`ORCH_GITEA_URL`≠`ORCH_GITEA_PUBLIC_URL`, + `ORCH_PLANE_API_URL`≠`ORCH_PLANE_WEB_URL`). +- **`.gitignore` уже неякорный** для `.env`, `data/`, `.env.watchdog` — вложенные копии этих имён + игнорируются на любом уровне без правок. +- **Claude CLI не запекается** в образ (маунты `ORCH_HOST_CLAUDE_*`) — внешнее предусловие хоста + заказчика; bundle его не поставляет (решение Владельца, BRD §1.3). + +ТЗ (02-trz.md §9) оставило архитектору OQ-1…OQ-7. Ниже — пакет решений D1…D11. + +## Решение + +### Сводка + +Bundled-комплект живёт в новом top-level каталоге **`deploy/bundled/`**: самодостаточный +compose-файл всего стека (зеркало официального Plane CE selfhost-référence + Gitea + орк + +watchdog, пиннинг неподвижными тегами) на **одной bridge-сети** с сервис-DNS для машинного +трафика и публикацией только «человеческих» портов. **`scripts/bootstrap_bundle.py`** +(python stdlib, режимы `plan`/`apply`/`verify`, step-движок check→ensure, exit 0/2/1) доводит +стек: preflight → секреты → up → init Gitea (полностью автоматом) → init Plane (честные +manual-step с верификацией) → онбординг sandbox-проекта **строго** через `onboard_project.py` → +git-доступ агентов token-remote → сборка `.env`/`.env.watchdog` орка → health/итог. Teardown — +только документированная процедура. Рантайм байт-в-байт: `src/**`, корневой compose, +`Dockerfile`, `STAGE_TRANSITIONS`/`QG_CHECKS`/machine-verdict/схема БД — не тронуты (NFR-1); +kill-switch не нужен — активация только явным запуском оператора на целевом хосте +(паттерн ORCH-009/102). + +### D1 — Расположение и изоляция: `deploy/bundled/`, project name `orchestrator-bundle` (OQ-1) + +- Новый top-level каталог **`deploy/`** — «дистрибутивы развёртывания» (исполняемые комплекты; + семантика дополняет `docs/deployment/` — инструкции). Bundle: **`deploy/bundled/docker-compose.yml`**, + один **самодостаточный** файл (include-композиция отвергнута — см. Альтернативы). +- Top-level **`name: orchestrator-bundle`** в compose: project name фиксирован → все тома/контейнеры + получают узнаваемый префикс `orchestrator-bundle_*`/`orchestrator-bundle-*` (требование TC-04 + тест-плана «узнаваемый bundle-префикс»; preflight bootstrap детектирует «грязный хост» по этому + префиксу). **`container_name` не пиннится ни у одного сервиса** — установка Lite и bundle на одном + хосте не сталкивается по именам с корневым compose (у которого `container_name: orchestrator` + закреплён). +- **Staging-контур орка в bundle отсутствует вовсе** (ни сервисом, ни профилем): заказчик Type B + эксплуатирует платформу для СВОИХ проектов, а не развивает её self-hosting'ом; репо `orchestrator` + в bundle-инсталляции **не регистрируется** как проект → вся self-deploy-машинерия + (`SELF_HOSTING_REPO="orchestrator"`-леафы, freshness, serial-gate freeze) структурно спит. + Нужен self-hosting у заказчика → это маршрут Lite/корневого compose (LITE_SETUP §9), не bundle. +- Имена сервисов: `orchestrator`, `orchestrator-watchdog` — платформенные конвенции (ORCH-101 D3); + `gitea`; Plane-стек — **upstream-имена** сервисов (минимальный дифф к référence, D3 ниже). + Запрет `plane*`/`gitea*` касается ТОЛЬКО корневого compose — на bundle-файл не распространяется. + +### D2 — Конфиг-слои: три файла, один писатель (OQ-1, FR-1/FR-3) + +| Файл | Роль | В гите | +|------|------|--------| +| `deploy/bundled/.env.example` | **bundle-конфиг-канон**: 100% ключей bundle-инфры (публичный хост, карта портов, uid/gid, пути Claude CLI, плейсхолдеры внутренних кред Plane/Gitea) | да (только плейсхолдеры/нейтральные дефолты) | +| `deploy/bundled/.env` | live bundle-конфиг; **авто-читается compose** из project dir — все `docker compose -f deploy/bundled/docker-compose.yml …` работают без флагов | нет (покрыт неякорным `.env` в `.gitignore`) | +| корневые `.env` / `.env.watchdog` | runtime-конфиг орка и watchdog — **ровно канон Lite** (REPLICATION §2 / `.env.example` / `.env.watchdog.example` применимы 1:1); в bundle-compose подключаются `env_file: ../../.env` (`required: false` — см. ниже) | нет (уже в `.gitignore`) | + +- **Отвергнут** отдельный live-файл с обязательным `--env-file`: оператор неизбежно наберёт голую + compose-команду без флага → интерполяции молча упадут в дефолты → пересоздание контейнеров с + чужими портами/путями (труднодиагностируемый класс R-4). Авто-`.env` в project dir — fail-safe + по построению. +- **`env_file` орка/watchdog — `required: false`** (паттерн уже в корневом compose у watchdog): + первый `up -d` поднимает ВЕСЬ стек до того, как конфиг орка собран (AC-1 «одна команда»); орк + без конфига жив (`/health` отвечает), bootstrap пересоздаёт его после сборки env (шаг 8 D5). +- **Bootstrap — единственный писатель** `deploy/bundled/.env` (дозапись сгенерённых кред), + корневого `.env` и `.env.watchdog` на целевом хосте: дублируемые между слоями ключи + (`ORCH_AGENT_HOME_DIR`, порт орка) когерентны механически, а не дисциплиной оператора. + Права `600`; повторный запуск НЕ перетирает существующие значения без явного флага + (паттерн `--force` `gen_secrets.py`). +- **Неймспейсы ключей:** один факт = одно имя (ORCH-101 D1) — существующие факты переиспользуют + существующие имена (`ORCH_RUN_UID/GID`, `ORCH_DOCKER_GID`, `ORCH_AGENT_HOME_DIR`, + `ORCH_HOST_CLAUDE_CODE_DIR`/`ORCH_HOST_NODE_BIN`/`ORCH_HOST_CLAUDE_DIR`/`ORCH_HOST_CLAUDE_JSON`); + bundle-only факты — префикс **`BUNDLE_*`** (`BUNDLE_PUBLIC_HOST`, `BUNDLE_PLANE_PORT`, + `BUNDLE_GITEA_HTTP_PORT`, `BUNDLE_ORCH_PORT`); внутренние креды Plane-стека — **upstream-имена** + Plane CE (значения генерирует bootstrap). Точный состав финализирует developer; форму держит + key-set-sync тест: **каждая `${VAR}`-интерполяция bundle-compose имеет ключ в + `deploy/bundled/.env.example`** (паттерн `.env.watchdog.example`, D5 ORCH-102). +- **Дефолты bundle-compose нейтральны** (FORBIDDEN-литералов нет — TC-06 распространяет скан на + bundle-артефакты): HOME акторов в bundle — `/home/orchestrator` (значение в `.env.example` + bundle, обе стороны группы ORCH-040 двигаются одной переменной — инвариант сохранён); + uid/gid/доки docker-gid заполняет bootstrap из `id -u`/`id -g`/`getent group docker`. +- Каталоги данных орка — bind внутри project dir: `./data` → `deploy/bundled/data` (покрыт + неякорным `data/` в `.gitignore`), `./repos` → `deploy/bundled/repos` (**добавить в + `.gitignore`**: `deploy/bundled/repos/`); bind, а не named volume — те же uid-причины, что в + корневом compose (ORCH-040: named volume создаётся root-owned, контейнер бежит под uid + оператора). Состояние Plane/Gitea (postgres/redis/mq/minio/gitea-data) — **именованные тома** + проекта (root-владение для них нормально: процессы своих образов). + +### D3 — Состав стека и пиннинг: зеркало upstream, неподвижные теги литералом (OQ-2) + +- **Plane CE** — зеркало официального selfhost-compose Plane CE (web/space/admin/api/worker/ + beat-worker/migrator/live + postgres/redis/mq/minio/proxy, ≈13–14 сервисов по факту référence + на момент пиннинга). Структура сервисов/env-контракт — upstream-имена (анти-дрейф к их докам; + своя «переписанная» топология Plane = неоплачиваемый долг сопровождения). +- **Gitea** — официальный `gitea/gitea` (НЕ rootless: rootless усложняет ssh/тома, а ssh-контур + и так не вводится — D8). +- **Пиннинг: точный неподвижный тег литералом в compose** (`image: :`), не `latest`, + не плавающий мажор, не `${VERSION}`-интерполяция (версия — не операторская ручка; её смена = + осознанная правка bundle под тестом). Digest-пиннинг **не требуется**: тег + анти-дрейф формы + (TC-03: ни одного `:latest`/безтегового образа) достаточны для NFR-6, digest нечитаем и + затрудняет осознанный апгрейд. +- **Точные теги фиксирует developer при реализации по фактически проверенному стенду** (ADR + сознательно не выдумывает номера версий — ложная точность хуже честной отсылки к référence); + обновление версий после ORCH-103 — отдельные задачи (BRD §6). +- Healthchecks: у инфра-сервисов (postgres/redis/minio/gitea) — стандартные; у Plane-сервисов — + что даёт upstream; недостающее добирает poll-ожидание bootstrap (D5). + +### D4 — Сеть: одна bridge, сервис-DNS внутрь, публикация только человеческих портов (OQ-5) + +- **Вся инсталляция — в одной именованной bridge-сети** compose-проекта. `network_mode: host` + в bundle **не используется** ни для кого: он был нужен нашему контуру ради ssh-деплоя в + 127.0.0.1 (ORCH-036/058) — в bundle эти пути структурно неактивны (`ORCH_DEPLOY_SSH_HOST` + пуст → freshness/self-deploy/build-cache-pruner no-op по построению). +- **Машинный трафик — строго сервис-DNS:** Plane→орк webhook `http://orchestrator:8500/webhook/plane`, + Gitea→орк `http://orchestrator:8500/webhook/gitea`, орк→Plane `ORCH_PLANE_API_URL=http://`, + орк→Gitea `ORCH_GITEA_URL=http://gitea:3000`, watchdog→орк + `WATCHDOG_METRICS_URL=http://orchestrator:8500/metrics`. Никаких `host-gateway`/`extra_hosts`. +- **Наружу публикуются только человеческие точки** (карта конфигурируема в bundle-каноне, + дефолты): Plane proxy → `${BUNDLE_PLANE_PORT:-8080}`, Gitea web → `${BUNDLE_GITEA_HTTP_PORT:-3000}`, + орк API → `${BUNDLE_ORCH_PORT:-8500}` (операторский smoke `curl /health`). **Postgres/redis/ + mq/minio наружу НЕ публикуются** (секрет-гигиена/поверхность атаки). +- **Публичные URL** (браузер оператора, ссылки в Plane-комментариях/Telegram) строятся от + **`BUNDLE_PUBLIC_HOST`** (дефолт `localhost`): `ORCH_GITEA_PUBLIC_URL=http://$BUNDLE_PUBLIC_HOST:3000`, + `ORCH_PLANE_WEB_URL=http://$BUNDLE_PUBLIC_HOST:8080`, WEB_URL Plane, ROOT_URL Gitea. Split + internal/public уже поддержан конфигом орка (ORCH-101) — новых ключей `src/**` не требуется. +- **Мина Gitea закрывается явно:** Gitea по умолчанию запрещает webhook'и в приватные адреса — + bundle задаёт `GITEA__webhook__ALLOWED_HOST_LIST=orchestrator` (env-конфиг образа Gitea), иначе + R-4 «задача не появилась» гарантирован. Smoke (FR-6) проверяет оба направления. +- HTTPS/домены/reverse-proxy заказчика — вне bundle (BRD §2.2); `BUNDLE_PUBLIC_HOST` + + документированный ручной шаг при необходимости. + +### D5 — Bootstrap: `scripts/bootstrap_bundle.py`, python stdlib, `plan`/`apply`/`verify` (OQ-4) + +- **Язык — python stdlib-only** (NFR-7; паттерн `gen_secrets.py`: работает на голом python3 + целевого хоста ДО `docker compose up`; bash отвергнут — 9-шаговый stateful-визард с + таймаутами/JSON/чистыми функциями под unit-тесты на bash не тестируем структурно). + Никаких импортов из `src/**` (bootstrap бежит вне venv платформы; канон-знания — только + субпроцессами кирпичей, см. ниже). +- **Режимы (паттерн ORCH-009):** `plan` — **дефолт**, ноль мутаций (печать плана + read-only + preflight-диагностика); `apply` — полный прогон; `verify` — read-only пост-проверка + (health/queue/metrics + `onboard_project.py verify` субпроцессом). Exit-коды: `0` успех / + `2` остановка на manual-step или незавершённое предусловие / `1` ошибка (контракт TRZ FR-2). +- **Step-движок check→ensure:** каждый шаг = `check()` (выполнено?) → skip | `ensure()` + (доводка). Повторный `apply` на инициализированном bundle = каскад skip (AC-8); + «resume» после manual-step = просто повторный запуск. Чистые функции (preflight-вердикт, + сборка плана, рендер env-файлов) выделены для unit-тестов (TC-08). +- **Последовательность apply (норматив TRZ FR-2, механика):** + 1. **Preflight (fail-fast, до мутаций):** docker+compose есть; `deploy/bundled/.env` существует + и обязательные ключи заполнены (пути Claude CLI — существуют на хосте, иначе warning-блок: + стек поднимется, конвейер без LLM не поедет); целевые порты свободны; томов/контейнеров с + префиксом `orchestrator-bundle` нет (иначе — явный «уже инициализирован, продолжаю в + ensure-режиме» либо отказ при противоречивом состоянии); RAM/диск ≥ минимумов из + BUNDLED_SETUP (пороги — константы скрипта, синхронизированы с доком); python3+venv доступны. + 2. **Секреты (FR-3):** webhook-секреты — **субпроцессом `scripts/gen_secrets.py --write `** + (не реализуются заново, AC-7); bundle-внутренние (пароли postgres/redis/mq/minio, + SECRET_KEY Plane, админ-пароль Gitea) — stdlib `secrets`; запись в `deploy/bundled/.env` + + корневой `.env`; существующие значения не перетираются без `--force-secrets`; значения + в stdout/лог **не печатаются** (только имена ключей). + 3. **Up + готовность:** `docker compose -f deploy/bundled/docker-compose.yml up -d` + (идемпотентен по построению — оба прочтения AC-1 истинны: оператор мог выполнить up сам); + ожидание готовности poll-циклами с таймаутами (health контейнеров, `migrator` завершился + `exit 0`, HTTP-пробы Plane/Gitea); по таймауту — диагностика «какой сервис не дождались + + хвост его логов». + 4. **Init Gitea — полностью автоматом** (D6). + 5. **Init Plane — manual-step чекпоинты** (D7). + 6. **Онбординг sandbox-проекта — строго `onboard_project.py apply` + `verify`** (D7). + 7. **Git-доступ агентов** (D8) + клон sandbox-репо в `deploy/bundled/repos/`. + 8. **Конфиг орка:** сборка корневого `.env` (база — канон `.env.example`: URL'ы D4, токены, + секреты, `ORCH_PROJECTS_JSON` из вывода onboard; `ORCH_DEPLOY_SSH_HOST=` пусто — + деплой-машинерия спит) и `.env.watchdog` (база — `.env.watchdog.example`; Telegram-ключи + опциональны — пусто = деградация только нотификаций); пересоздание + `up -d orchestrator orchestrator-watchdog` для подхвата env. + 9. **Health + итог:** `GET /health`, `/queue`, `/metrics`; финальная сводная таблица PASS/FAIL + по шагам; следующая команда оператора — smoke BUNDLED_SETUP §smoke (REPLICATION §4). +- **Контракт manual-step (fail-safe, BR-2):** печать точной инструкции (URL/что нажать/что + ввести) → ожидание подтверждения (интерактивно при TTY; без TTY — немедленный `exit 2` с той + же инструкцией) → **верификация результата API-пробой** (например, валидность введённого + `ORCH_PLANE_API_TOKEN` запросом к workspace) → только затем продолжение. Молчаливый пропуск + запрещён. +- **Запретов в скрипте нет:** delete-операций (`docker volume rm`/`rm -rf`/`down -v`) — **ноль** + (teardown — D9); боевых литералов FORBIDDEN — ноль (TC-06); печати секретов — ноль (NFR-3); + наш прод недостижим по построению (скрипт говорит только с локальным docker целевого хоста). + +### D6 — Init Gitea: полностью автоматизирован, branch protection НЕ настраивается (OQ-3-часть) + +- Административная учётка — **официальный CLI в контейнере**: + `docker compose … exec gitea gitea admin user create --admin …` (idempotent: предсуществование + пользователя → skip); API-токен — `gitea admin user generate-access-token` (или REST под basic + auth — равнозначно, выбирает developer по фактической версии Gitea) → `ORCH_GITEA_TOKEN`. +- **Один пользователь-бот** — владелец (`ORCH_GITEA_OWNER`) sandbox-репо и носитель токена для + API орка и token-remote агентов (D8). Отдельная россыпь пользователей на тестовый bundle — + неоправданная сложность (зафиксировано как осознанный компромисс в 10-tech-risks TR-7). +- **Branch protection на `main` НЕ включается; pre-receive не вводится** — норматив D10 ORCH-009 / + adr-0037 п.4 (ломают PR-merge API merge-актора, INV-4); bundle-Gitea конфигурируется тем же + правилом, BUNDLED_SETUP фиксирует его явно (ссылкой на LITE_SETUP §6, не копией). +- `GITEA__webhook__ALLOWED_HOST_LIST=orchestrator` — см. D4. + +### D7 — Init Plane: честные manual-step + онбординг строго кирпичом (OQ-3) + +- **Не автоматизируется (CE):** instance-setup/первый админ, создание workspace + (`ORCH_PLANE_WORKSPACE_SLUG`), выпуск `ORCH_PLANE_API_TOKEN` — три **manual-step чекпоинта** + контракта D5 (инструкция → подтверждение → API-верификация). **Прогрессивная автоматизация + разрешена:** если на момент реализации у конкретной пиннованной версии Plane CE обнаружится + стабильный API/seed-механизм для шага — developer вправе заменить manual-step на ensure **без + изменения контракта чекпоинта** (верификация результата остаётся той же) и без правки ADR. +- **Онбординг sandbox-проекта — ТОЛЬКО субпроцессом** `python3 scripts/onboard_project.py apply` + + `verify` из **host-venv чекаута** (канон запуска — ONBOARDING.md: venv с `requirements.txt`; + создание venv — ensure-шаг bootstrap). Env для субпроцесса (URL'ы/токены D4) bootstrap передаёт + через окружение процесса (pydantic env-переменные перекрывают `env_file`) — корневой `.env` к + этому моменту уже собран либо передаётся фрагментом. Собственная реализация статусов/лейблов/ + webhook в bootstrap **запрещена** (BR-6/AC-7; 22 статуса остаются за + `plane_sync._PLANE_NAME_TO_KEY`). +- **Workspace-webhook Plane** (Plane→орк) — остаётся manual-step **самого onboard-CLI** + (его шаг `plane.workspace-webhook` уже MANUAL — CE не даёт API); bootstrap лишь подставляет + правильный in-network URL `http://orchestrator:8500/webhook/plane` и секрет-имя в инструкцию + и верифицирует доставку на smoke (FR-6). +- `--webhook-url` для Gitea per-repo hook — `http://orchestrator:8500/webhook/gitea` (D4). + +### D8 — Git-доступ агентов: HTTP token-remote; ssh-контур не вводится (OQ-6) + +- Клон sandbox-репо в `deploy/bundled/repos/` с remote-URL вида + `http://@gitea:3000//.git` — **паттерн уже в каноне** + (`onboard_project.py::_push_url` делает initial push именно так); агенты наследуют origin + чекаута (push/fetch из контейнера — bridge-DNS, D4). +- **Ssh-контур в bundle не вводится:** ssh-порт Gitea не публикуется, маунт `ORCH_HOST_SSH_DIR` + в bundle-compose отсутствует (это нашему контуру нужен ssh к хосту/Gitea; в bundle — + лишняя поверхность: генерация ключей, known_hosts, регистрация в Gitea). +- Компромисс «токен в `.git/config` plaintext» зафиксирован честно: каталог `deploy/bundled/repos` + — локальный, права на токен-носители `600`, токен — бот-юзера одной тестовой инсталляции; + риск/митигейшн — 10-tech-risks TR-7. Git-идентичность агентов — существующие + `ORCH_AGENT_GIT_NAME`/`ORCH_GIT_EMAIL_DOMAIN` (дефолты годятся). + +### D9 — Teardown: только документированная процедура, не режим скрипта (OQ-7) + +`BUNDLED_SETUP.md` §13 «Остановка и полный сброс»: `docker compose -f … down` (остановка) / +`down -v` + удаление сгенерённых конфигов и `deploy/bundled/{data,repos}` (полный сброс) — с +явным предупреждением о необратимости. **Reset-режим в bootstrap отвергнут:** одна опечатка +флага = снос томов; ценность против fenced-команды — нулевая; зато скрипт получает структурную +гарантию «delete-операций НЕТ вообще» (упрощение TC-07 и ревью). + +### D10 — Док-канон: `docs/deployment/BUNDLED_SETUP.md`, 14 разделов, ссылки вместо форка (FR-4) + +Форма — канон LITE_SETUP (adr-0037 D2): нормативные разделы в порядке маршрута оператора, +каждый исполняемый шаг = fenced-команда + «Проверка:» PASS/FAIL, хост-специфика — только +плейсхолдеры. **14 разделов** (норматив; точные заголовки — за developer'ом, проверяемость — +структурный тест): (1) рамка Bundled (включая «что НЕ входит»: Claude CLI, Telegram, HTTPS; +границы vs Lite); (2) **требования к хосту** (RAM/диск/CPU **по замеру тестового развёртывания**, +карта портов D4, явное «Plane ≈ 14 контейнеров — ресурсоёмко»); (3) предусловия; +(4) получение кода; (5) секреты; (6) запуск bundle-compose; (7) bootstrap (+ перечень +manual-step Plane); (8) LLM — ссылкой на LITE_SETUP §7; (9) Telegram — ссылкой на LITE_SETUP §8; +(10) онбординг следующих проектов — ссылкой на ONBOARDING.md; (11) smoke — шаги REPLICATION §4; +(12) stateless-проверка; (13) остановка/полный сброс (D9); (14) траблшутинг (минимум: webhook +не доходит — включая `ALLOWED_HOST_LIST`, OOM/нехватка RAM, порт занят, claude не найден, +Plane-миграции не завершились). Fail-closed имена `Confirm Deploy`/`STOP` и «22 статуса» — +сверкой импорта в тесте, не литералом. `docs/operations/REPLICATION.md` §1: строка Type B → +✅ ORCH-103 + ссылка. **Норматив сопровождения (NFR-5):** изменил шаги Bundled-тиража → обнови +BUNDLED_SETUP.md в том же PR. + +### D11 — Анти-дрейф: три структурных тест-модуля (FR-5; без docker/сети/LLM) + +По тест-плану `04-test-plan.yaml` (имена модулей — норматив): `tests/test_bundle_compose.py` +(TC-01…04: yaml.safe_load, обязательные сервисы, заморозка корневого compose зеркалом +существующего ассерта, пины образов, префикс томов, key-set-sync `${VAR}` ⊆ +`deploy/bundled/.env.example`), `tests/test_bundled_setup_doc.py` (TC-05/06/09/10/11: разделы +D10, FORBIDDEN — **импорт** из `test_no_host_hardcodes.py`, секрет-эвристика hex≥32/alnum≥40 — +паттерн D8 ORCH-102, env-ключи ⊆ канонов, число статусов — импортом `plane_sync`, кросс-рефы, +CHANGELOG), `tests/test_bootstrap_script.py` (TC-07/08: ссылки на кирпичи, отсутствие +delete-операций и собственного списка статусов, unit чистых функций preflight/плана/рендера, +контракт exit 0/2/1). Существующие анти-дрейф тесты остаются зелёными **без правки их ассертов** +(AC-5/AC-6). + +## Альтернативы + +- **Расширить корневой `docker-compose.yml` (профиль `bundled`)** — отвергнуто: заморожен + анти-дрейфом ORCH-102 (TC-04) и нормативом adr-0037 п.2 «compose не форкается»; смешение + боевого контура с дистрибутивом = групповой риск self-hosting. +- **Include-композиция (`include:`/несколько `-f`)** — отвергнуто: многофайловость = новые + степени свободы запуска (забытый `-f` молча меняет состав), сложнее структурный тест; один + самодостаточный файл проще и детерминированнее (NFR-6). +- **Live env через `--env-file deploy/bundled/.env.bundled`** — отвергнуто: footgun голой + compose-команды без флага (молчаливые дефолты) — см. D2. +- **Орк в bundle под `network_mode: host` + `host-gateway` для webhook'ов** — отвергнуто: + хост-сеть нужна была нашему ssh-деплой-контуру, который в bundle спит; bridge даёт чистые + стабильные сервис-DNS-URL обоих направлений и нулевые порт-конфликты (D4). +- **Digest-пиннинг образов** — отвергнуто: нечитаем, усложняет осознанный апгрейд; неподвижный + тег + тест формы достаточны для NFR-6 (D3). +- **Ssh-доступ агентов к bundle-Gitea** — отвергнуто: три лишних механизма (ключи, known_hosts, + регистрация) против уже существующего token-remote-паттерна onboard (D8). +- **Bash-bootstrap** — отвергнуто: нет unit-тестируемых чистых функций (TC-08), JSON/поллинг/ + стейт-машина шагов на bash хрупки (D5). +- **Reset-режим bootstrap** — отвергнуто: риск-поверхность против нулевой ценности (D9). +- **Переписать Plane-стек «по-своему» (свои имена сервисов/env)** — отвергнуто: дрейф от + upstream-доков, неоплачиваемое сопровождение (D3). + +## Последствия + +- **+** Эпик ORCH-10 закрывается по типу B: заказчик без инфраструктуры получает конвейер + «под ключ» одной командой + одним bootstrap-прогоном с честными чекпоинтами. +- **+** Нулевой дрейф канонов: статусы/лейблы/секреты/smoke/док-форма — переиспользованы + (gen_secrets, onboard_project, REPLICATION §4, форма LITE_SETUP); рантайм байт-в-байт. +- **+** Наш прод недостижим по построению: артефакты инертны в нашем контуре, kill-switch не + нужен (паттерн ORCH-009); все существующие анти-дрейф тесты остаются зелёными. +- **−** Новая поверхность сопровождения: пиннованные версии Plane/Gitea стареют (апгрейд — + отдельные задачи, NFR-6); двойной `.env`-слой (bundle-инфра vs runtime орка) требует + дисциплины «писатель — bootstrap» (митигировано D2: один писатель + key-sync тест). +- **−** Manual-step Plane CE размывают UX «одной команды» — неустранимо честно (CE без API + первичной инициализации); митигировано контрактом чекпоинта (инструкция+верификация) и + прогрессивной автоматизацией (D7). +- **−** Токен в remote-URL агентских чекаутов — осознанный компромисс тестовой инсталляции + (TR-7; права 600, непубликуемые порты БД, один бот-юзер). +- **Откат:** удалить `deploy/`, `scripts/bootstrap_bundle.py`, `docs/deployment/BUNDLED_SETUP.md`, + три тест-модуля, строку REPLICATION §1 и записи CHANGELOG/CLAUDE.md/README — состояние репо 1:1 + (docs+scripts+tests, без миграций); на целевых хостах — процедура §13 (D9). + +## Ссылки + +- BRD: `docs/work-items/ORCH-103/01-brd.md` +- TRZ: `docs/work-items/ORCH-103/02-trz.md` (OQ-1…OQ-7 → D1…D9) +- Acceptance: `docs/work-items/ORCH-103/03-acceptance-criteria.md`; тест-план: `04-test-plan.yaml` +- Сквозной ADR: `docs/architecture/adr/adr-0038-bundled-replication-canon.md` +- Предшественники: adr-0035 (ORCH-009 onboarding: D10 branch-protection, manual-step, `_push_url`), + adr-0036 (ORCH-101 10-common: параметризация/«дефолт=боевое»/gen_secrets/REPLICATION), + adr-0037 (ORCH-102 Lite: док-канон/`.env.watchdog.example`/compose-подмножество) +- Сверено по коду/репо: `tests/test_lite_setup_doc.py` (заморозка корневого compose, FORBIDDEN-импорт, + секрет-эвристика), `tests/test_no_host_hardcodes.py` (`FORBIDDEN`), `scripts/gen_secrets.py` + (`--write PATH`, exit 0/2), `scripts/onboard_project.py` (закрытый src-импорт-лист, MANUAL + `plane.workspace-webhook`, `_push_url`, exit 0/2/1), `docs/operations/ONBOARDING.md` (host-venv), + `docker-compose.yml` (паттерны `${VAR:-default}`, `env_file required:false`, группа ORCH-040), + `.gitignore` (неякорные `.env`/`data/`/`.env.watchdog`) diff --git a/docs/work-items/ORCH-103/07-infra-requirements.md b/docs/work-items/ORCH-103/07-infra-requirements.md new file mode 100644 index 0000000..fce993d --- /dev/null +++ b/docs/work-items/ORCH-103/07-infra-requirements.md @@ -0,0 +1,73 @@ +--- +work_item: ORCH-103 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-11 +model_used: claude-opus-4-8 +--- + +# 07 — Инфра-требования: ORCH-103 — Bundled-тираж: весь стек одним комплектом + bootstrap + +Work Item: **ORCH-103** · Repo: **orchestrator** · Стадия: architecture + +> Вся инфраструктура этой задачи — **ЦЕЛЕВОЙ хост заказчика** (и одноразовый тестовый хост/VM +> приёмки). Инфраструктура НАШЕГО прод-контура (mva154) не затрагивается ни одним пунктом: +> артефакты bundle в нашем контуре инертны (NFR-2, паттерн ORCH-009). + +## I-1. Топология / окружения + +**Наш контур: N/A** (корневой `docker-compose.yml`, прод 8500, staging 8501 — байт-в-байт). + +**Целевой хост bundle (нормативно, ADR-001 D1/D3/D4):** +- Один Linux x86_64 хост, docker + docker compose v2, sudo у оператора. Compose-проект + **`orchestrator-bundle`** (`deploy/bundled/docker-compose.yml`), одна именованная bridge-сеть. +- Состав: `orchestrator` (build из корневого `Dockerfile`), `orchestrator-watchdog` + (build из `watchdog/Dockerfile`), `gitea` (пиннованный `gitea/gitea`), Plane CE-стек — + зеркало upstream selfhost-référence (≈13–14 сервисов: web/space/admin/api/worker/beat-worker/ + migrator/live + postgres/redis/mq/minio/proxy; точные теги пиннит developer по проверенному + стенду). Staging-контур орка отсутствует. +- **Карта портов (дефолты; конфигурируемы в `deploy/bundled/.env.example`):** + `${BUNDLE_ORCH_PORT:-8500}` — API орка (smoke/health), `${BUNDLE_PLANE_PORT:-8080}` — Plane + proxy (UI), `${BUNDLE_GITEA_HTTP_PORT:-3000}` — Gitea web. Postgres/redis/mq/minio/ssh-Gitea — + **наружу не публикуются**. Машинный трафик (webhooks в обе стороны, API, git, /metrics) — + внутрисетевой сервис-DNS. +- **Хранилище:** состояние Plane/Gitea — именованные тома `orchestrator-bundle_*`; данные орка — + bind `deploy/bundled/data`; репозитории агентов — bind `deploy/bundled/repos` (владелец — + uid оператора = `ORCH_RUN_UID`, инвариант ORCH-040). +- **Ресурсы (предусловие, гипотеза BRD §6 — финальные цифры по замеру на приёмке, AC-4):** + ориентир ≥ 4 vCPU / 8 GB RAM / 40 GB диска; preflight bootstrap проверяет и отказывает до + любых мутаций (BR-7). + +## I-2. Переменные окружения / секреты + +- **Новый канон:** `deploy/bundled/.env.example` (bundle-инфра: `BUNDLE_PUBLIC_HOST`, карта + портов, реюз `ORCH_RUN_UID/GID`/`ORCH_DOCKER_GID`/`ORCH_AGENT_HOME_DIR`/`ORCH_HOST_CLAUDE_*`, + плейсхолдеры внутренних кред Plane/Gitea по upstream-именам). Live-файлы только на целевом + хосте, права 600: `deploy/bundled/.env`, корневые `.env`/`.env.watchdog` (каноны Lite 1:1). +- **Корневой `.env.example` НЕ меняется** (bundle не вводит новых ключей `Settings`); в + `.gitignore` добавляется `deploy/bundled/repos/` (остальные live-файлы уже покрыты неякорными + `.env`/`data/`/`.env.watchdog`). +- **Секреты (FR-3):** webhook-секреты — `gen_secrets.py`; внутренние креды стека (postgres/ + redis/mq/minio/SECRET_KEY Plane, админ Gitea) — stdlib `secrets` в bootstrap; внешние + предусловия заказчика — Claude CLI/Anthropic-доступ (обязателен для конвейера), + Telegram-токены (опциональны). В репо и логах bootstrap секретов нет (NFR-3, тест-эвристика). + +## I-3. Деплой / рестарт + +- **Наш прод: рестарт НЕ требуется и НЕ выполняется.** Задача — docs+scripts+compose+tests; + мерж в `main` ничего не активирует в нашем контуре (никто не исполняет bundle-артефакты; + kill-switch не нужен — паттерн ORCH-009). Self-hosting инвариант соблюдён по построению. +- На целевом хосте пересоздание контейнеров орка/watchdog после сборки env — штатный шаг + bootstrap (D5 шаг 8); к нашему проду отношения не имеет. + +## I-4. CI/CD + +- `.gitea/workflows/**` — **без изменений**; три новых структурных тест-модуля подхватываются + существующим `pytest tests/ -q` (без docker/сети/LLM — CI-безопасны, TC-12). + +## I-5. Разовое предусловие приёмки (человек) + +Чистый тестовый Linux-хост/VM (ресурсы I-1) для ручной приёмки AC-1/AC-2/AC-3/AC-8 по +`BUNDLED_SETUP.md` + замер фактических минимумов RAM/диск/CPU для §2 дока (AC-4: цифры «не с +потолка»). На нашем боевом хосте bundle не запускается ни в каком виде (BRD §2.2). diff --git a/docs/work-items/ORCH-103/10-tech-risks.md b/docs/work-items/ORCH-103/10-tech-risks.md new file mode 100644 index 0000000..5c522f7 --- /dev/null +++ b/docs/work-items/ORCH-103/10-tech-risks.md @@ -0,0 +1,42 @@ +--- +work_item: ORCH-103 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-11 +model_used: claude-opus-4-8 +--- + +# 10 — Технические риски: ORCH-103 — Bundled-тираж: весь стек одним комплектом + bootstrap + +Work Item: **ORCH-103** · Repo: **orchestrator** · Стадия: architecture + +> Информационный (гейтом не парсится). Риски реализации и эксплуатации bundle; решения — +> `06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md` (D1…D11). + +## Реестр рисков + +| ID | Риск | Вер. | Влия. | Митигейшн | +|----|------|------|-------|-----------| +| TR-1 | **Ресурсоёмкость Plane** (≈13–14 контейнеров): OOM/вечные миграции на слабом хосте → «bundle не работает» | Сред. | Выс. | Preflight bootstrap (RAM/диск/CPU до мутаций, BR-7); честные цифры в BUNDLED_SETUP §2 **по замеру** (AC-4); таймауты ожидания готовности с диагностикой «кто не дождался + хвост логов» (D5 ш.3); траблшутинг §14 | +| TR-2 | **Дыры Plane CE API**: instance-setup/workspace/API-токен UI-only → UX «одной команды» размывается; молчаливый пропуск шага ломает всё дальше | Выс. | Сред. | Контракт manual-step (инструкция → подтверждение → **API-верификация результата**, exit 2 без TTY; D5/D7); число ручных шагов минимизировано (Gitea — полностью автоматом, D6); прогрессивная автоматизация разрешена без смены контракта | +| TR-3 | **Дрейф upstream-образов**: «плавающий» тег ломает воспроизводимость; пиннованные версии стареют (CVE/несовместимость) | Сред. | Сред. | Неподвижные теги литералом + тест формы TC-03 (не `latest`/безтеговых); теги фиксируются по фактически проверенному стенду (D3); апгрейд — отдельные задачи (NFR-6, BRD §6) | +| TR-4 | **Сетевая недостижимость вебхуков** (труднодиагностируемое «задача не появилась»): приватные адреса, рассинхрон URL | Сред. | Выс. | Одна bridge-сеть + строго сервис-DNS для машинного трафика (D4); **явный `GITEA__webhook__ALLOWED_HOST_LIST=orchestrator`** (Gitea по умолчанию режет приватные таргеты); URL подставляет bootstrap, не оператор; smoke проверяет ОБА направления (FR-6); траблшутинг §14 | +| TR-5 | **Соблазн форкнуть/расширить корневой compose** (упадёт анти-дрейф ORCH-102 TC-04) | Низ. | Выс. | Bundle строго отдельным файлом `deploy/bundled/` (D1); TC-02 зеркалит заморозку корневого compose; правило эскалации TRZ §7 — если всплывёт незакрытая параметризация `src/**`, остановиться и вернуть задачу, не «дотачивать молча» | +| TR-6 | **Утечка секретов** в репо/логи при генерации bundle-кред | Низ. | Выс. | В гите — только плейсхолдеры (канон D2); bootstrap не печатает значения (имена ключей/пути, D5); права 600; секрет-эвристика hex≥32/alnum≥40 + FORBIDDEN-скан на новых артефактах (TC-06); live-файлы в `.gitignore` | +| TR-7 | **Токен-remote агентов**: токен бот-юзера plaintext в `.git/config` чекаутов; один бот = широкие права | Сред. | Низ. | Осознанный компромисс тестовой инсталляции (D8): порты БД/брокера не публикуются, каталог локальный, права 600; ssh-контур сознательно не вводится (меньше поверхность); зафиксировано в BUNDLED_SETUP §1 «рамка» | +| TR-8 | **Путаница двух `.env`-слоёв** (bundle-инфра `deploy/bundled/.env` vs runtime орка корневой `.env`): ручная правка не того файла | Сред. | Сред. | Bootstrap — **единственный писатель** всех live-файлов (D2); авто-чтение compose из project dir (нет `--env-file`-футгана); шапки-комментарии в канонах перекрёстно ссылаются; key-set-sync тест TC-04 | +| TR-9 | **Хост-python/venv для onboard**: `onboard_project.py` требует venv с `requirements.txt` (канон ONBOARDING) — на голом хосте шаг 6 падает | Сред. | Сред. | Preflight проверяет python3/venv (D5 ш.1); создание venv — идемпотентный ensure-шаг bootstrap; сам bootstrap stdlib-only и от venv не зависит (D5) | +| TR-10 | **Повторный запуск/грязный хост**: bootstrap портит чужое состояние или дублирует своё | Низ. | Выс. | Step-движок check→ensure (skip-семантика, AC-8); детект «грязи» по префиксу `orchestrator-bundle` до мутаций; delete-операций в скрипте нет вообще — teardown только документированной процедурой §13 (D9); unit-тесты чистых функций preflight (TC-08) | + +## Сводный вывод + +Доминирующий класс — **эксплуатационные риски целевого хоста** (TR-1/TR-2/TR-4): они не +затрагивают наш прод и гасятся честным preflight, контрактом manual-step и smoke в обе стороны. +Рисков для прод-конвейера self-hosting **нет по построению** (NFR-1/NFR-2: рантайм байт-в-байт, +артефакты в нашем контуре инертны, kill-switch не требуется — паттерн ORCH-009; все существующие +анти-дрейф тесты остаются зелёными). Эскалация `arch:major-change` не требуется: новых стадий/ +компонентов рантайма/смены БД нет — задача целиком в слое дистрибуции (паттерн ORCH-101/102). +Возврат в анализ не требуется: ТЗ выполнимо без нарушения принципов; единственный заранее +оговорённый стоп-кран — TR-5 (обнаружение незакрытой параметризации `src/**` ⇒ остановка по +TRZ §7). Остаточный риск — **низкий**.