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