---
verdict: APPROVED        # APPROVED | REQUEST_CHANGES — строго одно из двух, UPPERCASE
work_item: ORCH-112
stage: review
author_agent: reviewer
status: approved
created_at: 2026-06-15
model_used: claude-opus-4-8
type: review
work_item_id: ORCH-112
version: 1
---

# Review ORCH-112 — deploy-base checkout-hygiene (resilient-pull)

## Summary

Багфикс инцидента **ORCH-111** (bug → escalate full-cycle): прод-self-deploy падал на голом
`git pull origin main` хост-хука из-за грязного shared main checkout (остатки ORCH-104 от ORCH-104).
Реализован **resilient-pull в хуке** (`--deploy`): перед pull при обнаружении грязи база сводится к
чистому `origin/main` (`git fetch` + `git reset --hard origin/main` + скоупленный `git clean -fd`),
под kill-switch, never-raise, скоуп self-hosting.

Проверены все 4 оси. Реализация **точно соответствует** ADR-001 (D1–D7) и сквозному adr-0044, все
10 критериев приёмки (AC-1…AC-10) покрыты содержательными тестами, документация (golden source)
обновлена в том же PR, инварианты конвейера/БД/exit-code-контракт хука — байт-в-байт не тронуты.

**Вердикт: APPROVED.** P0/P1/P2 findings отсутствуют.

### Что сверено (доказательная база)

**Ось 1 — соответствие ТЗ (02-trz) / критериям (03-acceptance-criteria):**
- FR-1 (устойчивый pull) / AC-1 — ✅ хук-блок «2a. Resilient pull» + регресс **TC-01** (зелёный после
  фикса) и **TC-01b** (тот же грязный base без гигиены → `would be overwritten by merge`, репро инцидента).
- FR-1 / AC-2 (untracked WIP) — ✅ **TC-02** (остатки сняты, не протекают в деплой).
- NFR-2 / AC-3 (сохранность) — ✅ **TC-03** (`.deploy-prev-image-*`, `deploy-hook.log`, gitignored
  `.env`/`data/*.db`, sibling `.deploy-state-*`/`.merge-lease-*.json`, `.git/worktrees/*` — на месте) +
  **TC-05** статический контракт (`git clean -fd`, **никогда `-x`**, явные excludes).
- BR-5 / AC-4 (happy-path) — ✅ **TC-04** (чистая база → no-op + fast-forward, exit-коды байт-в-байт).
- NFR-1 / AC-5 (self-hosting safety) — ✅ скоуп `$REPO`, `reset --hard origin/main` (не локальная
  догадка), нет push/force-push (TC-05 ассерт).
- FR-5 / AC-6 (kill-switch + обратимость) — ✅ **TC-06** (off → инертно; пустой CSV → self-hosting only;
  enduro не затронут).
- FR-2 / AC-7 (сходимость после cancel/failed) — ✅ **TC-07** (deploy-time self-heal; `cancel_task`
  корректно НЕ расширён — D4).
- FR-4 / AC-8 (наблюдаемость) — ✅ **TC-08** (`read_report`/`alert_dirty` never-raise) + врезка в
  `run_deploy_finalizer` (sentinel → Telegram, best-effort).
- NFR-5 / AC-9 (инвариант конвейера/БД) — ✅ **TC-09** + проверка дифа: `STAGE_TRANSITIONS`/`QG_CHECKS`/
  `check_*`/machine-verdict/схема БД/exit-code-контракт хука (0/1/2) не тронуты.
- BR-3 / AC-10 (документация) — ✅ см. ось 4.

**Ось 2 — соответствие ADR:**
- ADR-001 D1–D7 реализованы дословно: resilient-pull в хуке (не janitor/не container-side, D1),
  NEVER `-x` + excludes (D2), leaf `checkout_hygiene.py` + инжекция env в `build_deploy_command` (D3),
  `cancel_task` не расширяется / janitor не вводится (D4), sentinel → finalizer-alert (D5), docs (D6),
  только `--deploy` не `--build-staging` (D7, подтверждено размещением блока между шагами 1 и 2 пути
  `--deploy`).
- Трассировка (ORCH-078): правка `build_deploy_command` (маркеры ORCH-101/ORCH-058) — чисто аддитивна
  (одно env-присваивание после `EXPECTED_REVISION`), инвариант image-freshness не сломан; ORCH-036
  exit-code-контракт и ORCH-090 cancel-каскад не нарушены; INV-4 (никогда push/force-push `main`)
  соблюдён.

