---
work_item: ORCH-104
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-12
model_used: claude-opus-4-8
---

# 03 — Критерии приёмки (Acceptance Criteria): ORCH-104 — Установочный скрипт для Lite

Work Item: **ORCH-104** · Repo: **orchestrator** · Стадия: analysis

Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** (что
считается провалом). Reviewer/tester проверяют их буквально по файлам репозитория и поведению
скрипта (фейки HTTP/процессов — в тестах; сеть/docker в unit не нужны).

> Область: автоматизация LITE_SETUP.md §2–§11 одним установщиком (scripts+docs+tests). Подъём самих
> Plane/Gitea (Bundled), teardown, правки рантайма — вне области (см. 01-brd §2).

---

## AC-1 — Единый entry-point и режимы

**Условие:** `python3 scripts/install_lite.py` (дефолт-режим `plan`) запущен из корня чекаута.
- **PASS:** печатается нормативный план шагов + read-only preflight-диагностика; **ноль мутаций**
  (никаких записей в `.env`/`.env.watchdog`, никаких `docker compose up`); exit `0` (предусловия
  чисты) либо `2` (есть блокеры). Поддержаны режимы `plan`/`apply`/`verify` (argparse).
- **FAIL:** режим `plan` что-то пишет/поднимает; нет одного из режимов; неизвестный код выхода
  (не из `{0,1,2}`).

---

## AC-2 — Скан предусловий выявляет нехватку

**Условие:** на хосте отсутствует одно из предусловий (напр. `node` или свободный прод-порт).
- **PASS:** `plan`/preflight перечисляет отсутствующее как блокер с понятной строкой и подсказкой
  (что доустановить / какой порт занят); при всех присутствующих — блокеров нет, разрешён `apply`.
- **FAIL:** отсутствующая зависимость не отмечена; ложный блокер при фактически выполненном
  предусловии; preflight роняет скрипт исключением.

---

## AC-3 — Управляемая установка зависимостей (D-1)

**Условие:** недостающая зависимость, для которой определён пакетный менеджер хоста (`apt`/`dnf`).
- **PASS:** установщик печатает **точную команду** установки под обнаруженный дистрибутив; авто-
  выполнение — только после явного согласия (`y/N`); при отказе / отсутствии TTY / неизвестном
  дистрибутиве — печатает инструкцию и завершает `exit 2`, **не выполнив ни одной root-мутации
  молча**.
- **FAIL:** скрипт ставит системный пакет без подтверждения; команда не соответствует дистрибутиву;
  неизвестный дистрибутив приводит к падению вместо инструкции.

---

## AC-4 — Детект существующих Plane/Gitea и выбор (D-2)

**Условие:** на хосте присутствует ≥2 кандидата Plane (или Gitea); отдельный прогон — 0 кандидатов.
- **PASS:** при ≥2 — установщик показывает **нумерованный список** обнаруженных инсталляций и
  принимает выбор оператора; выбранный URL наполняет `ORCH_PLANE_*`/`ORCH_GITEA_*`. При 0 —
  запрашивает ручной ввод URL (фолбэк). Выбор/ввод проходит живую верификацию (AC-5) до записи.
- **FAIL:** при нескольких инсталляциях выбор не предложен (молча берётся первая/случайная); нет
  ручного фолбэка при 0 кандидатов; сбой детекта роняет установщик (нарушение never-raise NFR-3).

---

## AC-5 — Живая верификация введённых данных ДО записи

**Условие:** оператор вводит токен/URL Plane, Gitea или Telegram.
- **PASS:** введённое значение проверяется онлайн перед записью (Plane `GET …/projects/`; Gitea
  `GET /api/v1/user`; Telegram `getMe`); неверное → отклоняется (повтор до N либо `exit 2` с
  подсказкой) и **в `.env` не пишется**; верное → принимается и пишется. Секреты вводятся скрыто
  (`getpass`).
- **FAIL:** неверный секрет записан в `.env`; верификация отсутствует/после записи; секрет виден на
  экране при вводе.

---

## AC-6 — Гигиена секретов

**Условие:** прогон `apply`, затем повторный `apply`.
- **PASS:** ни одно значение секрета не появляется в stdout/логах (только имена ключей/пути);
  `.env` и `.env.watchdog` создаются правами **600**; повторный прогон **не перетирает** уже
  присутствующие значения секретов без `--force`/явного согласия.
- **FAIL:** секрет утёк в вывод/лог; права файла шире 600; повтор молча перезаписал существующий
  секрет.

---

## AC-7 — Каноны переиспользуются, не форкаются

