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