diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 070859d..7212b1c 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -195,6 +195,21 @@ env/конфиг; конвейер (`STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/ma `docs/work-items/ORCH-101/06-adr/ADR-001-host-parametrization-secrets-smoke.md` (D1…D10), `docs/work-items/ORCH-101/07-infra-requirements.md`, `10-tech-risks.md`. +**Type A — Lite (ORCH-102 — design).** Поверх 10-common вводится канон Lite-тиража: новый +docs-раздел `docs/deployment/` (витрина тиража, читатель — внешний оператор) с golden source +`docs/deployment/LITE_SETUP.md` — сквозной маршрут «голый хост → работающий конвейер» из 13 +нормативных разделов (предусловия → код → конфиг/секреты → Plane → Gitea → LLM → Telegram → +запуск → онбординг проекта → smoke → stateless-проверка → траблшутинг), каждый шаг = +fenced-команда + явная проверка (PASS/FAIL), хост-специфика — только плейсхолдеры. Compose не +форкается: `docker-compose.yml` сам является Lite-подмножеством (дефолтный `up -d` поднимает +ровно `orchestrator`+`orchestrator-watchdog`; staging — за профилем, в Lite опционален). Канон +watchdog-конфига — новый `.env.watchdog.example` (key-set = блоку `WATCHDOG_*` `.env.example`; +sidecar читает только `.env.watchdog`; C-1 ORCH-100 — отдельный бот). Норматив тиражной +инсталляции Gitea: branch protection на `main` НЕ включать (D10 ORCH-009, защита merge-актора). +Анти-дрейф — структурный `tests/test_lite_setup_doc.py`. Рантайм/конвейер — байт-в-байт +(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`. + ## Конвейер и Quality Gates ``` diff --git a/docs/architecture/adr/README.md b/docs/architecture/adr/README.md index 1a90f85..b9b65f4 100644 --- a/docs/architecture/adr/README.md +++ b/docs/architecture/adr/README.md @@ -41,11 +41,13 @@ Per-work-item решения живут в `docs/work-items//06-adr/ADR-NNN- | adr-0033 | Sidecar-watchdog F1b — мозг мониторинга в отдельном контейнере | proposed | 2026-06-10 | ORCH-100 | | adr-0034 | Машинный журнал уроков — таблица `lessons` + observer-leaf | proposed | 2026-06-10 | ORCH-098 | | adr-0035 | Turnkey-онбординг проектов — kit + операторский CLI + runbook | proposed | 2026-06-10 | ORCH-009 | +| adr-0036 | Фундамент тиража платформы — параметризация хоста, секреты, smoke (10-common) | proposed | 2026-06-10 | ORCH-101 | +| adr-0037 | Канон Lite-тиража — `docs/deployment/LITE_SETUP.md` + `.env.watchdog.example` | proposed | 2026-06-10 | ORCH-102 | > ⚠️ Историческая коллизия: номер `0007` занят двумя файлами — > `adr-0007-reconciler.md` (ORCH-053) и `adr-0007-executable-self-deploy.md` > (ORCH-036). Оба accepted; для новых сквозных ADR использовать следующий -> свободный номер (текущий максимум — `0035`). +> свободный номер (текущий максимум — `0037`). > adr-0014 **amends** adr-0013 (меняет критерий merge-verify на «SHA-в-main»). > adr-0016 **amends** adr-0013/0014 (гарантирует открытый код-PR перед merge_pr, ORCH-082). > adr-0020 реализует машинный слой к adr-0019 (ORCH-52b→52c). diff --git a/docs/architecture/adr/adr-0037-lite-replication-canon.md b/docs/architecture/adr/adr-0037-lite-replication-canon.md new file mode 100644 index 0000000..82114a7 --- /dev/null +++ b/docs/architecture/adr/adr-0037-lite-replication-canon.md @@ -0,0 +1,96 @@ +--- +work_item: ORCH-102 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-10 +model_used: claude-opus-4-8 +--- + +# adr-0037: Канон Lite-тиража — `docs/deployment/LITE_SETUP.md` + `.env.watchdog.example` (ORCH-102, 10a) + +## Статус +Proposed + +## Контекст + +Эпик ORCH-10 (D5 «Масштаб»), тип **A — Lite**: раздача орк+watchdog заказчику-тестеру, окружение +(Plane/Gitea/LLM/Telegram) он донастраивает сам. Фундамент 10-common (ORCH-101, adr-0036) в +`main`: технически платформа разворачивается без правки кода, но операционно знания размазаны по +4 operations-докам, писанным для оператора НАШЕГО хоста, — заказчик не может развернуть Lite без +доп-вопросов. Решения Владельца 10.06: оба типа тиража stateless; главный продукт ORCH-102 — +инструкция-golden-source в репо. + +Сквозной характер: вводится новый docs-раздел, новый канонический example-файл и нормативы, +обязательные для всех будущих задач эпика ORCH-10 и для любого агента, меняющего шаги тиража. +Детальный пакет решений (D1…D9, исходы вопросов ТЗ А-1…А-6) — work-item ADR: +`docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md`. + +## Решение + +1. **Новый docs-раздел `docs/deployment/` — витрина тиража.** Семантика: `deployment/` — «как + развернуть платформу у себя» (читатель — внешний оператор), `operations/` — «как + эксплуатировать наш прод». Golden source Lite — **`docs/deployment/LITE_SETUP.md`**: + сквозной маршрут «голый хост → работающий конвейер» из 13 нормативных разделов (рамка → + предусловия → код → конфиг/секреты → Plane → Gitea → LLM → Telegram → запуск → онбординг → + smoke → stateless-проверка → траблшутинг); каждый шаг = fenced-команда + явная проверка + (PASS/FAIL); хост-специфика — только плейсхолдеры. Канон не форкается: статусы/env/вебхуки — + ссылками на ONBOARDING/REPLICATION/SETUP_WEBHOOKS (`REPLICATION.md` остаётся в + `operations/`; перекрёстные ссылки в обе стороны). **Норматив сопровождения:** изменение + шагов тиража → обновление LITE_SETUP.md в том же PR (правило агентов №2). +2. **Compose не форкается.** `docker-compose.yml` сам является Lite-подмножеством (ровно + `orchestrator` + `orchestrator-watchdog` + `orchestrator-staging` за `profiles: [staging]`; + дефолтный `up -d` поднимает орк+watchdog; сервисов Plane/Gitea нет) — отдельный + `docker-compose.lite.yml` не вводится; свойство становится CI-гарантией (структурный тест + через `yaml.safe_load`). +3. **`.env.watchdog.example` — третий канонический env-example** (рядом с `.env.example`/ + `.env.staging.example`): закрывает ловушку файла-носителя (sidecar читает ТОЛЬКО + `.env.watchdog`; ключ `WATCHDOG_*` в `.env` для него инертен). Key-set = ровно блок + `WATCHDOG_*` из `.env.example` (равенство множеств держит тест); токены — плейсхолдеры; + шапка несёт C-1 ORCH-100 (отдельный watchdog-бот, токен орка переиспользовать запрещено) + и когерентность `WATCHDOG_METRICS_URL` ⇄ `ORCH_DEPLOY_PROD_TARGET_PORT`. +4. **Норматив тиражной инсталляции Gitea: branch protection на `main` НЕ включать; pre-receive + хуки не вводятся** (подтверждение ORCH-009 ADR-001 D10 для чужих инсталляций: + required-approvals/status-checks ломают PR-merge API merge-актора → ложные HOLD; защита + `main` — конвенция + скоуп токенов + INV-4). +5. **Staging-контур в Lite опционален:** базовый контур заказчика = prod-оркестратор + watchdog; + песочница 8501 нужна только при self-hosting развитии платформы у заказчика (регистрация + проекта `orchestrator`); guard ORCH-058 (staging-порт ≠ прод-порт) действует. +6. **Анти-дрейф — постоянная CI-гарантия:** `tests/test_lite_setup_doc.py` (структурный, без + сети/LLM): разделы/кирпичи дока, env-ключи дока ⊂ `.env.example`, key-sync watchdog-example, + compose-подмножество, stateless-норматив + отсутствие секретов/боевых литералов в + fenced-блоках (реюз центрального `FORBIDDEN` из `tests/test_no_host_hardcodes.py` импортом), + перекрёстность REPLICATION→LITE_SETUP + CHANGELOG. + +### Что НЕ меняется +`src/**`, `docker-compose.yml`, `Dockerfile`, `scripts/**`; `STAGE_TRANSITIONS`, состав +`QG_CHECKS`, семантика `check_*`, machine-verdict ключи, схема БД — байт-в-байт. Новый QG не +регистрируется (структурные тесты попадают в существующие гейты). Прод-контейнер в рамках +задачи не рестартуется (выкат — штатно: staging 8501 → `Confirm Deploy`). + +## Альтернативы +- **Инструкция в `docs/operations/`** — отвергнуто: другой целевой читатель; путь зафиксирован + Владельцем (D-4). +- **`docker-compose.lite.yml`** — отвергнуто: вторая правда о сервисах = дрейф-поверхность. +- **Pre-receive/branch protection как «защита `main`»** — отвергнуто: класс инцидента ORCH-063 + (ложные HOLD merge-актора); пересмотр — только отдельным ADR. +- **Без example-файла watchdog (шаг прозой)** — отвергнуто: двусмысленность файла-носителя + остаётся; example + key-sync тест надёжнее. + +## Последствия +- Type A эпика ORCH-10 закрыт продуктом-инструкцией; Type B (Bundled) строится поверх + (переиспользует разделы Lite). Полнота инструкции и compose-подмножество защищены CI. +- Цена: новый golden source требует сопровождения (норматив «в том же PR» + структурный тест + рвёт CI при дрейфе); осознанный дубль ключей `WATCHDOG_*` в двух example-файлах — под + key-sync тестом. +- Откат: удалить `docs/deployment/`, тест и `.env.watchdog.example`, вернуть строку + REPLICATION.md §1 — состояние 1:1 (docs+tests, без миграций). + +## Связи +adr-0036 (ORCH-101 — фундамент 10-common; этот ADR строит слой Lite поверх), adr-0035 +(ORCH-009 — onboarding-CLI/kit переиспользуются маршрутом §5/§6/§10; D10 подтверждён п.4), +adr-0033 (ORCH-100 — sidecar-watchdog; C-1 независимый Telegram-канал закреплён в example), +adr-0008 (ORCH-058 — staging-порт guard, вилка staging п.5), adr-0027/INV-4 (merge-актор — +основание запрета branch protection). Детально — +`docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md`, +`docs/work-items/ORCH-102/10-tech-risks.md`. diff --git a/docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md b/docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md new file mode 100644 index 0000000..275eca6 --- /dev/null +++ b/docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md @@ -0,0 +1,298 @@ +--- +work_item: ORCH-102 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-10 +model_used: claude-opus-4-8 +--- + +# ADR-001: Канон Lite-тиража — `docs/deployment/LITE_SETUP.md`, исходы А-1…А-6, анти-дрейф контур + +Work Item: **ORCH-102** — ORCH-10a Lite-тираж: перенос орк+watchdog + полная инструкция донастройки окружения +Стадия: **architecture** +Сквозная регистрация: **`docs/architecture/adr/adr-0037-lite-replication-canon.md`** +(новый docs-раздел `docs/deployment/`, новый канонический example-файл и нормативы тиражной +инсталляции — кросс-каттинг для всех будущих задач эпика ORCH-10 и для агентов, меняющих шаги тиража). + +## Статус +Proposed + +## Контекст + +Эпик ORCH-10 (D5 «Масштаб»), тип **A — Lite**: заказчик-тестер разворачивает на своей инфре +ТОЛЬКО орк+watchdog, окружение (Plane/Gitea/LLM/Telegram) донастраивает сам по инструкции. +Решения Владельца 10.06 (BRD §1.4): оба типа тиража stateless (D-2), главный продукт ORCH-102 — +**инструкция** `docs/deployment/LITE_SETUP.md` (D-4). + +Факты, сверенные с кодом/репо на ветке задачи: + +- **Фундамент 10-common (ORCH-101) в `main`** (adr-0036): хост-значения параметризованы + (`${VAR:-default}`, «дефолт = боевое значение»), секреты выпускаются заново + (`scripts/gen_secrets.py`), smoke с PASS/FAIL — `docs/operations/REPLICATION.md` §4, + анти-регресс `tests/test_no_host_hardcodes.py` (центральный список `FORBIDDEN` + хелпер + `find_violations`). Технически платформа разворачивается без правки кода; операционно + единого маршрута для заказчика нет (знания в 4 operations-доках, писанных для НАШЕГО хоста). +- **`docker-compose.yml` уже является Lite-подмножеством:** ровно 3 сервиса — + `orchestrator`, `orchestrator-watchdog`, `orchestrator-staging`; staging строго за + `profiles: [staging]`; сервисов/образов Plane/Gitea нет. Дефолтный `docker compose up -d` + поднимает ровно орк+watchdog. Свойство нигде не зафиксировано и ничем не защищено. +- **Гэп канона watchdog-конфига:** compose читает `.env.watchdog` + (`env_file: {path: .env.watchdog, required: false}`), но example-файла нет (есть только + `.env.example` / `.env.staging.example`). Ключи `WATCHDOG_*` (19 шт., дефолты = + `watchdog/config.py`) задокументированы внутри `.env.example` — однако sidecar-контейнер + читает ТОЛЬКО `.env.watchdog`: ключ, положенный оператором в `.env`, для watchdog **инертен** + (в `.env` его видит лишь контейнер орка через его `env_file`). Двусмысленность файла-носителя — + реальная ловушка первичной настройки. +- **«Pre-receive хуки» из бизнес-запроса конфликтуют с действующим нормативом:** ORCH-009 + ADR-001 **D10** — branch protection на `main` НЕ включать (required-approvals/status-checks → + 405/409-класс отказов `merge_pr` → ложные HOLD, класс инцидента ORCH-063); merge-актор работает + только через Gitea PR-merge API (INV-4, `src/merge_gate.py`). Pre-receive хуков в платформе нет; + защита `main` — конвенция (агенты не пушат `main`) + скоуп токенов. +- **Парсер YAML доступен тестам** (`yaml.safe_load` уже используется в + `tests/test_infra_parametrization.py::_compose_services`) — структурный тест + compose-подмножества не требует новых зависимостей. +- Прецедент приёмки smoke без нового железа: ORCH-101 AC-3 — staging-песочница + (`ORCH_STAGING_PORT`=8501, изолированная БД `./data/staging`) + sandbox-проект + (ONBOARDING.md §5, журнал smoke-прогонов). + +Задача — **docs+tests** (паттерн ORCH-077/092): `src/**`, `docker-compose.yml`, `Dockerfile`, +`scripts/**` читаются как источник истины и НЕ меняются; конвейер +(`STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict/схема БД) — байт-в-байт. + +## Решение + +### Сводка + +Создаётся новый docs-раздел **`docs/deployment/`** (витрина тиража) с golden source +**`docs/deployment/LITE_SETUP.md`** — сквозной маршрут «голый хост → работающий конвейер» из 13 +нормативных разделов (D2), собирающий кирпичи 10-common ссылками, без форка канона. Compose не +форкается (D4); канон watchdog-конфига закрывается новым **`.env.watchdog.example`** (D5); +раздел Gitea фиксирует действующий норматив D10 — branch protection НЕ включать, pre-receive не +вводить (D3). Полнота/гигиена дока и compose-подмножество защищаются структурным +`tests/test_lite_setup_doc.py` (D8). Рантайм не трогается; исходы всех вопросов ТЗ §3.8 — ниже. + +### D1 (исход А-3) — Размещение: новый раздел `docs/deployment/`, путь бизнес-запроса подтверждён + +**Решение: `docs/deployment/LITE_SETUP.md`, как зафиксировано Владельцем (D-4).** Семантика +разделов: `docs/deployment/` — «как развернуть платформу У СЕБЯ» (читатель — внешний +оператор/заказчик, платформу видит впервые); `docs/operations/` — «как эксплуатировать НАШ прод» +(читатель — оператор текущего хоста). `REPLICATION.md` остаётся в `docs/operations/` — это +runbook фундамента 10-common, исторический якорь ORCH-101, к пути прибит +`tests/test_replication_smoke.py`; перенос дал бы правку чужого артефакта без выгоды. +Перекрёстные ссылки обе стороны: REPLICATION.md §1 (строка Type A — Lite → ✅ ORCH-102 + ссылка +на LITE_SETUP.md) и LITE_SETUP.md §1 (границы — ссылкой на REPLICATION.md §1). INFRA.md получает +ссылку на deployment-раздел (по фактическому объёму, FR-7). + +Привязка: FR-1, FR-7, AC-1, AC-5. + +### D2 (FR-1) — Нормативная структура LITE_SETUP.md: 13 разделов, форма «команда + проверка» + +**Решение: фиксированная нумерация `## N. <Название>`, порядок = маршрут оператора** (состав — +обязательный минимум FR-1; анти-дрейф тест ассертит наличие и порядок заголовков): + +| § | Раздел | Ключевое содержимое (норматив) | +|---|--------|--------------------------------| +| 1 | Рамка Lite | что разворачиваем (орк+watchdog); что заказчик ставит сам; границы vs 10-common/Bundled — ссылка REPLICATION.md §1; платформенные конвенции (репо `orchestrator`, имена сервисов, контейнерный layout — не менять) | +| 2 | Предусловия хоста | контур: Linux x86_64, Docker Engine + Compose v2, git, python3, node + дистрибутив claude-code; `ORCH_RUN_UID/GID` = uid владельца `ORCH_HOST_REPOS_DIR` (ORCH-040), `ORCH_DOCKER_GID` ← `getent group docker`; ssh (`ORCH_HOST_SSH_DIR`); свободные порты 8500/8501; **каждое предусловие — команда проверки** | +| 3 | Перенос кода | `git clone ` в `ORCH_DEPLOY_HOST_REPO_PATH` (D7); НИКАКИХ данных/БД/`.env` с боевого хоста; watchdog отдельно не переносится (код в `watchdog/` того же репо) | +| 4 | Конфигурация | `.env` с нуля от `.env.example` (канон); `python3 scripts/gen_secrets.py [--write]`; карта env — ссылка REPLICATION.md §2; явный список обязательных ключей нового хоста (вкл. `ORCH_PROJECTS_JSON`); `.env.watchdog` от `.env.watchdog.example` (D5); когерентность портов `ORCH_DEPLOY_PROD_TARGET_PORT` ⇄ `WATCHDOG_METRICS_URL` ⇄ `ORCH_POST_DEPLOY_BASE_URL` | +| 5 | Подключение Plane | workspace/проект; 22 статуса с группами — создаёт `onboard_project.py apply`, таблица ссылкой ONBOARDING.md §1; явно — fail-closed `Confirm Deploy`/`STOP`; API-токен + проверка `curl`; webhook+HMAC: URL `…/webhook/plane`, `X-Plane-Signature`, каверза Plane CE — оба пути (UI и SQL), generic-команды по образцу SETUP_WEBHOOKS.md с плейсхолдерами | +| 6 | Подключение Gitea | репо (`onboard_project.py` или вручную); токен scope `repo`,`admin:repo_hook` + проверка; per-repo webhook (`push`/`pull_request`/`status`, `X-Gitea-Signature`, ОДИН глобальный секрет); **норматив D3: branch protection НЕ включать** | +| 7 | LLM (claude CLI) | дистрибутив claude-code и node на хосте = источники маунтов (`ORCH_HOST_CLAUDE_CODE_DIR`/`ORCH_HOST_NODE_BIN`); аутентификация CLI (`ORCH_HOST_CLAUDE_DIR`/`ORCH_HOST_CLAUDE_JSON`); проверка `claude --version`; модели — `ORCH_AGENT_MODEL_*`/`ORCH_AGENT_EFFORT_*` (резолв ORCH-41, дефолты — `.env.example`) | +| 8 | Telegram | бот трекера (BotFather → `ORCH_TELEGRAM_BOT_TOKEN`), `chat_id` через `getUpdates`, проверка `getMe`; **отдельный** watchdog-бот → `.env.watchdog` (C-1 ORCH-100: токен орка переиспользовать ЗАПРЕЩЕНО) | +| 9 | Запуск | `docker compose config` → `up -d --build` → ровно `orchestrator`+`orchestrator-watchdog` (D4); health-чек `GET /health` → 200 `"status":"ok"`, `/queue`+`/metrics` → штатный JSON (`schema_version: 1`); **вилка staging (D6)** | +| 10 | Регистрация проекта | `onboard_project.py plan → apply → verify` (статусы+лейблы `autoApprove`/`autoDeploy`/`Bug`, репо+webhook, kit, merged `ORCH_PROJECTS_JSON`) → строка в `.env` → управляемый рестарт → `/health`+`/queue`; маршрут — ссылка ONBOARDING.md, в доке — последовательность команд Lite | +| 11 | Smoke | чек-лист REPLICATION.md §4 (шаги 0–5, опционально 6 до `done`) ссылкой + Lite-предусловия; критерий «конвейер доехал»: задача в БД → analyst-job в `/queue` → артефакты `01–04` в ветке | +| 12 | Stateless-проверка | первый старт = пустая БД (`data/` чист); `GET /queue` — нулевые счётчики, ни одной задачи `ORCH-*`/`ET-*`; нормативная строка «данные/задачи/секреты боевого хоста НЕ переносятся» | +| 13 | Траблшутинг | ≥ 5 отказов, каждый «симптом → команда диагностики → лечение»: webhook 401/HMAC mismatch; задача не появилась (реестр/`ORCH_PROJECTS_JSON`/webhook); claude CLI не найден/не аутентифицирован; docker.sock permission denied (`ORCH_DOCKER_GID`); права `/repos` (uid mismatch, ORCH-040/057); Telegram молчит | + +**Форма (нормативно, NFR-6/BR-1):** (а) каждый нормативный шаг = fenced-команда + явная проверка +(маркер «Проверка:» / PASS/FAIL / ожидаемый вывод); (б) хост-специфика в fenced-блоках — ТОЛЬКО +плейсхолдеры `<...>`/`$ENV_VAR`; боевые литералы (`mva154`, `duckdns`, `82.22.50.71`, реальные +токены) запрещены; дефолты хост-переменных НЕ копируются в док — ссылка на карту +REPLICATION.md §2 (иначе fenced-скан D8 и форк канона); (в) язык — русский; (г) футер дока несёт +норматив сопровождения (NFR-5: меняешь шаги тиража → обнови LITE_SETUP.md в том же PR, +правило агентов №2). + +Привязка: FR-1, FR-3, FR-4, AC-1, AC-3. + +### D3 (исход А-1) — Раздел Gitea: канон D10, pre-receive НЕ вводить + +**Решение: раздел Gitea фиксирует канон платформы — репо + токен (scope `repo`, +`admin:repo_hook`) + per-repo webhook + нормативная рамка «branch protection на `main` НЕ +включать».** Формулировка бизнес-запроса «pre-receive хуки» трактуется по намерению («защита +`main`») и закрывается действующей моделью: мерж — только через Gitea PR-merge API под токеном +орка (INV-4, `src/merge_gate.py`); агенты не пушат `main` (конвенция + скоуп токенов). +Pre-receive хуки НЕ вводятся: это server-side механизм инсталляции Gitea заказчика, платформа +его не несёт и не проверяет; required-approvals/status-checks дали бы 405/409-класс отказов +`merge_pr` → ложные HOLD (ORCH-009 ADR-001 D10, инцидент ORCH-063). В §13 (траблшутинг) — +симптом «PR не мержится / HOLD» с первой проверкой «не включена ли branch protection». +Пересмотр — только отдельным ADR с анализом совместимости с merge-актором (ORCH-093/INV-4). + +Привязка: BRD §1.3/§6, FR-1 п.6, AC-7. + +### D4 (исход А-2) — Compose: существующий файл, без lite-форка; свойство держит тест + +**Решение: `docker-compose.yml` НЕ форкается** — он уже является Lite-подмножеством (факт из +контекста). Отдельный `docker-compose.lite.yml` отвергнут: второй файл = вторая правда = +поверхность дрейфа (зеркало решения D9 ORCH-101 «без скрипта-обвязки»). Свойство фиксируется +в LITE_SETUP.md §9 и защищается тестом (D8, TC-04) через `yaml.safe_load` (паттерн +`tests/test_infra_parametrization.py::_compose_services`): + +- `set(services) == {"orchestrator", "orchestrator-watchdog", "orchestrator-staging"}`; +- `services["orchestrator-staging"]["profiles"] == ["staging"]`; +- множество сервисов без `profiles` (= дефолтный `up -d`) — ровно + `{"orchestrator", "orchestrator-watchdog"}`; +- ни в имени сервиса, ни в `image:`/`container_name:` нет подстрок `plane`/`gitea` + (анти-появление молча). + +Привязка: FR-2, AC-2, BR-3. + +### D5 (исход А-4) — `.env.watchdog.example`: создать; key-set синхронизирован тестом + +**Решение: создать `.env.watchdog.example`** — третий канонический env-example +(симметрия `.env.example`/`.env.staging.example`), единственное разрешённое отклонение диффа от +docs+tests (предусмотрено ТЗ §2). Нормативное содержимое: + +- **полный набор ключей `WATCHDOG_*` — ровно тот же, что блок `WATCHDOG_*` в `.env.example`** + (key-set equality, 19 ключей; значения = дефолты канона/`watchdog/config.py`, + токены — пустые плейсхолдеры); +- шапка-комментарий: семантика файла-носителя — sidecar-контейнер читает ТОЛЬКО `.env.watchdog` + (compose `env_file: required: false`; отсутствие файла не ломает `up`; нет токена → fail-safe: + логи без отправки); **ключ `WATCHDOG_*` в `.env` для sidecar инертен**; +- норматив C-1 (ORCH-100): свой бот, независимый канал; токен орка переиспользовать запрещено; +- когерентность порта: `WATCHDOG_METRICS_URL` следует за `ORCH_DEPLOY_PROD_TARGET_PORT`; +- «не коммить реальный `.env.watchdog`» (зеркало шапки `.env.staging.example`). + +Анти-дрейф (D8, TC-02b): `keys(.env.watchdog.example) == {k ∈ keys(.env.example) | +k.startswith("WATCHDOG_")}` — появление нового ключа watchdog в каноне без обновления example +(и наоборот) ломает CI. Это дубль по необходимости, защищённый сверкой парсингом, не строкой +(NFR-4). LITE_SETUP.md §4/§8 предписывают `cp .env.watchdog.example .env.watchdog` + два токена. + +Привязка: FR-1 п.4/п.8, FR-6.2, AC-1, AC-7 (C-1). + +### D6 (исход А-5) — Staging-контур в Lite: опционален, явная вилка + +**Решение: базовый Lite-контур = prod-оркестратор (8500) + watchdog; `orchestrator-staging` +НЕ обязателен.** Staging нужен ТОЛЬКО если заказчик регистрирует проект `orchestrator` +(self-hosting развитие самой платформы у себя): стадия `deploy-staging` орка требует +песочницу 8501 (`.env.staging` от `.env.staging.example`, изолированная БД `./data/staging`, +guard ORCH-058: staging-порт ≠ прод-порт — fail-closed). Для целевого сценария «раздача на +тест» (заказчик гоняет СВОИ проекты) staging не участвует. В §9 — явная вилка двумя ветками +с командами; дефолтная ветка — без staging. + +Привязка: FR-1 п.9, AC-2 (staging за профилем), ТЗ §3.8 А-5. + +### D7 (исход А-6) — Источник кода: параметризованный шаг + +**Решение: шаг §3 — `git clone `** (плюс +опциональный `--branch <тег/срез>`). Конкретный канал дистрибуции (зеркало, доступ к нашему +Gitea, архив) — решение Владельца ВНЕ репо (коммерческая механика — анти-скоуп BRD §2.2); +док не хардкодит наш Gitea-URL (это же требует NFR-3/fenced-скан). Плейсхолдер +`` включён в перечень подстановок §3. + +Привязка: FR-1 п.3, BRD §6, ТЗ §3.8 А-6. + +### D8 (FR-6) — Анти-дрейф контур: `tests/test_lite_setup_doc.py` + +**Решение: один структурный модуль, детерминированный, без сети/LLM/subprocess** (образец — +`tests/test_replication_smoke.py`). Нормативные семейства проверок (точная нарезка по +тест-функциям — за developer, `04-test-plan.yaml`): + +| TC | Проверка | Механика | +|----|----------|----------| +| TC-01 | Док существует; 13 нормативных разделов D2 присутствуют **в заданном порядке** | regex по заголовкам `^## N\.` | +| TC-02 | Обязательные кирпичи FR-6.1: `gen_secrets.py`, `onboard_project.py`, `docker compose`, `/health`, `/queue`, `/metrics`, `ORCH_PROJECTS_JSON`, оба webhook-секрета, `X-Plane-Signature`/`X-Gitea-Signature`, `getent group docker`, `Confirm Deploy`/`STOP`, оба Telegram-канала, маркеры PASS/FAIL/«Проверка» | подстроки | +| TC-02b | Key-sync `.env.watchdog.example` ⇄ блок `WATCHDOG_*` `.env.example` (D5) | парсинг строк `^KEY=` обоих файлов, равенство множеств | +| TC-03 | Каждый токен `\b(ORCH\|WATCHDOG)_[A-Z0-9_]+\b` из дока существует ключом в `.env.example` (канон 100% ключей, ORCH-101) | regex по доку + парсинг `.env.example` | +| TC-04 | Compose-подмножество (D4): 3 сервиса, staging за профилем, дефолтный up = орк+watchdog, нет `plane*`/`gitea*` | `yaml.safe_load` | +| TC-05 | Stateless/гигиена: нормативная строка «не перенос…» присутствует; **в fenced-блоках** нет литералов центрального списка `FORBIDDEN` (импорт из `tests/test_no_host_hardcodes.py`, НЕ копия — один источник истины) и секретоподобных значений (эвристика: hex/base64 ≥ 32 симв., не плейсхолдер) | выделение fenced-блоков + `find_violations`/эвристика | +| TC-06 | Перекрёстность: `REPLICATION.md` §1 ссылается на `LITE_SETUP.md`; `CHANGELOG.md` несёт `ORCH-102` | подстроки | + +Скоуп секрет/литерал-скана — **только fenced-блоки** (копируемое); проза дока дефолтов не +дублирует (D2-форма, п. б) — поэтому полнодокументный скан не нужен и не даёт ложно-красных. +Существующие тесты (`test_no_host_hardcodes`, `test_replication_smoke`, +`test_infra_parametrization`, `test_onboarding_*`) не ослабляются; новые тесты попадают в +существующие гейты (`check_ci_green`/`check_tests_passed`/merge-gate re-test/coverage ORCH-027) +автоматически — новый QG НЕ регистрируется (ТЗ §6). + +Привязка: FR-6, BR-8, AC-5, AC-6. + +### D9 — Границы изменения; что НЕ делается; 07/08 — N/A + +- **Дифф задачи:** `docs/**` (LITE_SETUP.md, правки REPLICATION §1/INFRA/README/CLAUDE.md по + объёму), `tests/test_lite_setup_doc.py`, `CHANGELOG.md`, `.env.watchdog.example` (D5). + **`src/**`, `docker-compose.yml`, `Dockerfile`, `scripts/**` — ноль изменений**; любое + отклонение — только новым ADR (AC-7). +- `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict ключи/схема БД — байт-в-байт; новых + эндпоинтов/флагов/kill-switch нет (выключать нечего — D-4: док и тесты). +- **`07-infra-requirements.md` / `08-data-requirements.md` — N/A:** топология не меняется (ни + контейнера, ни порта, ни маунта), схема БД не меняется (ТЗ §5). Инфра-предусловий нет: + приёмочный smoke-прогон (FR-5/AC-4) идёт на СУЩЕСТВУЮЩЕМ контуре — staging-песочница + `ORCH_STAGING_PORT`=8501 + одноразовый sandbox-проект (прецедент ORCH-101 AC-3 / + ONBOARDING.md §5); прогон на реальном новом хосте желателен, но не требование приёмки. +- **Self-hosting (NFR-1):** прод-контейнер не рестартится; выкат — штатный конвейер + (deploy-staging 8501 → ручной `Confirm Deploy`). Для enduro-trails изменение инертно. +- Эскалация: `arch:major-change` не требуется (нет новой стадии/компонента/смены БД); ТЗ + удовлетворимо без нарушения принципов — возврат в анализ не нужен. + +## Альтернативы + +- **Разместить инструкцию в `docs/operations/`** — отвергнуто: целевой читатель другой (внешний + оператор, не оператор нашего прода); путь `docs/deployment/LITE_SETUP.md` зафиксирован + Владельцем (D-4), отступать не от чего (D1). +- **Перенести REPLICATION.md в `docs/deployment/`** — отвергнуто: правка чужого артефакта и + путей `tests/test_replication_smoke.py` без выгоды; перекрёстных ссылок достаточно (D1). +- **Отдельный `docker-compose.lite.yml`** — отвергнуто: вторая правда о сервисах, дрейф- + поверхность; существующий файл уже = подмножество, тест дешевле форка (D4). +- **Pre-receive хуки / branch protection для «защиты `main`»** — отвергнуто: ломает PR-merge + API merge-актора (D10 ORCH-009, ложные HOLD); защита — конвенция + скоуп токенов + INV-4 (D3). +- **Без `.env.watchdog.example`, шаг «создай файл с двумя ключами» в доке** — отвергнуто: + двусмысленность файла-носителя остаётся (ключи канонизированы в `.env.example`, но там для + sidecar инертны); example-файл + key-sync тест дешевле и надёжнее прозы (D5). +- **Скопировать таблицу 22 статусов / карту env в LITE_SETUP.md** — отвергнуто: форк канона + (R-2); ссылки на golden source + явное упоминание только fail-closed имён + (`Confirm Deploy`/`STOP`) — достаточно (FR-4); дубль допустим только с тест-сверкой + парсингом/импортом (прецедент — TC-02b). +- **Полнодокументный секрет-скан (не только fenced)** — отвергнуто: ложно-красные на прозе + («не используйте mva154-значения»); норматив D2(б) выводит дефолты из дока, fenced-скан + покрывает всё копируемое (D8). + +## Последствия + +- **+** Закрывается Type A эпика ORCH-10: заказчик получает единый исполняемый маршрут + «голый хост → конвейер» без археологии по 4 докам; Type B (Bundled) строится поверх + (переиспользует §2–§8, §11–§13). +- **+** Compose-подмножество и полнота инструкции из «фактов» становятся CI-гарантиями + (TC-01…TC-06); ловушка файла-носителя watchdog-конфига закрыта каноном (D5). +- **+** Нулевой риск рантайма: docs+tests, конвейер байт-в-байт, kill-switch не нужен. +- **−** Новый golden source = новая обязанность сопровождения (шаги тиража меняются → + LITE_SETUP.md в том же PR; NFR-5) — митигировано футером-нормативом, правилом агентов №2 и + структурным тестом, который рвёт CI при дрейфе кирпичей. +- **−** Дубль ключей `WATCHDOG_*` в двух example-файлах — принят осознанно, защищён key-sync + тестом TC-02b (равенство множеств, не строк). +- **−** Приёмка «без доп-вопросов» на staging-песочнице ≠ прогон на реальном чужом хосте — + остаточный риск принят (прецедент ORCH-101 AC-3); зазор сужают чек-команды предусловий §2 и + траблшутинг §13. +- **Откат:** удалить `docs/deployment/`, `tests/test_lite_setup_doc.py`, + `.env.watchdog.example`, вернуть строку REPLICATION.md §1 — состояние 1:1, ни миграций, + ни состояния (ТЗ §7). + +## Ссылки + +- BRD: `docs/work-items/ORCH-102/01-brd.md` (решения Владельца D-1…D-5, факты §1.3) +- TRZ: `docs/work-items/ORCH-102/02-trz.md` (FR-1…FR-7, вопросы §3.8 А-1…А-6) +- Acceptance: `docs/work-items/ORCH-102/03-acceptance-criteria.md` (AC-1…AC-7) +- Риски: `docs/work-items/ORCH-102/10-tech-risks.md` +- Сквозной ADR: `docs/architecture/adr/adr-0037-lite-replication-canon.md` +- Сверено по коду/репо: `docker-compose.yml` (3 сервиса, `profiles: [staging]`, + `env_file: {path: .env.watchdog, required: false}`), `.env.example` (блок `WATCHDOG_*`, + 19 ключей), `watchdog/config.py` (дефолты), `tests/test_infra_parametrization.py` + (`yaml.safe_load`-паттерн), `tests/test_no_host_hardcodes.py` (`FORBIDDEN`, + `find_violations`), `tests/test_replication_smoke.py` (образец структурного теста дока), + `docs/operations/REPLICATION.md` §1–§5, `docs/operations/ONBOARDING.md` §1/§5, + `docs/operations/SETUP_WEBHOOKS.md` +- Инварианты соседних решений: ORCH-009 ADR-001 D10 (`docs/work-items/ORCH-009/06-adr/…`), + adr-0036 (ORCH-101, 10-common), adr-0035 (onboarding), adr-0033 (sidecar-watchdog, C-1 + ORCH-100), ORCH-040 (uid/gid/HOME), ORCH-058 (staging-порт guard), INV-4 (ORCH-093) diff --git a/docs/work-items/ORCH-102/10-tech-risks.md b/docs/work-items/ORCH-102/10-tech-risks.md new file mode 100644 index 0000000..14c1dc6 --- /dev/null +++ b/docs/work-items/ORCH-102/10-tech-risks.md @@ -0,0 +1,40 @@ +--- +work_item: ORCH-102 +stage: architecture +author_agent: architect +status: proposed +created_at: 2026-06-10 +model_used: claude-opus-4-8 +--- + +# 10 — Технические риски: ORCH-102 — ORCH-10a Lite-тираж (инструкция + анти-дрейф) + +Work Item: **ORCH-102** · Repo: **orchestrator** · Стадия: architecture + +> Информационный (гейтом не парсится). Перечисляет риски реализации и их митигейшн. +> Решения, на которые ссылаются митигейшны (D1…D9), — `06-adr/ADR-001-lite-setup-doc-canon.md`. + +## Реестр рисков + +| ID | Риск | Вер. | Влия. | Митигейшн | +|----|------|------|-------|-----------| +| TR-1 | **Дрейф инструкции**: платформа развивается, шаги LITE_SETUP.md тихо устаревают (новый env-ключ, изменённый маршрут онбординга) | Сред. | Сред. | Структурный `tests/test_lite_setup_doc.py` (D8: кирпичи, env-ключи ⊂ `.env.example`, compose-подмножество, перекрёстность) рвёт CI при дрейфе; футер-норматив NFR-5 + правило агентов №2 («шаги тиража → док в том же PR»); reviewer-ось обзорных доков (правило №6) | +| TR-2 | **Форк канона**: скопированная в док таблица статусов/env/вебхуков разъезжается с golden source | Низ. | Сред. | D2/FR-4: канон — только ссылками (ONBOARDING §1 / REPLICATION §2–§4 / SETUP_WEBHOOKS); в доке дублируются лишь fail-closed имена (`Confirm Deploy`/`STOP`) и списки обязательных ключей — оба под TC-02/TC-03; единственный структурный дубль (`WATCHDOG_*`) — под key-sync TC-02b | +| TR-3 | **«Без доп-вопросов» непроверяемо**: реальный заказчик найдёт пробел, который не увидел внутренний прогон | Сред. | Низ. | Операционализация BRD §6: каждый шаг = команда+проверка (TC-01/02), приёмочный smoke-прогон на чистом контуре фиксируется в `13-test-report.md`/`15-staging-log.md`, траблшутинг ≥5 отказов (§13). Остаточные пробелы лечатся правкой golden source штатной задачей | +| TR-4 | **Гетерогенность хостов заказчиков**: rootless docker, иной uid владельца репо, нет node/claude-code, arm64 | Сред. | Сред. | §2 «Предусловия» с явным поддерживаемым контуром (Linux x86_64, Docker+Compose v2, git, python3, node) и чек-командой на каждое предусловие (`getent group docker`, владение `ORCH_HOST_REPOS_DIR` = `ORCH_RUN_UID`); вне контура — вне гарантии Lite (BRD §6); §13 покрывает типовые отказы (docker.sock gid, uid mismatch ORCH-040/057) | +| TR-5 | **Заказчик включит branch protection / pre-receive на `main`** вопреки нормативу → ложные HOLD merge-актора на ЕГО инсталляции (класс ORCH-063) | Низ. | Выс. (у заказчика) | D3: явная нормативная рамка в §6 («НЕ включать», основание D10 ORCH-009/INV-4) + симптом в §13 («PR не мержится/HOLD → проверь protection»); для нашего прода риск нулевой (его Gitea не трогается) | +| TR-6 | **Утечка секрета/боевого литерала** через примеры дока или `.env.watchdog.example` | Низ. | Выс. | NFR-3: только плейсхолдеры; TC-05 — fenced-скан на `FORBIDDEN` (импорт из `test_no_host_hardcodes.py`, один источник истины) + эвристика секретоподобных значений; security-гейт ORCH-022 (`17-security-report.md`) обязан остаться PASS; D2(б): дефолты хост-переменных в док не копируются | +| TR-7 | **Хрупкость анти-дрейф теста**: ложно-красный CI от безобидной правки прозы → соблазн ослабить тест | Сред. | Низ. | D8: ассерты только на стабильное (заголовки `## N.`, подстроки-кирпичи, парсинг env-ключей, `yaml.safe_load`), запреты — только в fenced-блоках; никаких проверок прозы/формулировок; паттерн уже обкатан (`test_replication_smoke.py` стабилен) | +| TR-8 | **`.env.watchdog.example` разъезжается** с блоком `WATCHDOG_*` `.env.example` / дефолтами `watchdog/config.py` | Низ. | Низ. | TC-02b: равенство множеств ключей двух файлов; значения = дефолты канона (сверены с `watchdog/config.py` при ревью); полнота самого `.env.example` уже держится тестами ORCH-101 | +| TR-9 | **Приёмочный smoke на staging-песочнице ≠ голый чужой хост**: средовые эффекты нового железа (сеть, версии docker) не проверены | Сред. | Низ. | Принятый прецедент ORCH-101 AC-3 (без нового железа в контуре задачи; FR-5(г) — прогон на реальном хосте желателен, не блокер); зазор сужают чек-команды §2 и траблшутинг §13; первый реальный тираж — операторски сопровождаемый | + +## Сводный вывод + +Доминирующий класс — **документационный** (дрейф/полнота/гигиена), систематически закрытый +структурными тестами (TC-01…TC-06) и нормативом golden source; рантайм-рисков нет — дифф +docs+tests (+`.env.watchdog.example`), конвейер и `src/**` байт-в-байт (D9), kill-switch не +требуется. Самый тяжёлый по влиянию риск (TR-5) материализуется только на инсталляции +заказчика и купирован явным нормативом + траблшутингом. Эскалация `arch:major-change` не +требуется; возврат в анализ не нужен. **Остаточный риск для self-hosting прод-конвейера — +минимальный** (прод-контейнер не трогается; выкат — штатно через staging 8501 → +`Confirm Deploy`).