**Условие:** ревизия `scripts/install_lite.py` и его прогон.
- **PASS:** webhook-секреты выпускаются вызовом `gen_secrets.py`; проект регистрируется вызовом
  `onboard_project.py apply`+`verify`; `.env` рендерится из `.env.example`. Структурный тест
  подтверждает ссылки на оба кирпича и **отсутствие форка** канона статусов Plane (нет
  захардкоженных 22 имён статусов) и собственной генерации секретов.
- **FAIL:** установщик сам генерирует webhook-секреты / сам создаёт статусы-лейблы-репо / сам
  выдумывает ключи env вместо рендера из канона.

---

## AC-8 — Идемпотентный apply

**Условие:** два последовательных `apply` (фейки Plane/Gitea/процессов в тесте).
- **PASS:** второй прогон помечает завершённые шаги как `skipped`; дублей проекта/webhook не
  создаётся; значения `.env` стабильны; exit-код консистентен (`0`, либо `2` если остался
  manual-step).
- **FAIL:** повтор создаёт дубли / перевыпускает секреты / падает на уже сделанном шаге.

---

## AC-9 — Рантайм не затронут

**Условие:** диф PR ORCH-104.
- **PASS:** изменены только `scripts/**`, `docs/**`, `tests/**` (+ `CLAUDE.md`/`CHANGELOG.md`);
  `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, machine-verdict ключи, схема БД —
  **байт-в-байт** (снапшот-тест инвариантов зелёный, не изменён этим PR).
- **FAIL:** PR трогает `src/**` / реестр стадий / гейты / схему БД; снапшот-тест инвариантов
  изменён или падает.

---

## AC-10 — stdlib-only, без delete-операций, без импорта платформы

**Условие:** структурный анализ `scripts/install_lite.py` (AST/токенайз/grep).
- **PASS:** только stdlib-импорты (из разрешённого набора); нет `from src …`/`import src`; нет
  деструктивных операций (`docker … rm`, `rm -rf`, удаление веток, `git push --force`); каноны-
  знания — только субпроцессами кирпичей.
- **FAIL:** найден сторонний/платформенный импорт; найдена delete/force-операция; найден захардкод
  хост-литералов (переиспользуется `FORBIDDEN`-набор `tests/test_no_host_hardcodes.py`).

---

## AC-11 — Health-гейт и итоговый вердикт

**Условие:** успешный `apply`, затем `verify`.
- **PASS:** `GET /health` → 200, `GET /queue` и `GET /metrics` → валидный JSON; итоговая сводка
  печатает PASS; любой FAIL шага даёт `exit 1` с диагностикой. `verify` (read-only) повторяет
  health-контракты + `onboard_project.py verify`.
- **FAIL:** скрипт рапортует успех при неотвечающем `/health`; нет финальной сводки; FAIL не
  даёт `exit 1`.

---

## AC-12 — Синхронизация документации

**Условие:** ревизия `docs/deployment/LITE_SETUP.md` и анти-дрейф теста.
- **PASS:** LITE_SETUP.md содержит указатель на установщик как рекомендованный путь (ручной
  маршрут сохранён фолбэком); `tests/test_lite_setup_doc.py` ассертит наличие ссылки и **остаётся
  зелёным** во всех существующих проверках (13 разделов, кирпичи, key-sync); обновлены
  `CLAUDE.md`/`CHANGELOG.md` (и витрина `docs/overview/`, если затронута).
- **FAIL:** установщик добавлен, но LITE_SETUP.md/`CLAUDE.md` о нём молчат; анти-дрейф тест не
  обновлён/красный (reviewer-ось обзорных доков, ORCH-079/011 → finding ≥P1).

---

## Сводная матрица AC ↔ BR/FR/NFR
| AC | Покрывает | Тип проверки |
|----|-----------|--------------|
| AC-1 | BR-1 / FR-1 | integration (plan zero-mutation) |
| AC-2 | BR-2 / FR-2 | unit (preflight verdict) |
| AC-3 | BR-3 / FR-3 / D-1 | unit + integration (consent / no-TTY) |
| AC-4 | BR-5 / FR-4 / D-2 | unit (discovery/select) |
| AC-5 | BR-4 / FR-5 | integration (verify-before-write) |
| AC-6 | BR-8 / NFR-2 | unit + integration (mask / 600 / no-overwrite) |
| AC-7 | BR-6 / FR-6 / FR-9 / INV-2 | structural (reuse bricks, anti-fork) |
| AC-8 | BR-7 / NFR-5 | integration (idempotency) |
| AC-9 | BR-8 / NFR-1 / INV-1 | structural (invariant snapshot) |
| AC-10 | BR-8 / NFR-4 / NFR-6 / INV-3 / INV-7 | structural (AST/grep) |
| AC-11 | BR-7 / FR-10 | integration (health gate) |
| AC-12 | BR-9 / FR-11 | structural (doc anti-drift) |
