architect(ET): auto-commit from architect run_id=609

This commit is contained in:
2026-06-10 22:02:49 +03:00
committed by orchestrator-deployer
parent 30f1f33af1
commit 443ddc6b6f
5 changed files with 452 additions and 1 deletions

View File

@@ -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
```

View File

@@ -41,11 +41,13 @@ Per-work-item решения живут в `docs/work-items/<id>/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).

View File

@@ -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`.

View File

@@ -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 <ORCHESTRATOR_GIT_URL>` в `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 (шаги 05, опционально 6 до `done`) ссылкой + Lite-предусловия; критерий «конвейер доехал»: задача в БД → analyst-job в `/queue` → артефакты `0104` в ветке |
| 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 <ORCHESTRATOR_GIT_URL> <ORCH_DEPLOY_HOST_REPO_PATH>`** (плюс
опциональный `--branch <тег/срез>`). Конкретный канал дистрибуции (зеркало, доступ к нашему
Gitea, архив) — решение Владельца ВНЕ репо (коммерческая механика — анти-скоуп BRD §2.2);
док не хардкодит наш Gitea-URL (это же требует NFR-3/fenced-скан). Плейсхолдер
`<ORCHESTRATOR_GIT_URL>` включён в перечень подстановок §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)

View File

@@ -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`).