37 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-103 | architecture | architect | proposed | 2026-06-11 | 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), smokedocs/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; повторный запуск НЕ перетирает существующие значения без явного флага (паттерн--forcegen_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.examplebundle, обе стороны группы 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: <repo>:<x.y.z>), не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, орк→PlaneORCH_PLANE_API_URL=http://<plane-proxy-svc>, орк→GiteaORCH_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}(операторский smokecurl /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, механика):
- Preflight (fail-fast, до мутаций): docker+compose есть;
deploy/bundled/.envсуществует и обязательные ключи заполнены (пути Claude CLI — существуют на хосте, иначе warning-блок: стек поднимется, конвейер без LLM не поедет); целевые порты свободны; томов/контейнеров с префиксомorchestrator-bundleнет (иначе — явный «уже инициализирован, продолжаю в ensure-режиме» либо отказ при противоречивом состоянии); RAM/диск ≥ минимумов из BUNDLED_SETUP (пороги — константы скрипта, синхронизированы с доком); python3+venv доступны. - Секреты (FR-3): webhook-секреты — субпроцессом
scripts/gen_secrets.py --write <tmp>(не реализуются заново, AC-7); bundle-внутренние (пароли postgres/redis/mq/minio, SECRET_KEY Plane, админ-пароль Gitea) — stdlibsecrets; запись вdeploy/bundled/.env+ корневой.env; существующие значения не перетираются без--force-secrets; значения в stdout/лог не печатаются (только имена ключей). - Up + готовность:
docker compose -f deploy/bundled/docker-compose.yml up -d(идемпотентен по построению — оба прочтения AC-1 истинны: оператор мог выполнить up сам); ожидание готовности poll-циклами с таймаутами (health контейнеров,migratorзавершилсяexit 0, HTTP-пробы Plane/Gitea); по таймауту — диагностика «какой сервис не дождались + хвост его логов». - Init Gitea — полностью автоматом (D6).
- Init Plane — manual-step чекпоинты (D7).
- Онбординг sandbox-проекта — строго
onboard_project.py apply+verify(D7). - Git-доступ агентов (D8) + клон sandbox-репо в
deploy/bundled/repos/. - Конфиг орка: сборка корневого
.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. - Health + итог:
GET /health,/queue,/metrics; финальная сводная таблица PASS/FAIL по шагам; следующая команда оператора — smoke BUNDLED_SETUP §smoke (REPLICATION §4).
- Preflight (fail-fast, до мутаций): docker+compose есть;
- Контракт 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 applyverifyиз 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 URLhttp://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/<repo>с remote-URL видаhttp://<token>@gitea:3000/<owner>/<repo>.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/configplaintext» зафиксирован честно: каталог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-импорт-лист, MANUALplane.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)