14 KiB
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 (D1–D11): новый каталог
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 ✅ — последовательность шагов 1–9 ТЗ воспроизведена 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-креды — stdlibsecrets(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 дока, без форка), минимальный сигнал «артефакты 01–04 в ветке» зафиксирован.
- 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)
- D1–D11 реализованы без отклонений; сквозной
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 и onboardexit 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 (детальная, D1–D11) |
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-часть).