analyst(ET): auto-commit from analyst run_id=602
All checks were successful
CI / test (push) Successful in 1m9s
All checks were successful
CI / test (push) Successful in 1m9s
This commit is contained in:
175
docs/work-items/ORCH-101/01-brd.md
Normal file
175
docs/work-items/ORCH-101/01-brd.md
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
---
|
||||||
|
work_item: ORCH-101
|
||||||
|
stage: analysis
|
||||||
|
author_agent: analyst
|
||||||
|
status: ready-for-review
|
||||||
|
created_at: 2026-06-10
|
||||||
|
model_used: claude-opus-4-8
|
||||||
|
---
|
||||||
|
|
||||||
|
# 01 — BRD (бизнес-требования): ORCH-101 — ORCH-10-common: расхардкод + секреты + smoke (фундамент тиража)
|
||||||
|
|
||||||
|
Work Item: **ORCH-101** · Repo: **orchestrator** · Стадия: analysis
|
||||||
|
|
||||||
|
## 1. Бизнес-контекст и проблема
|
||||||
|
|
||||||
|
Эпик **ORCH-10** (домен 📈 D5 «Масштаб», `docs/epics/self-evolution.md`, D5.3) — тираж платформы
|
||||||
|
для раздачи **текущей** функциональности нескольким заказчикам **на тест**. Решения Славы 10.06:
|
||||||
|
|
||||||
|
- **Два типа тиража, оба stateless** (наши задачи/данные НЕ переносятся, чистый старт):
|
||||||
|
- **Тип A (Lite):** перенос орк + watchdog на новую инфру; окружение донастраивается по чёткой
|
||||||
|
инструкции.
|
||||||
|
- **Тип B (Bundled):** весь стек (Plane/Gitea/орк) одним комплектом.
|
||||||
|
- **ORCH-101 = 10-common** — общий фундамент **обоих** типов. Без него ни Lite, ни Bundled не поедут.
|
||||||
|
|
||||||
|
**Проблема.** Платформа фактически прибита гвоздями к хосту `mva154`:
|
||||||
|
|
||||||
|
1. **Хардкоды хоста/окружения.** Аудит репо (стадия analysis, см. реестр в `02-trz.md` §3.1)
|
||||||
|
подтвердил: в `src/**` есть код, который **обходит конфиг** — внешний URL Gitea
|
||||||
|
`http://git.mva154.duckdns.org` зашит в `src/plane_sync.py`, `HOME=/home/slin` и git-идентичность
|
||||||
|
`*@mva154.local` зашиты в трёх местах (`launcher`/`self_deploy`/`post_deploy`); в
|
||||||
|
`docker-compose.yml` зашиты пути `/home/slin/...`, gid docker-группы `999`, uid `1000:1000`,
|
||||||
|
node-пути; в `Dockerfile` — `useradd … -d /home/slin slin` и порт CMD; в
|
||||||
|
`scripts/orchestrator-deploy-hook.sh` — `REPO=/home/slin/repos/orchestrator`. На новом хосте
|
||||||
|
(другой пользователь, другие пути, другой gid docker, другие URL) платформа не заведётся без
|
||||||
|
правки кода — это блокер тиража.
|
||||||
|
2. **Секреты.** Боевые секреты (`.env`) копировать на чужую инфраструктуру нельзя (общий
|
||||||
|
webhook-secret = чужой хост сможет слать нам валидные вебхуки; утечка токенов). Механизма
|
||||||
|
«сгенерировать НОВЫЙ комплект секретов на новом хосте» нет; `.env.example` не имеет чек-листа
|
||||||
|
«что обязан заполнить оператор».
|
||||||
|
3. **Верификация.** Нет процедуры убедиться, что развёрнутая копия платформы **жива**: «завести
|
||||||
|
тестовый проект + задачу → конвейер доехал». Без неё каждый тираж — слепой деплой.
|
||||||
|
|
||||||
|
**Ценность:** после ORCH-101 платформа разворачивается на новой инфре конфигурацией (env), с
|
||||||
|
собственными секретами и проверяется воспроизводимым smoke-прогоном. Это критический путь всего
|
||||||
|
эпика ORCH-10.
|
||||||
|
|
||||||
|
## 2. Объём (scope)
|
||||||
|
|
||||||
|
### В объёме
|
||||||
|
|
||||||
|
1. **Расхардкод** — вынести все хардкоды хоста/окружения в конфиг/env **с дефолтами, равными
|
||||||
|
текущим значениям**: IP/hostname (`82.22.50.71`, `mva154`, `*.duckdns.org`), пути
|
||||||
|
(`/home/slin/...`, `/repos`), порты (8500/8501/3000/8091), gid docker `999`, uid `1000`, имена
|
||||||
|
контейнеров/сервисов/образов, URL Plane/Gitea, git-идентичность агентов. Зоны:
|
||||||
|
`src/**`, `watchdog/**`, `docker-compose.yml`, `Dockerfile`, `scripts/`, `.env.example`.
|
||||||
|
Аудит — **весь репо** (нормативный реестр находок — `02-trz.md` §3.1).
|
||||||
|
2. **Секреты** — механизм генерации **новых** секретов на новом хосте (webhook-секреты —
|
||||||
|
криптослучайная генерация на месте; внешние токены Plane/Gitea/Telegram — выпуск на целевых
|
||||||
|
системах по чек-листу). Полный шаблон `.env.example` с плейсхолдерами + чек-лист «что заполнить».
|
||||||
|
Боевые секреты не покидают текущий хост.
|
||||||
|
3. **Smoke-верификация** — документированная воспроизводимая процедура (и/или скрипт-обвязка):
|
||||||
|
«завести тестовый проект + задачу → конвейер доехал», с чёткими критериями PASS/FAIL.
|
||||||
|
4. **Анти-регресс** — структурный тест (grep-тест по образцу `tests/test_agent_prompts_canon.py`),
|
||||||
|
запрещающий возврат хост-хардкодов в `src/**`.
|
||||||
|
5. **Доки** — deployment-раздел (параметризация, карта новых env-ключей, чек-лист секретов,
|
||||||
|
smoke-процедура) + обновление карты env в `docs/operations/INFRA.md` + `CHANGELOG.md`.
|
||||||
|
|
||||||
|
### Вне объёма
|
||||||
|
|
||||||
|
- **Сама инструкция тиража Типа A (Lite) end-to-end** и **сборка комплекта Типа B (Bundled)** —
|
||||||
|
отдельные задачи эпика ORCH-10; 10-common даёт им фундамент.
|
||||||
|
- **Перенос данных/задач/истории** — тираж stateless по решению Славы 10.06 (чистый старт).
|
||||||
|
- **Мультитенантность** (D5.6), горизонтальный воркер-пул (D5.4), IaC-автоматизация
|
||||||
|
(Terraform/Ansible) — не сейчас.
|
||||||
|
- **Изменение конвейера**: `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключи /
|
||||||
|
схема БД — не трогаются (задача конфигурационная, не процессная).
|
||||||
|
- **Расхардкод исторических документов** `docs/**`, `memory/**`, `CHANGELOG.md` — историческая
|
||||||
|
правда не переписывается; аудит судит **код и инфра-файлы**, не архив.
|
||||||
|
- **`tests/**`** — тестам разрешены литералы как фикстуры/ожидания.
|
||||||
|
- **Onboarding-kit** (`onboarding/`, ORCH-009) — уже параметризован плейсхолдерами `{{NAME}}`;
|
||||||
|
правится только при необходимости согласовать новые env-ключи.
|
||||||
|
|
||||||
|
## 3. Заинтересованные стороны
|
||||||
|
|
||||||
|
- **Слава (Owner)** — заказчик эпика ORCH-10; принимает BRD (гейт Approved) и прод-деплой
|
||||||
|
(Confirm Deploy).
|
||||||
|
- **Будущие заказчики-тестеры** — потребители тиража Типов A/B (получают платформу на своей инфре).
|
||||||
|
- **Стрим** — оператор тиража: выполняет инструкцию, генерирует секреты, гоняет smoke.
|
||||||
|
- **Последующие задачи эпика ORCH-10 (Lite/Bundled)** — прямые потребители фундамента.
|
||||||
|
- **Агенты конвейера / self-hosting** — изменения катятся через общий прод-инстанс; регресс на
|
||||||
|
текущем хосте недопустим (обслуживает и enduro-trails).
|
||||||
|
|
||||||
|
## 4. Бизнес-требования (BR)
|
||||||
|
|
||||||
|
- **BR-1 (расхардкод src)** — в `src/**` и `watchdog/**` не остаётся хост-специфичных литералов
|
||||||
|
(IP/hostname/пути/порты/URL/идентичности), **обходящих конфиг**: каждое такое значение читается
|
||||||
|
из `Settings` (env `ORCH_*` / `WATCHDOG_*`) с дефолтом, равным текущему боевому значению.
|
||||||
|
Дефолты в `src/config.py` / `watchdog/config.py` — легитимное место значений по умолчанию.
|
||||||
|
- **BR-2 (расхардкод инфра-файлов)** — `docker-compose.yml`, `Dockerfile`,
|
||||||
|
`scripts/orchestrator-deploy-hook.sh` параметризованы (compose-интерполяция `${VAR:-default}`,
|
||||||
|
`ARG`, env-override соответственно); без заданных переменных конфигурация резолвится в текущие
|
||||||
|
значения 1:1.
|
||||||
|
- **BR-3 (секреты)** — существует механизм получения **нового** комплекта секретов на новом хосте:
|
||||||
|
генерируемые локально (webhook-секреты) создаются криптослучайно; выпускаемые внешними системами
|
||||||
|
(токены Plane/Gitea/Telegram) перечислены в чек-листе с указанием, где их выпустить.
|
||||||
|
`.env.example` покрывает 100% обязательных ключей. Копирование боевых секретов не требуется ни
|
||||||
|
на одном шаге.
|
||||||
|
- **BR-4 (smoke)** — существует документированная воспроизводимая процедура верификации тиража:
|
||||||
|
«развёрнутый инстанс → тестовый проект + задача → конвейер доехал», с момента старта до явного
|
||||||
|
PASS/FAIL-критерия; воспроизводимость подтверждена прогоном на текущей инфре (staging-песочница
|
||||||
|
8501 / sandbox-проект).
|
||||||
|
- **BR-5 (zero-regression)** — на текущем хосте поведение 1:1: все новые параметры имеют дефолты =
|
||||||
|
сегодняшним значениям; пустой/неизменённый `.env` даёт байт-в-байт текущее поведение; полный
|
||||||
|
`pytest tests/ -q` зелёный.
|
||||||
|
- **BR-6 (анти-регресс)** — возврат хост-хардкода в `src/**` ломает CI: структурный тест
|
||||||
|
сканирует код (вне комментариев/докстрингов) на запрещённые литералы.
|
||||||
|
- **BR-7 (доки)** — deployment-раздел + карта env + чек-лист секретов + smoke-процедура
|
||||||
|
опубликованы в `docs/operations/`; `CHANGELOG.md` обновлён (правило агентов №2).
|
||||||
|
|
||||||
|
## 5. Нефункциональные требования (NFR)
|
||||||
|
|
||||||
|
- **NFR-1 (self-hosting safety)** — задача не перезапускает и не роняет прод-контейнер
|
||||||
|
`orchestrator`; правки `docker-compose.yml`/`Dockerfile` инертны до следующего штатного деплоя
|
||||||
|
через конвейер (staging 8501 → Confirm Deploy). Никаких push/force-push в `main` мимо PR.
|
||||||
|
- **NFR-2 (обратимость)** — «kill-switch природа» параметризации: отсутствие новых env-переменных
|
||||||
|
= текущее поведение. Отдельный функциональный kill-switch не требуется — дефолты и есть откат.
|
||||||
|
- **NFR-3 (секреты вне гита)** — реальные значения только в `.env`/`.env.staging` на хосте
|
||||||
|
(правило агентов №8); в гит попадают только шаблоны/плейсхолдеры; генератор секретов никогда не
|
||||||
|
перезаписывает существующий `.env` молча.
|
||||||
|
- **NFR-4 (инварианты соседних задач, правило №9)** — параметризация не ослабляет зафиксированные
|
||||||
|
инварианты: ORCH-058 «freshness-путь целится ТОЛЬКО в staging, никогда в прод 8500»; ORCH-040
|
||||||
|
«uid 1000 + group_add 999 (МИНА 1 — не удалять) + HOME согласован с маунтами»; INV-4 «мерж только
|
||||||
|
через Gitea PR-merge API». Затронутые маркеры читаются перед правкой.
|
||||||
|
- **NFR-5 (стабильность анти-регресс теста)** — grep-тест детерминирован и не флапает: судит код,
|
||||||
|
исключает комментарии/докстринги/`tests/**`/`docs/**`; список запрещённых литералов
|
||||||
|
централизован в самом тесте.
|
||||||
|
- **NFR-6 (конвейер не трогаем)** — `STAGE_TRANSITIONS`, состав `QG_CHECKS`, семантика `check_*`,
|
||||||
|
machine-verdict ключи, схема БД — байт-в-байт прежние.
|
||||||
|
|
||||||
|
## 6. Допущения и ограничения
|
||||||
|
|
||||||
|
- Целевой хост тиража: Linux + Docker + Compose; Plane и Gitea доступны (для Типа A — донастройка
|
||||||
|
по инструкции Lite-задачи; их URL/токены подаются через env). Claude CLI присутствует на хосте
|
||||||
|
(пути маунтов параметризуются).
|
||||||
|
- Тираж — **single-tenant**: один инстанс платформы на заказчика; общая мультитенантная топология
|
||||||
|
вне объёма.
|
||||||
|
- Имя self-hosting репо платформы (`orchestrator`) — платформенная конвенция; делать ли его
|
||||||
|
конфигурируемым (`SELF_HOSTING_REPO`) — решение архитектора (см. `02-trz.md` §3.4, вопрос А-2).
|
||||||
|
- Встроенный fallback-реестр проектов (`src/projects.py::_DEFAULT_PROJECTS`) содержит UUID'ы Plane
|
||||||
|
текущего хоста; на новом хосте обязателен `ORCH_PROJECTS_JSON` — фиксируется в чек-листе, сами
|
||||||
|
UUID'ы в дефолте безвредны (не сматчатся) и остаются как документированный fallback.
|
||||||
|
- Историческая документация и комментарии кода могут упоминать `mva154`/пути — это не хардкоды
|
||||||
|
поведения и аудитом кода не считаются.
|
||||||
|
|
||||||
|
## 7. Критерии успеха
|
||||||
|
|
||||||
|
Платформа разворачивается на чужой инфре **без правки кода** — только env/конфиг; секреты
|
||||||
|
выпускаются заново по чек-листу; smoke-прогон даёт явный PASS; на текущем хосте — ноль изменений
|
||||||
|
поведения и зелёный полный регресс. Детальные условия PASS/FAIL — `03-acceptance-criteria.md`
|
||||||
|
(AC-1…AC-8); прослеживаемость BR ↔ AC — в сводной матрице там же.
|
||||||
|
|
||||||
|
## 8. Риски
|
||||||
|
|
||||||
|
Кратко (детально — `10-tech-risks.md`, заполняет архитектор):
|
||||||
|
|
||||||
|
- **Р-1: скрытый регресс при параметризации горячих путей** (`launcher` env агентов, compose
|
||||||
|
volumes) — митигируется дефолтами = текущим значениям + полным регрессом + staging-прогоном.
|
||||||
|
- **Р-2: ослабление инварианта ORCH-058** при конфигуризации staging-порта (`_STAGING_PORT`) —
|
||||||
|
митигируется guard-условием «staging-порт ≠ прод-порт» и решением архитектора (возможно,
|
||||||
|
оставить константой с обоснованием).
|
||||||
|
- **Р-3: флап анти-регресс grep-теста** (ложные срабатывания на комментарии) — митигируется
|
||||||
|
правилами исключений NFR-5.
|
||||||
|
- **Р-4: неполный аудит** (пропущенный хардкод всплывает на первом реальном тираже) — митигируется
|
||||||
|
нормативным реестром §3.1 ТЗ + smoke-процедурой как последней линией обнаружения.
|
||||||
|
- **Р-5: расползание скоупа в Lite/Bundled** — митигируется явным «вне объёма» §2.
|
||||||
253
docs/work-items/ORCH-101/02-trz.md
Normal file
253
docs/work-items/ORCH-101/02-trz.md
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
---
|
||||||
|
work_item: ORCH-101
|
||||||
|
stage: analysis
|
||||||
|
author_agent: analyst
|
||||||
|
status: ready-for-review
|
||||||
|
created_at: 2026-06-10
|
||||||
|
model_used: claude-opus-4-8
|
||||||
|
---
|
||||||
|
|
||||||
|
# 02 — ТЗ (TRZ): ORCH-101 — ORCH-10-common: расхардкод + секреты + smoke (фундамент тиража)
|
||||||
|
|
||||||
|
Work Item: **ORCH-101** · Repo: **orchestrator** · Стадия: analysis
|
||||||
|
|
||||||
|
> ТЗ описывает **что** должно измениться и **где** (модули/контракты/артефакты), выведено из BRD и
|
||||||
|
> фактического кода (аудит выполнен по репо на ветке задачи). **Как** (точные имена новых
|
||||||
|
> конфиг-ключей, механика генератора секретов, форма smoke-скрипта) — решает архитектор в `06-adr/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Сводка изменения
|
||||||
|
|
||||||
|
Три слоя одного фундамента тиража (эпик ORCH-10, оба типа A/B):
|
||||||
|
|
||||||
|
1. **Расхардкод:** все хост-специфичные значения, которые сегодня **обходят конфиг** (зашиты в код
|
||||||
|
`src/**`, в `docker-compose.yml`, `Dockerfile`, `scripts/orchestrator-deploy-hook.sh`),
|
||||||
|
переводятся на чтение из `Settings` (env `ORCH_*`) / compose-интерполяцию `${VAR:-default}` /
|
||||||
|
`ARG` / env-override — **с дефолтами, равными текущим боевым значениям** (zero-regression).
|
||||||
|
Нормативный реестр находок — §3.1 (результат аудита всего репо).
|
||||||
|
2. **Секреты:** механизм выпуска **нового** комплекта секретов на новом хосте (генерация
|
||||||
|
webhook-секретов + чек-лист внешних токенов) + полнота `.env.example`.
|
||||||
|
3. **Smoke:** документированная воспроизводимая процедура «тестовый проект + задача → конвейер
|
||||||
|
доехал» с PASS/FAIL-критериями (+ анти-регресс grep-тест против возврата хардкодов).
|
||||||
|
|
||||||
|
Конвейер (`STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/verdict-ключи/схема БД) — не меняется.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Задействованные модули / пути
|
||||||
|
|
||||||
|
| Путь | Действие |
|
||||||
|
|------|----------|
|
||||||
|
| `src/config.py` | изменить: новые `Settings`-ключи (HOME агент-процессов, git-идентичность акторов, внешний Gitea-URL уже есть — `gitea_public_url`; прочее по §3.1), дефолты = текущим значениям |
|
||||||
|
| `src/plane_sync.py` | изменить: `notify_stage_change` — убрать литерал `gitea_base = "http://git.mva154.duckdns.org"` и owner `admin` из путей ссылок → `settings.gitea_public_url` (fallback `gitea_url`) + `settings.gitea_owner` |
|
||||||
|
| `src/agents/launcher.py` | изменить: env агент-процесса (×2 места) — `HOME` и git author/committer (`claude-bot@mva154.local`) из конфига |
|
||||||
|
| `src/self_deploy.py` | изменить: env detached-процесса — `HOME`, `deploy-finalizer@mva154.local` из конфига |
|
||||||
|
| `src/post_deploy.py` | изменить: env монитора — `HOME`, `post-deploy-monitor@mva154.local` из конфига |
|
||||||
|
| `src/image_freshness.py` | изменить ИЛИ обосновать: `_STAGING_PORT = 8501` (см. §3.4 А-1, инвариант ORCH-058) |
|
||||||
|
| `src/qg/checks.py` | изменить ИЛИ обосновать: `SELF_HOSTING_REPO = "orchestrator"` (см. §3.4 А-2) |
|
||||||
|
| `src/fs_normalize.py` | изменить: fallback-подсказку `sudo chown -R 1000:1000 /repos/_wt` строить из `settings` (uid/worktrees_dir) |
|
||||||
|
| `src/projects.py` | не менять логику; `_DEFAULT_PROJECTS` остаётся документированным fallback (UUID'ы чужого Plane безвредны); фиксация в чек-листе «на новом хосте обязателен `ORCH_PROJECTS_JSON`» |
|
||||||
|
| `watchdog/config.py` | проверить/синхронизировать: уже env-driven (`WATCHDOG_*`); дефолт `http://127.0.0.1:8500/metrics` согласовать с параметризацией прод-порта |
|
||||||
|
| `docker-compose.yml` | изменить: интерполяция `${VAR:-default}` для всех хост-значений (§3.1 B) |
|
||||||
|
| `Dockerfile` | изменить: `ARG` для uid/gid/username/home; CMD-порт — по решению архитектора (§3.4 А-3) |
|
||||||
|
| `scripts/orchestrator-deploy-hook.sh` | изменить: `REPO=/home/slin/repos/orchestrator` → env-override `REPO="${REPO:-…}"` |
|
||||||
|
| `scripts/` (новый) | создать: генератор секретов и/или smoke-обвязка — форма за архитектором (§3.2, §3.3) |
|
||||||
|
| `.env.example` | изменить: новые ключи, секции секретов с плейсхолдерами, чек-лист «что заполнить» |
|
||||||
|
| `.env.staging.example` | изменить: согласовать с новыми ключами (при необходимости) |
|
||||||
|
| `tests/test_no_host_hardcodes.py` (новый) | создать: структурный анти-регресс grep-тест (FR-8) |
|
||||||
|
| `tests/` (новые) | создать: тесты параметризации/секретов/smoke по `04-test-plan.yaml` |
|
||||||
|
| `docs/operations/` | создать deployment-раздел тиража (предложение: `REPLICATION.md`; имя финализирует архитектор) + обновить карту env в `INFRA.md` |
|
||||||
|
| `CHANGELOG.md`, `CLAUDE.md`, `README.md` | изменить: запись изменения; паспорт/обзорные доки — по фактическому объёму (правило агентов №2/№6) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Функциональные требования
|
||||||
|
|
||||||
|
### FR-1 — Нормативный реестр хардкодов (результат аудита) и их устранение
|
||||||
|
|
||||||
|
Привязка: BR-1, BR-2. Аудит выполнен по всему репо (grep по классам: IP/hostname, пути, порты,
|
||||||
|
gid/uid, URL, идентичности). Реестр ниже **нормативен**: developer закрывает каждую строку;
|
||||||
|
расхождение «нашёл ещё» — дополняет реестр в PR, а не игнорирует.
|
||||||
|
|
||||||
|
#### 3.1. Реестр
|
||||||
|
|
||||||
|
**A. `src/**` / `watchdog/**` — код, обходящий конфиг (блокеры AC-1):**
|
||||||
|
|
||||||
|
| # | Файл:строка | Хардкод | Требование |
|
||||||
|
|---|---|---|---|
|
||||||
|
| A1 | `src/plane_sync.py:1064` | `gitea_base = "http://git.mva154.duckdns.org"` в `notify_stage_change` + owner `admin` в построении ссылок Branch/PR | читать `settings.gitea_public_url` (fallback `settings.gitea_url`; loopback-семантика — как в `notifications._build_plane_issue_link`) + `settings.gitea_owner`; никаких новых ключей не требуется |
|
||||||
|
| A2 | `src/agents/launcher.py:594, 825` | `"HOME": "/home/slin"`; `GIT_AUTHOR/COMMITTER_EMAIL: claude-bot@mva154.local` (×2 места) | HOME и git-идентичность из конфига (новые ключи, дефолты = текущим значениям) |
|
||||||
|
| A3 | `src/self_deploy.py:332–336` | `"HOME": "/home/slin"`; `deploy-finalizer@mva154.local` | то же (единый источник с A2; имя актора может остаться per-actor) |
|
||||||
|
| A4 | `src/post_deploy.py:575–579` | `"HOME": "/home/slin"`; `post-deploy-monitor@mva154.local` | то же |
|
||||||
|
| A5 | `src/image_freshness.py:61` | `_STAGING_PORT = 8501` (модульная константа; намеренный анти-prod-инвариант ORCH-058 AC-9) | конфигуризовать с дефолтом 8501 **с сохранением инварианта** (guard «staging-порт ≠ прод-порт») ИЛИ оставить константой с явным обоснованием в ADR — решение архитектора (§3.4 А-1) |
|
||||||
|
| A6 | `src/qg/checks.py:517` | `SELF_HOSTING_REPO = "orchestrator"` (узел: на него опираются все `*_repos`-leaf'ы «empty CSV → self-hosting only») | конфиг-ключ с дефолтом `orchestrator` ИЛИ нормативная платформенная константа («репо платформы в тираже обязан называться `orchestrator`», фиксируется в deployment-доке) — решение архитектора (§3.4 А-2) |
|
||||||
|
| A7 | `src/fs_normalize.py:539` | fallback-строка подсказки `sudo chown -R 1000:1000 /repos/_wt` | строить из `settings.fs_target_uid` / `settings.worktrees_dir` (соседние строки 533–535 уже так делают) |
|
||||||
|
| A8 | `src/disk_watchdog.py:95` | fallback `["/repos", "/app/data"]` — зеркало дефолта `settings.disk_monitor_paths` | допустимое config-backed зеркало; не блокер. Требование: значения остаются синхронными с конфиг-дефолтом (одна точка истины желательна — на усмотрение архитектора) |
|
||||||
|
| A9 | `src/projects.py:42–55` | `_DEFAULT_PROJECTS`: UUID'ы Plane текущего хоста | НЕ блокер (документированный fallback «out of the box», чужие UUID не сматчатся). Требование: чек-лист тиража обязывает задать `ORCH_PROJECTS_JSON` |
|
||||||
|
| A10 | `watchdog/config.py:100,144` | дефолт `http://127.0.0.1:8500/metrics` | уже env-driven (`WATCHDOG_METRICS_URL`); дефолт легитимен; согласовать с А-3 (прод-порт), если тот станет параметром |
|
||||||
|
|
||||||
|
> Паттерн `getattr(settings, "x", <литерал>)` (A7/A8 и др.) — **config-backed fallback**, не
|
||||||
|
> хардкод-блокер: значение управляется конфигом. Блокер — только код, который конфиг **обходит**.
|
||||||
|
|
||||||
|
**B. `docker-compose.yml` — хост-специфика (блокеры AC-6):**
|
||||||
|
|
||||||
|
| # | Место | Хардкод |
|
||||||
|
|---|---|---|
|
||||||
|
| B1 | volumes ×3 сервисов | `/home/slin/repos:/repos`(+`:ro`), `/home/slin/.claude`, `/home/slin/.claude.json`, `/home/slin/.orchestrator-ssh:/home/slin/.ssh` |
|
||||||
|
| B2 | volumes ×2 сервисов | `/usr/lib/node_modules/@anthropic-ai/claude-code:/opt/claude-code:ro`, `/usr/bin/node:/usr/bin/node:ro` |
|
||||||
|
| B3 | `group_add` ×3 | `"999"` (gid docker-группы хоста) |
|
||||||
|
| B4 | `user:` ×2 | `"1000:1000"` (uid:gid хоста) |
|
||||||
|
| B5 | environment | `ORCH_HOST_REPOS_DIR=/home/slin/repos` ×2, `ORCH_DEPLOY_HOST_REPO_PATH=/home/slin/repos/orchestrator`, `DEPLOY_SSH_USER=slin` ×2, `ORCH_DEPLOY_SSH_USER=slin`, `DEPLOY_HOOK_SCRIPT=/home/slin/bin/enduro-deploy-hook.sh` ×2 (legacy enduro) |
|
||||||
|
| B6 | `orchestrator-staging.command` | порт `8501` |
|
||||||
|
|
||||||
|
Требование: compose-интерполяция `${VAR:-default}` (источник — `.env`, штатный механизм Compose);
|
||||||
|
`docker compose config` без заданных переменных резолвится в текущие значения 1:1. Целевая
|
||||||
|
семантика «HOME внутри контейнера согласован с маунтами `.claude`/`.ssh` и `useradd` Dockerfile»
|
||||||
|
(инвариант ORCH-040) сохраняется как согласованная группа переменных.
|
||||||
|
|
||||||
|
**C. `Dockerfile`:**
|
||||||
|
|
||||||
|
| # | Место | Хардкод |
|
||||||
|
|---|---|---|
|
||||||
|
| C1 | `useradd -u 1000 -g 1000 -m -d /home/slin -s /bin/bash slin` | uid/gid/home/username |
|
||||||
|
| C2 | `CMD … --port 8500` | прод-порт |
|
||||||
|
|
||||||
|
Требование: C1 → `ARG` с дефолтами (1000/1000//home/slin/slin). C2 — по решению архитектора
|
||||||
|
(§3.4 А-3): порт уже переопределяем через compose `command:`; допустимо оставить CMD-дефолт 8500.
|
||||||
|
|
||||||
|
**D. `scripts/`:**
|
||||||
|
|
||||||
|
| # | Место | Хардкод |
|
||||||
|
|---|---|---|
|
||||||
|
| D1 | `orchestrator-deploy-hook.sh:38` | `REPO=/home/slin/repos/orchestrator` (единственный непараметризованный путь скрипта; остальное уже env-override) |
|
||||||
|
|
||||||
|
Требование: `REPO="${REPO:-/home/slin/repos/orchestrator}"` (паттерн остальных переменных скрипта).
|
||||||
|
`scripts/staging_check.py` — уже env-driven (`ORCH_PLANE_API_URL`/`ORCH_GITEA_URL`/`--base-url`),
|
||||||
|
изменений не требует.
|
||||||
|
|
||||||
|
**E. Уже параметризовано (НЕ трогать, фиксация аудита):** дефолты `src/config.py`
|
||||||
|
(`plane_api_url:8091`, `gitea_url:3000`, `repos_dir`, `host_repos_dir`, `worktrees_dir`, `runs_dir`,
|
||||||
|
`db_path`, `deploy_ssh_user`, `deploy_host_repo_path`, `deploy_prod_target_port:8500`,
|
||||||
|
`post_deploy_base_url`, `disk_monitor_paths`, `claude_bin`) — легитимные значения по умолчанию,
|
||||||
|
управляемые env; менять их значения запрещено (BR-5).
|
||||||
|
|
||||||
|
### FR-2 — Чтение хост-значений из конфига в `src/**`
|
||||||
|
|
||||||
|
Привязка: BR-1. Каждый блокер класса A (A1–A4, A7; A5/A6 — по исходу §3.4) читает значение через
|
||||||
|
`settings` (существующие либо новые ключи `Settings` с env `ORCH_*`). Требования к новым ключам:
|
||||||
|
|
||||||
|
- дефолт = текущее боевое значение (BR-5): `/home/slin`, `claude-bot`, `*@mva154.local`-адреса;
|
||||||
|
- описательный комментарий в `config.py` по образцу существующих блоков (назначение + env-имя);
|
||||||
|
- ключи отражены в `.env.example` и карте env `INFRA.md`;
|
||||||
|
- точные имена ключей и группировка (например, единый `agent_home_dir` + per-actor идентичности
|
||||||
|
или общий email-домен) — решение архитектора; ТЗ фиксирует контракт: **ни один из пяти акторов
|
||||||
|
(агенты CLI ×2 места, self-deploy finalizer, post-deploy monitor, fs-подсказка) не содержит
|
||||||
|
литералов `/home/slin` / `mva154` после изменения**.
|
||||||
|
|
||||||
|
### FR-3 — Параметризация инфра-файлов
|
||||||
|
|
||||||
|
Привязка: BR-2. Реестр B/C/D закрыт; механика — compose-интерполяция / `ARG` / shell-default.
|
||||||
|
Инварианты: ORCH-040 (uid 1000 + `group_add` docker-gid + HOME-согласованность маунтов — группа
|
||||||
|
переменных меняется согласованно, «МИНА 1» `group_add` не удаляется), ORCH-058 (staging-сервис
|
||||||
|
никогда не резолвится в прод-таргет). Поведение `docker compose config` при пустом окружении —
|
||||||
|
эквивалент текущего файла (проверяется тестом TC-06).
|
||||||
|
|
||||||
|
### FR-4 — Механизм секретов нового хоста
|
||||||
|
|
||||||
|
Привязка: BR-3. Состав:
|
||||||
|
|
||||||
|
1. **Инвентаризация** (фиксация аудита): генерируемые локально — `ORCH_PLANE_WEBHOOK_SECRET`,
|
||||||
|
`ORCH_GITEA_WEBHOOK_SECRET`; выпускаемые внешними системами — `ORCH_PLANE_API_TOKEN`,
|
||||||
|
`ORCH_PLANE_BOT_*` (7 шт., опциональны: fallback на `ORCH_PLANE_API_TOKEN`), `ORCH_GITEA_TOKEN`,
|
||||||
|
`ORCH_TELEGRAM_BOT_TOKEN` (+ несекретный `ORCH_TELEGRAM_CHAT_ID`).
|
||||||
|
2. **Генерация:** webhook-секреты создаются криптослучайно (стандарт: `secrets`-модуль Python /
|
||||||
|
эквивалент, длина ≥ 32 байт энтропии) на целевом хосте; форма (отдельный
|
||||||
|
`scripts/gen_secrets.py`, режим onboarding-CLI или документированная команда) — решение
|
||||||
|
архитектора. Требования к поведению: повторный запуск даёт другие значения; существующий `.env`
|
||||||
|
никогда не перезаписывается молча (NFR-3); вывод согласован с ключами `.env.example`.
|
||||||
|
3. **Чек-лист** в deployment-доке: для каждого секрета — где выпустить (Plane UI/API, Gitea UI,
|
||||||
|
BotFather), куда вписать, как проверить. Явное правило: «боевые секреты текущего хоста не
|
||||||
|
копируются».
|
||||||
|
4. **Полнота `.env.example`:** каждый обязательный для старта ключ присутствует (с плейсхолдером
|
||||||
|
или дефолтом), включая новые ключи FR-2/FR-3; секретные значения — только плейсхолдеры.
|
||||||
|
|
||||||
|
### FR-5 — Smoke-верификация тиража
|
||||||
|
|
||||||
|
Привязка: BR-4. Документированная процедура (deployment-раздел) + опциональная скрипт-обвязка
|
||||||
|
(форма — архитектор; кандидаты-кирпичи уже в репо: `scripts/onboard_project.py` `plan/apply/verify`,
|
||||||
|
`scripts/staging_check.py`, `GET /health` / `/queue` / `/metrics`):
|
||||||
|
|
||||||
|
1. **Шаги:** поднять инстанс (env+секреты заполнены) → `GET /health` ок → завести тестовый
|
||||||
|
проект (onboarding-CLI `verify` или sandbox-проект) → создать тестовую задачу → убедиться, что
|
||||||
|
конвейер «доехал» (задача продвигается по стадиям; минимальный обязательный сигнал — стадия
|
||||||
|
`analysis` отработала и артефакты `01–04` созданы; полный прогон до `done` — расширенный режим).
|
||||||
|
2. **Критерии:** каждый шаг имеет явный PASS/FAIL; итог процедуры — однозначный вердикт.
|
||||||
|
3. **Воспроизводимость:** процедура прогоняется на текущей инфре (staging-песочница 8501 +
|
||||||
|
sandbox-проект) без нового железа — это и приёмочная проверка AC-3.
|
||||||
|
4. **Stateless:** процедура нигде не предполагает перенос данных/БД с боевого хоста.
|
||||||
|
|
||||||
|
### FR-6 — Анти-регресс структурный тест
|
||||||
|
|
||||||
|
Привязка: BR-6. Новый `tests/test_no_host_hardcodes.py` (образец — `tests/test_agent_prompts_canon.py`):
|
||||||
|
|
||||||
|
- сканирует `src/**/*.py` и `watchdog/**/*.py` на запрещённые литералы: `82.22.50.71`,
|
||||||
|
`/home/slin`, `mva154`, `duckdns` (список централизован в тесте; расширяем);
|
||||||
|
- судит **код**: строки-комментарии и докстринги исключаются (NFR-5; механика исключения —
|
||||||
|
прагматичная построчная/AST — за developer);
|
||||||
|
- `tests/**`, `docs/**`, `.env.example` — вне зоны сканирования;
|
||||||
|
- допускает точечный явный allowlist (файл:литерал) с комментарием-обоснованием — пустой на
|
||||||
|
момент сдачи задачи (все блокеры A закрыты).
|
||||||
|
|
||||||
|
### FR-7 — Документация
|
||||||
|
|
||||||
|
Привязка: BR-7. Deployment-раздел в `docs/operations/` (предложение: `REPLICATION.md`): карта
|
||||||
|
новых env-переменных (включая compose-интерполяцию), процедура секретов (FR-4), smoke-процедура
|
||||||
|
(FR-5), границы «10-common vs Lite vs Bundled». Обновления: `INFRA.md` (карта env),
|
||||||
|
`CHANGELOG.md`; `CLAUDE.md`/`README.md` — по фактическому объёму изменений (правило №2/№6).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4. Вопросы архитектору (решаются в `06-adr/`, не в ТЗ)
|
||||||
|
|
||||||
|
- **А-1:** `_STAGING_PORT = 8501` (`image_freshness`) — конфиг-ключ с guard'ом «≠ прод-порт» или
|
||||||
|
нормативная константа? Инвариант ORCH-058 AC-9 («никогда не прод 8500») обязан сохраниться.
|
||||||
|
- **А-2:** `SELF_HOSTING_REPO = "orchestrator"` — конфиг-ключ или платформенная конвенция тиража
|
||||||
|
(репо платформы всегда `orchestrator`)? На него завязаны все `*_repos`-leaf'ы.
|
||||||
|
- **А-3:** CMD-порт Dockerfile (8500) — `ARG` или остаётся (порт уже переопределяем compose
|
||||||
|
`command:`)?
|
||||||
|
- **А-4:** форма генератора секретов (отдельный скрипт / режим `onboard_project.py` /
|
||||||
|
документированная команда) и форма smoke (чистый runbook / runbook + скрипт).
|
||||||
|
- **А-5:** группировка новых конфиг-ключей FR-2 (единый HOME-ключ + per-actor email'ы vs общий
|
||||||
|
email-домен).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Изменения API
|
||||||
|
|
||||||
|
Нет. Существующие эндпоинты (`/health`, `/queue`, `/metrics`) переиспользуются smoke-процедурой
|
||||||
|
read-only; новые эндпоинты не вводятся.
|
||||||
|
|
||||||
|
## 5. Изменения схемы БД
|
||||||
|
|
||||||
|
Нет.
|
||||||
|
|
||||||
|
## 6. Требования к новым/изменённым QG checks
|
||||||
|
|
||||||
|
Нет. `QG_CHECKS`/`check_*`/machine-verdict ключи не меняются. Новый структурный тест (FR-6) —
|
||||||
|
обычный pytest: попадает в существующие гейты `check_ci_green`/`check_tests_passed`/merge-gate
|
||||||
|
re-test автоматически, без регистрации нового QG.
|
||||||
|
|
||||||
|
## 7. Совместимость / регресс
|
||||||
|
|
||||||
|
- **Zero-regression (BR-5):** все новые параметры — с дефолтами, равными сегодняшним боевым
|
||||||
|
значениям; пустой/неизменённый `.env` ⇒ байт-в-байт текущее поведение; `docker compose config`
|
||||||
|
без переменных ⇒ эквивалент текущего файла. Полный `pytest tests/ -q` зелёный.
|
||||||
|
- **Обратимость (NFR-2):** откат = не задавать новые переменные. Функциональный kill-switch не
|
||||||
|
требуется (дефолты и есть прежнее поведение); вводить новый флаг ради флага запрещено.
|
||||||
|
- **Область раската:** изменения инертны для enduro-trails (значения те же); compose/Dockerfile
|
||||||
|
вступают в силу только при следующем штатном деплое через конвейер (staging 8501 → ручной
|
||||||
|
Confirm Deploy) — прод-контейнер в рамках задачи не рестартуется (NFR-1).
|
||||||
|
- **Инварианты соседних маркеров (правило №9):** ORCH-040 (uid/gid/HOME/«МИНА 1»), ORCH-058
|
||||||
|
(freshness → только staging), ORCH-036/059 (self-deploy фазы, Confirm Deploy), INV-4 (мерж только
|
||||||
|
через PR-merge API) — сохраняются; соответствующие ADR прочитаны при правке помеченных блоков.
|
||||||
131
docs/work-items/ORCH-101/03-acceptance-criteria.md
Normal file
131
docs/work-items/ORCH-101/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
---
|
||||||
|
work_item: ORCH-101
|
||||||
|
stage: analysis
|
||||||
|
author_agent: analyst
|
||||||
|
status: ready-for-review
|
||||||
|
created_at: 2026-06-10
|
||||||
|
model_used: claude-opus-4-8
|
||||||
|
---
|
||||||
|
|
||||||
|
# 03 — Критерии приёмки (Acceptance Criteria): ORCH-101 — ORCH-10-common: расхардкод + секреты + smoke
|
||||||
|
|
||||||
|
Work Item: **ORCH-101** · Repo: **orchestrator** · Стадия: analysis
|
||||||
|
|
||||||
|
Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL**
|
||||||
|
(что считается провалом). Любой машинный/ручной reviewer проверяет их буквально по файлам
|
||||||
|
репозитория. AC-1…AC-4 — дословно из бизнес-запроса (уточнены до проверяемости); AC-5…AC-8 —
|
||||||
|
детализация скоупа (секреты/инфра/анти-регресс/инварианты).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-1 — Ноль хардкодов хоста/путей/портов в `src/**`
|
||||||
|
|
||||||
|
**Условие:** в коде `src/**` и `watchdog/**` (вне комментариев/докстрингов) нет хост-специфичных
|
||||||
|
литералов, обходящих конфиг; всё читается из env/конфига с дефолтами. Проверка — grep-тестом
|
||||||
|
(`tests/test_no_host_hardcodes.py`) и ревью по реестру `02-trz.md` §3.1.
|
||||||
|
- **PASS:**
|
||||||
|
- блокеры A1–A4, A7 реестра закрыты: `grep -rn "git.mva154.duckdns.org\|/home/slin\|mva154.local" src/ watchdog/` не находит ни одного вхождения в исполняемом коде (докстринги/комментарии — допустимы);
|
||||||
|
- `src/plane_sync.py::notify_stage_change` строит ссылки Branch/PR из `settings.gitea_public_url` (fallback `gitea_url`) и `settings.gitea_owner`;
|
||||||
|
- env-словари акторов (`launcher` ×2, `self_deploy`, `post_deploy`) берут HOME и git-идентичность из `settings`;
|
||||||
|
- A5 (`_STAGING_PORT`) и A6 (`SELF_HOSTING_REPO`) либо конфигуризованы, либо явно обоснованы в ADR задачи как платформенные константы (решение архитектора зафиксировано);
|
||||||
|
- структурный тест `tests/test_no_host_hardcodes.py` существует, его allowlist пуст (или каждая запись обоснована комментарием), тест зелёный.
|
||||||
|
- **FAIL:** хотя бы один литерал `82.22.50.71` / `/home/slin` / `mva154` / `duckdns` в исполняемом коде `src/**`/`watchdog/**`; ИЛИ ссылка в Plane-комментарии всё ещё строится от захардкоженного `http://git.mva154.duckdns.org`; ИЛИ A5/A6 не конфигуризованы и не обоснованы в ADR; ИЛИ анти-регресс тест отсутствует/красный.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-2 — Без регресса: на текущем хосте поведение 1:1
|
||||||
|
|
||||||
|
**Условие:** дефолты всех новых параметров равны текущим боевым значениям; pytest зелёный.
|
||||||
|
- **PASS:**
|
||||||
|
- каждый новый `Settings`-ключ / compose-переменная / `ARG` / shell-default имеет дефолт, равный значению, зашитому до задачи (`/home/slin`, `claude-bot@mva154.local`-адреса, gid 999, uid 1000, порты 8500/8501, пути node/claude-code и т.д.);
|
||||||
|
- `docker compose config` без заданных переменных окружения резолвится в эквивалент текущей конфигурации (volumes/user/group_add/environment/command совпадают по значениям);
|
||||||
|
- значения существующих дефолтов `src/config.py` (реестр §3.1 E) не изменены;
|
||||||
|
- полный `pytest tests/ -q` зелёный;
|
||||||
|
- `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключи / схема БД — без изменений (диф не затрагивает их семантику).
|
||||||
|
- **FAIL:** хотя бы один дефолт отличается от текущего боевого значения; ИЛИ `docker compose config` при пустом окружении даёт иную эффективную конфигурацию; ИЛИ любой существующий тест красный; ИЛИ диф меняет машину стадий/реестр QG/схему БД.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-3 — Smoke-процедура задокументирована и воспроизводима
|
||||||
|
|
||||||
|
**Условие:** существует документированная процедура «развёрнутый инстанс → тестовый проект +
|
||||||
|
задача → конвейер доехал» с явными PASS/FAIL-критериями; воспроизводимость подтверждена.
|
||||||
|
- **PASS:**
|
||||||
|
- в `docs/operations/` есть раздел/документ (предложение ТЗ: `REPLICATION.md`) с пошаговой smoke-процедурой: health-check инстанса → тестовый проект → тестовая задача → подтверждение продвижения конвейера (минимум: `analysis` отработала, артефакты `01–04` созданы; расширенный режим — до `done`);
|
||||||
|
- каждый шаг имеет явный ожидаемый результат (PASS/FAIL), итог — однозначный вердикт;
|
||||||
|
- процедура не требует переноса данных/секретов с боевого хоста (stateless);
|
||||||
|
- воспроизводимость подтверждена хотя бы одним прогоном на текущей инфре (staging-песочница 8501 / sandbox-проект) — результат зафиксирован в артефактах задачи (например, `13-test-report.md` / `15-staging-log.md`);
|
||||||
|
- если введена скрипт-обвязка (`scripts/…`) — она запускается (`--help`/dry-run без ошибок) и покрыта тестом из `04-test-plan.yaml`.
|
||||||
|
- **FAIL:** процедуры нет; ИЛИ шаги без явных критериев («посмотреть, что всё ок»); ИЛИ процедура требует копирования боевых данных/секретов; ИЛИ заявленный прогон не зафиксирован; ИЛИ скрипт-обвязка падает на запуске.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-4 — Доки (deployment-раздел) + CHANGELOG
|
||||||
|
|
||||||
|
**Условие:** документация обновлена в том же PR (правило агентов №2; reviewer проверяет — №6).
|
||||||
|
- **PASS:**
|
||||||
|
- deployment-раздел (см. AC-3) дополнительно содержит: карту всех новых env-переменных (имя, назначение, дефолт), процедуру/чек-лист секретов (см. AC-5), границы «10-common vs Lite vs Bundled»;
|
||||||
|
- карта переменных окружения в `docs/operations/INFRA.md` дополнена новыми ключами;
|
||||||
|
- `CHANGELOG.md` содержит запись ORCH-101;
|
||||||
|
- `CLAUDE.md`/`README.md` обновлены, если фактический объём изменений того требует (новые операторские способности/ограничения).
|
||||||
|
- **FAIL:** новый env-ключ отсутствует в карте env; ИЛИ нет записи в `CHANGELOG.md`; ИЛИ deployment-раздел не покрывает секреты/smoke; ИЛИ README выдаёт решённое за открытое (правило №6).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-5 — Секреты: генерация новых, не копирование боевых
|
||||||
|
|
||||||
|
**Условие:** механизм выпуска нового комплекта секретов на новом хосте существует и безопасен.
|
||||||
|
- **PASS:**
|
||||||
|
- webhook-секреты (`ORCH_PLANE_WEBHOOK_SECRET`, `ORCH_GITEA_WEBHOOK_SECRET`) генерируются криптослучайно (≥ 32 байт энтропии; повторный запуск даёт другие значения);
|
||||||
|
- механизм никогда не перезаписывает существующий `.env` молча;
|
||||||
|
- чек-лист перечисляет все внешние токены (`ORCH_PLANE_API_TOKEN`, `ORCH_PLANE_BOT_*`, `ORCH_GITEA_TOKEN`, `ORCH_TELEGRAM_BOT_TOKEN`) с указанием, где их выпустить и куда вписать; явно сказано «боевые секреты не копируются»;
|
||||||
|
- `.env.example` покрывает 100% ключей, обязательных для старта (включая новые из AC-1/AC-2), секретные значения — только плейсхолдеры; реальные секреты в гит не попадают (`.gitleaks`/security-гейт зелёный);
|
||||||
|
- `.env.staging.example` согласован (если затронут).
|
||||||
|
- **FAIL:** секрет генерируется детерминированно/слабо; ИЛИ запуск механизма затирает существующий `.env`; ИЛИ в `.env.example` отсутствует обязательный ключ; ИЛИ в гит закоммичено реальное секретное значение; ИЛИ процедура предписывает копирование боевого секрета.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-6 — Инфра-файлы параметризованы
|
||||||
|
|
||||||
|
**Условие:** `docker-compose.yml`, `Dockerfile`, `scripts/orchestrator-deploy-hook.sh` не требуют
|
||||||
|
правки под новый хост — только переменные.
|
||||||
|
- **PASS:**
|
||||||
|
- реестр §3.1 B/C/D закрыт: пути `/home/slin/...`, gid `999`, uid `1000:1000`, node/claude-code-пути, ssh-user, staging-порт в `command:`, `REPO=` в deploy-hook — параметризованы (`${VAR:-default}` / `ARG` / `"${REPO:-…}"`) с дефолтами = текущим значениям;
|
||||||
|
- связка «uid/gid/HOME/маунты `.claude`+`.ssh`/`useradd`» меняется согласованной группой переменных (инвариант ORCH-040 сохранён, `group_add` docker-gid не удалён);
|
||||||
|
- структурный тест параметризации (TC-06/TC-12 из `04-test-plan.yaml`) зелёный.
|
||||||
|
- **FAIL:** хотя бы одно значение реестра B/C/D осталось непараметризованным; ИЛИ группа ORCH-040 рассогласована (HOME ≠ маунт-таргеты при дефолтах); ИЛИ `group_add` удалён; ИЛИ структурный тест красный.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-7 — Анти-регресс защита от возврата хардкодов
|
||||||
|
|
||||||
|
**Условие:** возврат хост-хардкода в `src/**` ломает CI.
|
||||||
|
- **PASS:** `tests/test_no_host_hardcodes.py` существует; сканирует `src/**`+`watchdog/**` на централизованный список запрещённых литералов (минимум: `82.22.50.71`, `/home/slin`, `mva154`, `duckdns`); исключает комментарии/докстринги/`tests/**`/`docs/**`; детерминирован (повторные прогоны стабильны); демонстрационно ловит подсадку литерала (негативная самопроверка в самом тесте или в тестах теста).
|
||||||
|
- **FAIL:** тест отсутствует; ИЛИ не ловит подсаженный в код литерал из списка; ИЛИ флапает; ИЛИ список литералов размазан по нескольким местам без единой точки правки.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AC-8 — Self-hosting безопасность и инварианты соседних задач
|
||||||
|
|
||||||
|
**Условие:** задача не дестабилизирует общий прод и не ослабляет зафиксированные инварианты.
|
||||||
|
- **PASS:**
|
||||||
|
- в рамках задачи прод-контейнер `orchestrator` не перезапускается; прод-деплой — только штатно (staging 8501 → ручной `Confirm Deploy`);
|
||||||
|
- инвариант ORCH-058 сохранён: freshness/staging-путь не может быть переконфигурирован в прод-таргет (guard «staging-порт ≠ прод-порт» при конфигуризации A5 — либо A5 остался константой по ADR);
|
||||||
|
- INV-4 сохранён (никаких push/force-push в `main` мимо PR-merge API);
|
||||||
|
- маркеры `ORCH-NNN` в правленых блоках сохранены/обновлены корректно (правило №9).
|
||||||
|
- **FAIL:** диф содержит рестарт/останов прод-контейнера вне штатного деплой-пути; ИЛИ конфигурацией можно нацелить staging-rebuild на прод-порт; ИЛИ нарушен INV-4; ИЛИ правка помеченного блока стёрла инвариант соседнего ADR.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сводная матрица AC ↔ BR/FR
|
||||||
|
|
||||||
|
| AC | Покрывает |
|
||||||
|
|----|-----------|
|
||||||
|
| AC-1 | BR-1 / FR-1, FR-2, FR-6 |
|
||||||
|
| AC-2 | BR-5 / FR-1 (реестр E), FR-2, FR-3, NFR-6 |
|
||||||
|
| AC-3 | BR-4 / FR-5 |
|
||||||
|
| AC-4 | BR-7 / FR-7 |
|
||||||
|
| AC-5 | BR-3 / FR-4, NFR-3 |
|
||||||
|
| AC-6 | BR-2 / FR-3, NFR-4 |
|
||||||
|
| AC-7 | BR-6 / FR-6, NFR-5 |
|
||||||
|
| AC-8 | NFR-1, NFR-2, NFR-4 / FR-3 |
|
||||||
146
docs/work-items/ORCH-101/04-test-plan.yaml
Normal file
146
docs/work-items/ORCH-101/04-test-plan.yaml
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
work_item: ORCH-101
|
||||||
|
stage: analysis
|
||||||
|
author_agent: analyst
|
||||||
|
status: ready-for-review
|
||||||
|
created_at: 2026-06-10
|
||||||
|
model_used: claude-opus-4-8
|
||||||
|
title: "ORCH-10-common: расхардкод + секреты + smoke — план тестов"
|
||||||
|
framework: pytest
|
||||||
|
scope: >
|
||||||
|
Покрывается: расхардкод src/**+watchdog/** (чтение хост-значений из Settings),
|
||||||
|
параметризация docker-compose.yml/Dockerfile/deploy-hook (структурные проверки),
|
||||||
|
полнота .env.example, поведение генератора секретов, запускаемость smoke-обвязки,
|
||||||
|
анти-регресс grep-тест и его негативная самопроверка, zero-regression (полный
|
||||||
|
регресс tests/). Вне покрытия: реальный e2e-тираж на новом железе (заменён
|
||||||
|
воспроизводимым прогоном smoke-процедуры на staging-песочнице 8501 — AC-3),
|
||||||
|
задачи Lite/Bundled эпика ORCH-10, перенос данных (stateless по решению 10.06).
|
||||||
|
notes: >
|
||||||
|
Имена новых тест-модулей — предложение analyst; developer может переименовать,
|
||||||
|
сохранив покрытие TC. Дефолты всех новых параметров обязаны равняться текущим
|
||||||
|
боевым значениям (AC-2) — тесты фиксируют это явно. Анти-регресс тест судит
|
||||||
|
исполняемый код (комментарии/докстринги исключены) — образец структурных тестов:
|
||||||
|
tests/test_agent_prompts_canon.py. Полный регресс tests/ должен оставаться
|
||||||
|
зелёным; STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict не меняются —
|
||||||
|
новые тесты входят в существующие гейты (check_ci_green) автоматически.
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- id: TC-01
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Анти-регресс скан: в исполняемом коде src/** и watchdog/** нет запрещённых
|
||||||
|
литералов (82.22.50.71, /home/slin, mva154, duckdns); комментарии/докстринги
|
||||||
|
исключаются; список литералов централизован; allowlist пуст либо каждая
|
||||||
|
запись обоснована (AC-1, AC-7 / FR-6).
|
||||||
|
module: tests/test_no_host_hardcodes.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-02
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Негативная самопроверка сканера: подсаженный во временный/фиктивный модуль
|
||||||
|
запрещённый литерал детектируется (сканер реально ловит, не вечно-зелёный)
|
||||||
|
(AC-7 / FR-6, NFR-5).
|
||||||
|
module: tests/test_no_host_hardcodes.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-03
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
plane_sync.notify_stage_change строит ссылки Branch/PR из
|
||||||
|
settings.gitea_public_url (fallback gitea_url) + settings.gitea_owner;
|
||||||
|
литерал git.mva154.duckdns.org и owner admin из кода удалены; при
|
||||||
|
переопределённых настройках ссылка меняется соответственно (monkeypatch,
|
||||||
|
без сети) (AC-1 / FR-1 A1, FR-2).
|
||||||
|
module: tests/test_host_config_keys.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-04
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Env агент-процесса в agents/launcher (оба места запуска): HOME и
|
||||||
|
GIT_AUTHOR/COMMITTER_NAME/EMAIL берутся из settings; дефолты равны прежним
|
||||||
|
значениям (/home/slin, claude-bot@mva154.local); переопределение env-ключей
|
||||||
|
меняет словарь окружения (AC-1, AC-2 / FR-1 A2, FR-2).
|
||||||
|
module: tests/test_host_config_keys.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-05
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Env self_deploy (deploy-finalizer) и post_deploy (monitor): HOME и
|
||||||
|
git-идентичность из settings, дефолты = прежним значениям; литералы
|
||||||
|
/home/slin и *@mva154.local из кода удалены (AC-1, AC-2 / FR-1 A3–A4, FR-2).
|
||||||
|
module: tests/test_host_config_keys.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-06
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Структурная проверка docker-compose.yml: хост-значения реестра ТЗ §3.1 B
|
||||||
|
(пути /home/slin/*, node/claude-code-пути, gid 999, uid 1000:1000, ssh-user,
|
||||||
|
staging-порт в command) выражены интерполяцией ${VAR:-default}, и дефолты
|
||||||
|
равны текущим значениям; group_add docker-gid присутствует во всех трёх
|
||||||
|
сервисах (инвариант ORCH-040 «МИНА 1») (AC-2, AC-6 / FR-3).
|
||||||
|
module: tests/test_infra_parametrization.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-07
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Структурная проверка Dockerfile: uid/gid/username/home параметризованы через
|
||||||
|
ARG с дефолтами 1000/1000/slin//home/slin; решение по CMD-порту соответствует
|
||||||
|
ADR задачи (ARG либо обоснованный дефолт 8500) (AC-2, AC-6 / FR-3, §3.4 А-3).
|
||||||
|
module: tests/test_infra_parametrization.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-08
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Полнота .env.example: каждый обязательный для старта ключ присутствует,
|
||||||
|
включая ВСЕ новые ключи FR-2/FR-3 (сверка с Settings.model_fields по
|
||||||
|
выбранному архитектором контракту); секретные ключи содержат только
|
||||||
|
плейсхолдеры, не реальные значения; deploy-hook принимает REPO через
|
||||||
|
env-override (AC-5, AC-6 / FR-1 D1, FR-4.4).
|
||||||
|
module: tests/test_infra_parametrization.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-09
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Генератор секретов: два запуска дают различные значения; длина/энтропия
|
||||||
|
webhook-секретов >= 32 байт; существующий .env никогда не перезаписывается
|
||||||
|
молча (запуск при существующем файле -> отказ/явное подтверждение);
|
||||||
|
вывод согласован с ключами .env.example (AC-5 / FR-4, NFR-3).
|
||||||
|
module: tests/test_secrets_gen.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-10
|
||||||
|
type: integration
|
||||||
|
description: >
|
||||||
|
Smoke-обвязка и процедура: deployment-док с пошаговой smoke-процедурой
|
||||||
|
существует и содержит явные PASS/FAIL-критерии каждого шага; если введён
|
||||||
|
скрипт — он запускается без ошибок в безопасном режиме (--help/dry-run, без
|
||||||
|
сети/LLM-расходов); карта env в INFRA.md дополнена; CHANGELOG.md содержит
|
||||||
|
запись ORCH-101 (AC-3, AC-4 / FR-5, FR-7).
|
||||||
|
module: tests/test_replication_smoke.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-11
|
||||||
|
type: unit
|
||||||
|
description: >
|
||||||
|
Инвариант ORCH-058 при исходе А-1: если staging-порт стал конфигуром —
|
||||||
|
дефолт 8501, guard «staging-порт != прод-порт» отвергает совпадение
|
||||||
|
(freshness-путь невозможно нацелить на прод); если остался константой —
|
||||||
|
тест фиксирует константу 8501 и наличие обоснования в ADR задачи
|
||||||
|
(AC-8 / FR-1 A5, NFR-4).
|
||||||
|
module: tests/test_host_config_keys.py
|
||||||
|
expected: PASS
|
||||||
|
|
||||||
|
- id: TC-12
|
||||||
|
type: integration
|
||||||
|
description: >
|
||||||
|
Полный регресс: pytest tests/ -q зелёный на дефолтной конфигурации (пустой
|
||||||
|
env) — поведение платформы на текущем хосте 1:1; существующие тесты не
|
||||||
|
правились под задачу (кроме согласованных структурных) (AC-2 / BR-5, NFR-6).
|
||||||
|
module: tests/
|
||||||
|
expected: PASS
|
||||||
Reference in New Issue
Block a user