ORCH-112: resilient-pull hygiene for dirty shared deploy-base (fix incident ORCH-111) #136

Merged
admin merged 8 commits from feature/ORCH-112-bug-failed-cancelled-task-arti into main 2026-06-15 15:33:21 +03:00
Owner

ORCH-112 — гигиена shared deploy-базы: устойчивый self-deploy git pull

Тип: fix (bug → escalate full-cycle). Инцидент: ORCH-111.

Проблема

Прод-self-deploy падал на шаге git pull origin main хост-хука с
error: Your local changes to the following files would be overwritten by merge: src/config.py
— грязь, оставленная неуспешной/отменённой/брошенной задачей ORCH-104 в общем main checkout.
Деплой вставал → ручное вмешательство (на self-hosting — групповой риск: встаёт деплой всех проектов).

Решение (по ADR-001 / adr-0044)

Resilient-pull, встроенный в прод-deploy-хук (--deploy): перед git pull хук при обнаружении
грязи приводит deploy-базу к чистому актуальному origin/main
(git fetch + git reset --hard origin/main + скоупленный git clean -fd).

  • Новый leaf src/checkout_hygiene.py (чистый never-raise, образец serial_gate/cancel/self_deploy):
    applies(repo) (kill-switch + скоуп, пусто → self-hosting only, локально и ПЕРВЫМ),
    hook_env (инжекция CHECKOUT_HYGIENE=1 HYGIENE_REPORT=… в detached-команду self_deploy.build_deploy_command
    только при applies==True, иначе "" → голый pull 1:1), read_report/alert_dirty (наблюдаемость),
    snapshot() (read-only блок GET /queue).
  • Хук-блок «2a. Resilient pull» между шагом «1. Capture PREV_IMG» и «2. Pull».
  • Сохранность (NFR-2): git cleanтолько -fd, НИКОГДА -x; явные -e '.deploy-prev-image-*'
    и -e 'deploy-hook.log'; sibling .deploy-state-*/.merge-lease-*.json и .git/worktrees/* — вне области.
  • Сходимость после failed/cancelled — этим же deploy-time self-heal; cancel_task не расширяется,
    janitor не вводится. Наблюдаемость — Phase-C finalizer читает sentinel hygiene + Telegram-алерт.

Инварианты

Аддитивно, под kill-switch (ORCH_CHECKOUT_HYGIENE_ENABLED, дефолт true; off → байт-в-байт до ORCH-112),
never-raise, скоуп self-hosting. STAGE_TRANSITIONS / реестр QG_CHECKS / семантика и имена check_* /
machine-verdict-ключи / схема БД / exit-code-контракт хука (0/1/2, ORCH-036) — байт-в-байт не тронуты.

Тесты

tests/test_deploy_checkout_hygiene.py (TC-01…TC-10: шелл-симуляция реального хука во временном git-репо
без сети/прода/ssh + unit). TC-01 — обязательный регресс ORCH-111: КРАСНЫЙ до фикса, ЗЕЛЁНЫЙ после.
Полный регресс — pytest tests/ -q: 2018 passed.

Документация (golden source, в том же PR)

CLAUDE.md, CHANGELOG.md, .env.example (developer); docs/operations/INFRA.md,
docs/architecture/README.md, adr-0044 (architecture-стадия). ADR:
docs/work-items/ORCH-112/06-adr/ADR-001-deploy-base-checkout-hygiene.md.

🤖 Generated with Claude Code

## ORCH-112 — гигиена shared deploy-базы: устойчивый self-deploy `git pull` **Тип:** `fix` (bug → escalate full-cycle). **Инцидент:** ORCH-111. ### Проблема Прод-self-deploy падал на шаге `git pull origin main` хост-хука с `error: Your local changes to the following files would be overwritten by merge: src/config.py` — грязь, оставленная неуспешной/отменённой/брошенной задачей ORCH-104 в **общем** main checkout. Деплой вставал → ручное вмешательство (на self-hosting — групповой риск: встаёт деплой всех проектов). ### Решение (по ADR-001 / adr-0044) **Resilient-pull, встроенный в прод-deploy-хук** (`--deploy`): перед `git pull` хук при обнаружении грязи приводит deploy-базу к чистому актуальному `origin/main` (`git fetch` + `git reset --hard origin/main` + **скоупленный** `git clean -fd`). - **Новый leaf `src/checkout_hygiene.py`** (чистый never-raise, образец `serial_gate`/`cancel`/`self_deploy`): `applies(repo)` (kill-switch + скоуп, пусто → self-hosting only, локально и ПЕРВЫМ), `hook_env` (инжекция `CHECKOUT_HYGIENE=1 HYGIENE_REPORT=…` в detached-команду `self_deploy.build_deploy_command` только при `applies==True`, иначе `""` → голый pull 1:1), `read_report`/`alert_dirty` (наблюдаемость), `snapshot()` (read-only блок `GET /queue`). - **Хук-блок «2a. Resilient pull»** между шагом «1. Capture PREV_IMG» и «2. Pull». - **Сохранность (NFR-2):** `git clean` — **только `-fd`, НИКОГДА `-x`**; явные `-e '.deploy-prev-image-*'` и `-e 'deploy-hook.log'`; sibling `.deploy-state-*`/`.merge-lease-*.json` и `.git/worktrees/*` — вне области. - **Сходимость после failed/cancelled** — этим же deploy-time self-heal; `cancel_task` **не расширяется**, janitor **не вводится**. **Наблюдаемость** — Phase-C finalizer читает sentinel `hygiene` + Telegram-алерт. ### Инварианты Аддитивно, под kill-switch (`ORCH_CHECKOUT_HYGIENE_ENABLED`, дефолт `true`; off → байт-в-байт до ORCH-112), never-raise, скоуп self-hosting. `STAGE_TRANSITIONS` / реестр `QG_CHECKS` / семантика и имена `check_*` / machine-verdict-ключи / схема БД / exit-code-контракт хука (0/1/2, ORCH-036) — **байт-в-байт не тронуты**. ### Тесты `tests/test_deploy_checkout_hygiene.py` (TC-01…TC-10: шелл-симуляция реального хука во временном git-репо без сети/прода/ssh + unit). **TC-01 — обязательный регресс ORCH-111: КРАСНЫЙ до фикса, ЗЕЛЁНЫЙ после.** Полный регресс — `pytest tests/ -q`: **2018 passed**. ### Документация (golden source, в том же PR) `CLAUDE.md`, `CHANGELOG.md`, `.env.example` (developer); `docs/operations/INFRA.md`, `docs/architecture/README.md`, `adr-0044` (architecture-стадия). ADR: `docs/work-items/ORCH-112/06-adr/ADR-001-deploy-base-checkout-hygiene.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
admin added 7 commits 2026-06-15 15:16:06 +03:00
Self-deploy git pull blocked on a dirty shared main checkout (manual/abandoned
WIP from a failed/cancelled task) — incident ORCH-111: "Your local changes to
src/config.py would be overwritten by merge" wedged the prod deploy and required
manual intervention (a group risk on self-hosting).

The deploy hook (--deploy) now converges the deploy-base to a clean, current
origin/main BEFORE the pull (git fetch + reset --hard origin/main + a SCOPED
`git clean -fd`, NEVER -x), strictly preserving the rollback/log artefacts
(.deploy-prev-image-* / deploy-hook.log via -e), gitignored .env/data/*.db/build
(no -x), and sibling/.git state (out of clean scope). Gated by CHECKOUT_HYGIENE
env injected by self_deploy.build_deploy_command only when the new pure never-raise
leaf src/checkout_hygiene.py says applies(repo) (kill-switch + self-hosting scope).
Convergence after failed/cancelled is this same deploy-time self-heal — cancel_task
is NOT extended and no background janitor is introduced. Observability: the hook
writes a `hygiene` sentinel, the Phase-C finalizer reads it and sends a best-effort
Telegram alert.

Additive, under kill-switch (ORCH_CHECKOUT_HYGIENE_ENABLED, default true; off ->
bare `git pull origin main` 1:1 before ORCH-112), never-raise, self-hosting scope.
STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict keys / DB schema / the
hook exit-code contract (0/1/2, ORCH-036) are byte-for-byte untouched.

Coverage: tests/test_deploy_checkout_hygiene.py (TC-01..TC-10; real-hook shell
simulation in a temp git repo, no network/prod/ssh, + unit). TC-01 is the
mandatory ORCH-111 regression (RED before the fix, GREEN after). Docs golden
source updated in the same PR (CLAUDE.md, CHANGELOG.md, .env.example; INFRA.md /
architecture/README.md / adr-0044 written at the architecture stage).

Refs: ORCH-112

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tester(ET): auto-commit from tester run_id=706
All checks were successful
CI / test (push) Successful in 3m59s
CI / test (pull_request) Successful in 3m9s
344ab72f37
admin force-pushed feature/ORCH-112-bug-failed-cancelled-task-arti from b4d28a5401 to 344ab72f37 2026-06-15 15:16:06 +03:00 Compare
admin added 1 commit 2026-06-15 15:33:17 +03:00
deploy(ORCH-036): finalize SUCCESS for ORCH-112
All checks were successful
CI / test (push) Successful in 3m9s
CI / test (pull_request) Successful in 3m11s
285f5f05dc
admin merged commit 6abb444839 into main 2026-06-15 15:33:21 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/orchestrator#136