**Ось 3 — качество кода:**
- Leaf чистый, never-raise, ленивые импорты (`self_deploy`/`qg.checks`/`notifications`) — leaf-инвариант
  доказан **TC-05** (`leaf_is_a_pure_leaf`). Docstrings на всех публичных функциях. `shlex.quote` на
  инжектируемом пути. Env-проводка консистентна с существующим паттерном `result`-sentinel
  (`initiate_deploy` пред-создаёт `container_state_dir` → запись `hygiene` гарантированно проходит).
- **Багфикс-трек регресс-тест (ORCH-019 / BR-4):** присутствует — **TC-01** (фиксатор дефекта,
  зелёный после фикса) в паре с **TC-01b** (репродукция инцидента: голый pull аборт без гигиены).
- Все ссылки на API существуют (`notifications.link_for`/`send_telegram`, `self_deploy.host_state_dir`/
  `container_state_dir`, `qg.checks.is_self_hosting_repo`). `repo`/`work_item_id`/`task_id` в скоупе
  финализатора.
- Тесты содержательные: 17 TC (шелл-симуляция реального хука в герметичном git-репо без сети/прода/ssh
  + unit). Прогон: **17/17 зелёные**; смежные deploy/config/stage_engine/frontmatter — **200/200 зелёные**;
  docs/hardcode/canon — **101/101 зелёные**.

**Ось 4 — документация (golden source):**
- `src/` изменён → документация обновлена **в том же PR**: `docs/operations/INFRA.md` (инвариант
  deploy-база ≠ workspace), `docs/architecture/README.md` (раздел ORCH-112 design), `CHANGELOG.md`,
  `CLAUDE.md` (паспортный блок), `.env.example` (новые ключи), ADR-001 + сквозной
  `docs/architecture/adr/adr-0044`. Консистентны между собой и с кодом.
- Обзорные доки (ORCH-079): открытые пункты `README.md` «Известные ограничения» (Telegram 48h /
  intra-repo deps / batch-автоном) этим PR **не закрываются** → обновление не требуется. ✅
- Витрина системы (ORCH-011): фикс — внутренняя устойчивость deploy-пути, **не** новая
  стадия/гейт/агент/интеграция/способность → витрина `docs/overview/` не затронута;
  `tests/test_system_docs.py` зелёный. ✅

## Findings

### P0 — Blocker
- Нет.

### P1 — Must fix
- Нет.

### P2 — Should fix
- Нет.

### P3 — Nice-to-have (не блокирует, на усмотрение)
- `tests/test_deploy_checkout_hygiene.py::test_tc05_hook_clean_is_never_destructive` ассертит
  `assert "-x" not in code` по **всем** исполняемым строкам хука. Текущий хук токена `-x` не содержит
  (тест зелёный), но будущая легитимная конструкция (`set -x`, `[ -x file ]`, `chmod +x`) ложно уронит
  ассерт. Можно сузить проверку до строки(ок) `git clean` — но это страховка критичного инварианта
  INV-HYGIENE-1, поэтому строгость намеренна и допустима. Не блокирует.

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

**Статус: обновлена полностью, в том же PR (golden source соблюдён).**

| Документ | Статус |
|----------|--------|
| `docs/work-items/ORCH-112/06-adr/ADR-001-deploy-base-checkout-hygiene.md` | ✅ заведён (architecture, после escalate full-cycle) |
| `docs/architecture/adr/adr-0044-deploy-base-checkout-hygiene.md` | ✅ сквозной ADR заведён |
| `docs/operations/INFRA.md` | ✅ инвариант deploy-база ≠ workspace + страховка resilient-pull |
| `docs/architecture/README.md` | ✅ раздел ORCH-112 (design) |
| `CHANGELOG.md` | ✅ запись [Unreleased] |
| `CLAUDE.md` | ✅ паспортный блок |
| `.env.example` | ✅ `ORCH_CHECKOUT_HYGIENE_ENABLED` / `_REPOS` |
| `docs/overview/` (витрина, ORCH-011) | ➖ не требуется (внутренний deploy-fix, не новая способность) |
| `README.md` «Известные ограничения» (ORCH-079) | ➖ не требуется (открытые пункты не закрываются) |

Необновлённой документации при изменённом `src/` **нет** → ось 4 пройдена, P0 по документации
отсутствует.
