Files
orchestrator/docs/work-items/ORCH-103/12-review.md

14 KiB
Raw Blame History

verdict, work_item, stage, author_agent, status, created_at, model_used, type, work_item_id, version
verdict work_item stage author_agent status created_at model_used type work_item_id version
APPROVED ORCH-103 review reviewer approved 2026-06-11 claude-opus-4-8 review ORCH-103 1

Review ORCH-103 — ORCH-10b Bundled-тираж: весь стек одним комплектом + bootstrap-скрипт

Машинный вердикт читается ТОЛЬКО из verdict: во frontmatter.

Summary

PR закрывает Type B эпика ORCH-10 строго по ТЗ и ADR-001 (D1D11): новый каталог deploy/bundled/ (самодостаточный compose 16 сервисов, project name orchestrator-bundle, пиннинг неподвижными тегами, одна bridge-сеть, только человеческие порты, мина GITEA__webhook__ALLOWED_HOST_LIST закрыта), scripts/bootstrap_bundle.py (stdlib-only, plan-дефолт/apply/verify, step-движок check→ensure, exit 0/2/1, ноль delete-операций), конфиг-канон deploy/bundled/.env.example (ни одного дефолтного пароля), docs/deployment/BUNDLED_SETUP.md (14 разделов канона D10) и три содержательных анти-дрейф тест-модуля. Рантайм байт-в-байт: git diff main не содержит src/**, корневого docker-compose.yml, Dockerfile, .gitea/workflows/** (AC-6 подтверждён diff-stat'ом). Полный регресс: pytest tests/ -q → 1844 passed, 0 failed (AC-5); существующие анти-дрейф тесты (test_lite_setup_doc.py, test_no_host_hardcodes.py, канон ORCH-009) не правились. Документация обновлена в том же PR по всем точкам §8 ТЗ. Findings — только P2/P3, блокеров нет → APPROVED.

Оси проверки

1. Соответствие ТЗ (02-trz.md, 03-acceptance-criteria.md)

  • FR-1 — отдельный bundle-compose; состав = ADR D1/D3 (тест test_bundle_has_exactly_the_adr_service_set фиксирует множество); пиннинг всех сторонних образов литералом (TC-03); тома — именованные с префиксом проекта + bind строго внутри project dir (TC-04); достижимость в обе стороны — сервис-DNS (D4) + ALLOWED_HOST_LIST; карта портов в доке §2, конфликт порта → отказ preflight; staging-контура нет вовсе.
  • FR-2 — последовательность шагов 19 ТЗ воспроизведена 1:1 в APPLY_STEPS (тест test_apply_steps_match_normative_plan держит соответствие нормативному плану); идемпотентность — check→ensure/skip (AC-8 покрыт unit'ами resume/«противоречивое состояние»); exit-контракт 0/2/1 (test_exit_code_contract); манифест manual-step честный: инструкция → подтверждение → API-верификация, без TTY — немедленный exit 2.
  • FR-3 — webhook-секреты строго субпроцессом gen_secrets.py (структурный тест); bundle-креды — stdlib secrets (token_hex ≥16 байт, unit проверяет длину); в репо только пустые плейсхолдеры (test_bundle_secrets_in_example_are_empty_placeholders); права 600; без перетирания без --force-secrets (test_merge_missing_secrets_never_overwrites_without_force).
  • FR-4 — BUNDLED_SETUP.md: все 14 разделов в порядке маршрута, fenced-команды + «Проверка:»/PASS/FAIL в каждом исполняемом разделе, общие шаги ссылками на LITE_SETUP §5§8 / ONBOARDING / REPLICATION §4 (форк канона отсутствует), fail-closed имена Confirm Deploy/STOP и «22 статуса» — сверкой импорта plane_sync._PLANE_NAME_TO_KEY.
  • FR-5 — три модуля без docker/сети/LLM; FORBIDDEN — импорт из test_no_host_hardcodes.py (один источник истины); секрет-эвристика hex≥32/alnum≥40 с негативным самочеком (не-evergreen); key-set-sync ${VAR} ⊆ bundle-канона; заморозка корневого compose зеркалом ассерта ORCH-102.
  • FR-6 — smoke = REPLICATION §4 поверх bundle (§11 дока, без форка), минимальный сигнал «артефакты 0104 в ветке» зафиксирован.
  • AC-матрица: AC-4…AC-9 в файловой/структурной части — PASS (TC-01…TC-12 зелёные). AC-1/AC-2/AC-3/AC-8(повторный прогон) в e2e-части — ручная приёмка на чистом хосте/VM по рамке самих AC (в CI docker/LLM не гоняются) — остаётся за стадией приёмки, см. P2-2.

2. Соответствие ADR (06-adr/ADR-001, adr-0038)

  • D1D11 реализованы без отклонений; сквозной adr-0038-bundled-replication-canon.md заведён.
  • Прогрессивная автоматизация webhook (D7) — единственное место, где реализация глубже буквы ADR: step_plane_webhook регистрирует workspace-webhook прямой записью в Postgres инсталляции. Сверено: это не новый канон, а переиспользование документированного «пути Б» LITE_SETUP §5.4 (тот же INSERT-контракт, колонка-в-колонку), контракт чекпоинта сохранён (верификация SELECT'ом; при отказе — fallback на честный manual-step с той же проверкой), схема стабильна благодаря пину v0.23.1. D7 явно разрешает такую замену manual-step → ensure «без правки ADR». Нарушения нет; в доке §7 чекпоинт 4 описан честно.
  • Трассировка (TRACEABILITY): правка чужого маркированного блока одна — строка Type B в REPLICATION.md §1 (артефакт ORCH-101); это запланированная точка расширения, прямо предписанная ТЗ FR-6/§8 — инвариант ORCH-101 не сломан. Остальные изменения аддитивны (новые файлы / новые секции CLAUDE.md, README архитектуры, CHANGELOG).
  • Нормативы предшественников соблюдены: branch protection НЕ настраивается (D10 ORCH-009 / INV-4 — явно в D6 и §14.6 дока), compose не форкается (adr-0037), «дефолт = боевое» не нарушен (корневой .env.example не тронут).

3. Качество кода

  • pytest tests/ -q: 1844 passed, 0 failed (66s); новые тесты содержательные — unit'ы чистых функций покрывают позитив/негатив/resume/противоречивое состояние, эвристики имеют негативный самочек, ast-скан stdlib-allowlist реально закрыт.
  • Бизнес-логика скрипта аккуратная: never-print секретов в stdout (только имена ключей), маскированный лог при падении clone (токен в URL не утекает в вывод), _psql через stdin (секрет не в argv), fail-fast preflight до любых мутаций, диагностика «кто не дождался + хвост логов». Не багфикс-трек (feature) — требование регресс-теста-фиксатора BR-4/ORCH-019 неприменимо.
  • Замечания P2/P3 — ниже; ни одно не ломает инварианты конвейера и не относится к рантайму платформы (скрипт исполняется только оператором на целевом хосте).

4. Документация — обязательная проверка

Выполнена явно, см. раздел «Документация» ниже. Обновлено всё требуемое в том же PR.

Findings

P0 — Blocker

  • (нет)

P1 — Must fix

  • (нет)

P2 — Should fix

  • P2-1. Секреты в argv субпроцессов (scripts/bootstrap_bundle.py): step_init_gitea передаёт GITEA_ADMIN_PASSWORD аргументом --password в docker compose exec gitea gitea admin user create …, а step_agent_git — токен в clone-URL аргументом git clone http://oauth2:<token>@…. Значения видимы в ps хоста на время исполнения. Формально AC-8 («секрет виден в stdout/логе») не нарушен, но это против духа NFR-3 (ТЗ FR-2) и непоследовательно с собственной argv-гигиеной скрипта (_psql прогоняет секреты через stdin с явным комментарием «секреты не попадают в argv, NFR-3»). Рекомендация: для clone — использовать git -c credential.helper/GIT_ASKPASS либо дописать компромисс в TR-7 (10-tech-risks) и шапку скрипта; для gitea admin user create альтернатив CLI мало — минимум зафиксировать окно экспозиции в TR-7. Не блокер: разовая операция оператора на одноарендном целевом хосте, угроза-модель совпадает с уже зафиксированным компромиссом TR-7.
  • P2-2. Замер цифр «Требований к хосту» отложен на приёмку (AC-4): BUNDLED_SETUP §2 декларирует «подтверждаются замером приёмочного развёртывания» — на момент ревью цифры (8 GB / 40 GB / 4 vCPU) синхронизированы с константами preflight структурным тестом, но фактический замер ещё не зафиксирован. По рамке 03-acceptance-criteria (e2e — ручная приёмка вне CI) это допустимо, однако при ручной приёмке AC-1/AC-2 результат замера нужно зафиксировать (13-test-report / 15-staging-log или правка §2), иначе FAIL-условие AC-4 «цифры с потолка» останется формально незакрытым.

P3 — Nice to have

  • P3-1. step_plane_webhook: slug/secret интерполируются в SQL-строку без экранирования одинарных кавычек. Секрет — hex от gen_secrets (безопасен), slug — операторский ввод; кавычка в slug уронит INSERT. Риск минимален (ON_ERROR_STOP + fail-safe fallback на manual-step), но дешёвое value.replace("'", "''") сняло бы класс целиком.
  • P3-2. run_verify: при одновременном health-FAIL и onboard exit 2 функция возвращает EXIT_MANUAL (2), маскируя ошибку (ожидался бы приоритет 1). Поведенческая мелочь read-only режима.

Документация

Артефакт Статус
CLAUDE.md новый раздел «Bundled-тираж (ORCH-103)» (паттерн ORCH-101/102)
docs/architecture/README.md блок Type B — Bundled рядом с 10-common/Lite
CHANGELOG.md запись feat: ORCH-103 (детальная, D1D11)
docs/operations/REPLICATION.md §1 Type B → ORCH-103 + ссылка на BUNDLED_SETUP.md
docs/deployment/BUNDLED_SETUP.md создан, 14 разделов канона D10, держится тестом
ADR work-item 06-adr/ADR-001-… + сквозной adr-0038-bundled-replication-canon.md
07-infra-requirements.md, 10-tech-risks.md (TR-1…TR-9) заведены архитектором, кросс-рефы из кода сходятся
.gitignore deploy/bundled/repos/ (NFR-3)
README.md «Известные ограничения» (ORCH-079) проверено явно: PR не закрывает ни один из 3 открытых пунктов (Telegram 48h / intra-repo deps / пакетный автоном) — обновление витрины не требуется

src/** не изменён (PR — deploy/scripts/docs/tests), поэтому P0-правило «src/ изменён без документации» неприменимо; документация при этом обновлена полностью.

Итог

verdict: APPROVED — P0/P1 отсутствуют; P2-1/P2-2 и P3 рекомендуется снять follow-up'ом или при ручной приёмке Bundled-развёртывания (AC-1/AC-2/AC-3/AC-8 e2e-часть).