work_item: ORCH-112
stage: analysis
author_agent: analyst
status: ready-for-review
created_at: 2026-06-15
model_used: claude-opus-4-8
title: "Гигиена shared deploy-базы: устойчивость self-deploy git pull к грязному дереву"
framework: pytest
scope: >
  Покрывается: устойчивость self-deploy `git pull origin main` к грязной shared deploy-базе
  (модифицированные tracked + untracked файлы), сходимость базы к чистому origin/main после
  failed/cancelled задач, сохранность deploy-rollback-состояния, kill-switch/область, наблюдаемость,
  self-hosting safety. Вне покрытия: модель worktree per-task (ORCH-2, не трогается), запрет ручных
  операций оператора, изменения STAGE_TRANSITIONS/QG_CHECKS/схемы БД (их нет).
notes: >
  TC-01 — ОБЯЗАТЕЛЬНЫЙ регресс-тест воспроизведения инцидента ORCH-111: КРАСНЫЙ до фикса, ЗЕЛЁНЫЙ
  после. Шелл-симуляции хука моделировать по образцу tests/test_deploy_hook_rollback_sim.py
  (временный git-репо во временной директории, без сети/прода/ssh). Полный регресс `pytest tests/ -q`
  обязан оставаться зелёным (NFR-5). Точные имена тест-модулей/функций уточнит разработчик; тип
  гигиены (resilient-pull / janitor / guard) выберет архитектор — тесты сформулированы так, чтобы
  проверять ТРЕБУЕМЫЙ ИНВАРИАНТ (база сходится к чистому origin/main, артефакты сохранены), а не
  конкретный механизм.

tests:
  - id: TC-01
    type: integration
    description: "РЕГРЕСС (обязательный, red→green): shared deploy-база с локальной модификацией tracked-файла src/config.py + untracked файлами — симуляция шага git pull хука приводит базу к чистому origin/main и НЕ падает с 'local changes would be overwritten by merge' (воспроизводит ORCH-111; красный до фикса)."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-02
    type: integration
    description: "Untracked WIP-файлы (install_lite.py / test_install_lite.py / lite-install.example.yaml) в shared-базе не блокируют операцию и база сходится к чистому origin/main."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-03
    type: integration
    description: "Сохранность (NFR-2): после гигиены файлы $REPO/.deploy-prev-image-* , deploy-hook.log, sibling .deploy-state-* / .merge-lease-*.json и .git/worktrees/* НЕ удалены; rollback по .deploy-prev-image-* остаётся работоспособным."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-04
    type: integration
    description: "Happy-path без регресса: на ЧИСТОЙ shared-базе шаг pull — обычный fast-forward; наблюдаемое поведение и exit-коды (0/1/2, ORCH-036) байт-в-байт прежние."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-05
    type: unit
    description: "Self-hosting safety: путь гигиены никогда не оперирует веткой main на remote, не делает force-push, не рестартит прод и не сносит worktree/ветки других задач; операции ограничены настроенным путём deploy-базы (статический/поведенческий ассерт)."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-06
    type: unit
    description: "Kill-switch off → деплой/pull байт-в-байт прежний (голый git pull origin main); on → активна устойчивая гигиена. Область applies(repo): self-hosting orchestrator real, прочие репо — no-op."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-07
    type: integration
    description: "Сходимость после cancel/failed: cancel_task (ORCH-090) / failed-исход не оставляет рабочих остатков в shared-базе, блокирующих будущий деплой; последующий self-deploy проходит без ручного вмешательства."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-08
    type: unit
    description: "Наблюдаемость: обнаружение грязной базы и факт автоочистки (или отказ) попадают в лог; Telegram-алерт best-effort/never-raise (его сбой не валит деплой), номер задачи кликабельный."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-09
    type: unit
    description: "Инвариант конвейера: STAGE_TRANSITIONS / реестр QG_CHECKS / имена и семантика check_* / machine-verdict ключи / exit-code-контракт хука — не изменены (структурный анти-регресс)."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS

  - id: TC-10
    type: unit
    description: "Документация-инвариант: docs/operations/INFRA.md и docs/architecture/README.md содержат правило «shared main checkout — deploy/worktree-management база, не workspace» (структурная сверка)."
    module: tests/test_deploy_checkout_hygiene.py
    expected: PASS
