architect(ET): auto-commit from architect run_id=609
All checks were successful
CI / test (push) Successful in 59s
All checks were successful
CI / test (push) Successful in 59s
This commit is contained in:
@@ -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
|
||||
|
||||
```
|
||||
|
||||
@@ -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).
|
||||
|
||||
96
docs/architecture/adr/adr-0037-lite-replication-canon.md
Normal file
96
docs/architecture/adr/adr-0037-lite-replication-canon.md
Normal 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`.
|
||||
298
docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md
Normal file
298
docs/work-items/ORCH-102/06-adr/ADR-001-lite-setup-doc-canon.md
Normal 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 (шаги 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 <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)
|
||||
40
docs/work-items/ORCH-102/10-tech-risks.md
Normal file
40
docs/work-items/ORCH-102/10-tech-risks.md
Normal 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`).
|
||||
Reference in New Issue
Block a user