--- work_item: ORCH-112 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-15 model_used: claude-opus-4-8 escalate: full-cycle --- # 01 — BRD / Bug-report: ORCH-112 — failed/cancelled task artifacts must be cleaned from shared checkout Work Item: **ORCH-112** · Repo: **orchestrator** · Стадия: analysis · Трек: **Bug → эскалация в full-cycle** > ⚠️ **`escalate: full-cycle` (ADR-001 D5 ORCH-019).** Баг помечен `Bug`, но по сути это > **архитектурный + safety-critical (self-hosting)** дефект: правка лежит в самом опасном пути > прод-деплоя (хост-хук, прямо перед рестартом прод-контейнера) и требует **решения о политике > жизненного цикла** shared checkout (ADR). Поэтому выпускается **полный** analysis-пакет, а не > облегчённый bug-пакет. Оператор снимает багфикс-трек: `POST /bug-fast-track/escalate?work_item=ORCH-112` > → задача пойдёт через стадию `architecture` (architect выпустит ADR для политики cleanup/изоляции). --- ## 1. Бизнес-контекст и проблема ### Симптом (наблюдаемое) Self-deploy задачи **ORCH-111** упал на шаге `git pull origin main` хост-хука деплоя с ошибкой: ``` error: Your local changes to the following files would be overwritten by merge: src/config.py Please commit your changes or stash them before you merge. ``` Деплой прерван, конвейер потребовал **ручного вмешательства оператора** (на self-hosting это групповой риск — встаёт деплой и всех других проектов). ### Причина симптома (установленный факт) В **общем (shared) checkout** `/home/slin/repos/orchestrator` оставались грязные файлы от ранее **неуспешной/отменённой/перезапущенной задачи ORCH-104** (тема Lite installer): - модифицированный tracked-файл: `src/config.py`; - модифицированный/untracked: `docs/deployment/LITE_SETUP.md`; - untracked: `scripts/install_lite.py`, `tests/test_install_lite.py`, `docs/deployment/lite-install.example.yaml`. Через несколько дней эти остатки **заблокировали** `git pull` другой задачи (ORCH-111). ### Локализация (анализ — куда смотреть архитектору/разработчику) **Установленный факт о топологии (CLAUDE.md / `docs/architecture/README.md`):** `/home/slin/repos/orchestrator` (хост) == `/repos/orchestrator` (контейнер, bind-mount) == **main clone** (`settings.repos_dir/` = `settings.deploy_host_repo_path`). Это **deploy-база и база управления worktree'ами**, а НЕ рабочая копия агента. 1. **Первичный дефект — нерезистентный `git pull`.** `scripts/orchestrator-deploy-hook.sh:224-226` делает `cd "$REPO"` (= deploy-база) и **голый** `git pull origin main` **без гигиены рабочего дерева**. Любая локальная правка tracked-файла блокирует merge → деплой падает. Проверено: во всём `src/`+`scripts/` **нет ни одного** `git reset --hard` / `git clean` / `git stash` для приведения базы к чистому состоянию. Shared checkout трактуется как «всегда чистый», что не гарантировано. 2. **Невыполненный/неэнфорснутый инвариант + отсутствие «дворника».** Нормальный конвейер **не пишет** в рабочее дерево main clone: агенты работают в изолированных worktree'ах `/repos/_wt//` (`git_worktree.ensure_worktree`); `docker build` использует контекст **worktree** (`image_freshness._host_worktree_path`), не main clone; fallback'и гейтов на main clone — **только чтение** (`git show origin/main:...`, `qg/checks.py:451`, `coverage_gate.py:297`, `stage_engine.py:145`). Поэтому грязь ORCH-104 почти наверняка — **ручной/брошенный WIP** в shared checkout во время инцидента ORCH-104 (косвенное подтверждение: файлы `install_lite.py`/`test_install_lite.py`/`lite-install.example.yaml` **никогда не существовали в git-истории** — закоммиченный артефакт ORCH-104 это `scripts/setup_lite.py`, commit `e2cf883`). Вне зависимости от источника: **нет механизма**, который детектирует/чистит грязную базу и **нет задокументированного/энфорснутого инварианта** «main checkout — неизменяемая deploy-база, не workspace». 3. **`cancel_task` чистит worktree + remote-ветку, но НЕ shared checkout.** `stage_engine.cancel_task` (шаг 3d, строки ~2330-2343): `remove_worktree(repo, branch)` + `gitea.delete_remote_branch(repo, branch)`. Это корректно (конвейер в main clone не пишет), но означает **нулевое покрытие** случая «грязная deploy-база» в каскадах failed/cancelled. **Вывод:** даже если первопричина грязи — ручное действие, устойчивость должна быть на стороне системы: deploy-база обязана **самовосстанавливаться** в чистый `origin/main` перед pull, а политика жизненного цикла — гарантировать, что остатки failed/cancelled задач не клинят будущие операции. ## 2. Объём (scope) ### В объёме - Сделать self-deploy `git pull origin main` (shared deploy-база) **устойчивым к грязному рабочему дереву** — приведение базы к чистому `origin/main` **автономно**, без ручного вмешательства. - Гарантировать, что после **failed / cancelled / брошенной** задачи в shared checkout не остаётся рабочих остатков, способных заблокировать будущий деплой/операцию (сходимость базы к чистому `origin/main`). - Задокументировать (и где осуществимо — мягко энфорснуть/гардить) инвариант «shared main checkout — deploy/worktree-management база, НЕ редактируемый workspace». - Наблюдаемость: лог + Telegram-алерт, когда deploy-база найдена грязной и автоочищена (или отказ). ### Вне объёма - ❌ Запрет/контроль ручных операций оператора в shared checkout (вне технической власти системы; закрываем устойчивостью, а не запретом). - ❌ Изменение модели worktree per-task (`git_worktree`, ORCH-2) — она корректна и не трогается. - ❌ Любое изменение `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключей / схемы БД. - ❌ Изменение поведения деплоя на чистой базе (happy-path должен остаться байт-в-байт). - ❌ Выбор конкретного механизма (reset --hard vs janitor vs guard) — это **зона архитектора** (ADR). ## 3. Заинтересованные стороны - **Заказчик/оператор (Слава)** — страдает от ручного разруливания залипших деплоев; принимает результат. - **Self-hosting конвейер orchestrator** — прямой потребитель (надёжность прод-деплоя). - **Все проекты на общем инстансе (enduro-trails)** — косвенно: залипший self-deploy орка останавливает обслуживание их задач. ## 4. Бизнес-требования (BR) - **BR-1** — Грязное рабочее дерево shared deploy-базы (модифицированные tracked-файлы и/или untracked-файлы) **НЕ должно блокировать** self-deploy `git pull origin main`: деплой обязан привести базу к чистому, актуальному `origin/main` **без ручного вмешательства**. - **BR-2** — После failed / cancelled / брошенной задачи в shared checkout **не должно оставаться** рабочих остатков этой задачи, способных заблокировать будущий деплой/git-операцию; база **сходится** к чистому `origin/main`. - **BR-3** — Инвариант «shared main checkout (`/`) — deploy/worktree-management база, НЕ workspace» должен быть **задокументирован** (`docs/operations/INFRA.md` + `docs/architecture/README.md`) и, где осуществимо, **энфорснут/гардирован**; конвейер/агенты **никогда** не пишут рабочие изменения в main clone (верифицировать, что это так). - **BR-4** — **Наблюдаемость:** обнаружение грязной базы и факт автоочистки (или отказ) должны логироваться и алертиться (Telegram, кликабельный номер) — оператор видит, что гигиена сработала. - **BR-5** — На **чистой** базе поведение деплоя — **байт-в-байт прежнее** (обычный fast-forward `git pull`); никакого регресса happy-path. ## 5. Нефункциональные требования (NFR) - **NFR-1 (self-hosting safety)** — гигиена **никогда** не трогает ветку `main` на remote, не делает force-push, не рестартит прод-контейнер вне штатного гейта, не удаляет worktree/ветки **других активных** задач. Оперирует **только** настроенным путём deploy-базы. - **NFR-2 (сохранность deploy-состояния)** — автоочистка **не должна** удалять артефакты, легитимно живущие под `$REPO`/рядом: rollback-снимки `$REPO/.deploy-prev-image-*` (`deploy_prod_prev_image_file`), `deploy-hook.log`, sibling-состояния `/.deploy-state-*` / `.merge-lease-*.json`, и админ-записи worktree в `.git/worktrees`. (Наивный `git clean -xfd` в `$REPO` уничтожил бы `.deploy-prev-image-*` и сломал rollback — это **жёсткое ограничение** для архитектора/разработчика.) - **NFR-3 (обратимость / kill-switch)** — новое поведение под флагом; выключенный флаг → деплой байт-в-байт как до ORCH-112 (голый `git pull origin main`). - **NFR-4 (надёжность)** — never-raise / fail-safe (по образцу leaf'ов `serial_gate`/`cancel`); идемпотентность; restart-safe; сбой гигиены не должен маскировать или ухудшать исход деплоя сверх текущего. - **NFR-5 (нулевая регрессия конвейера)** — `STAGE_TRANSITIONS` / `QG_CHECKS` / `check_*` / machine-verdict ключи / схема БД / exit-code-контракт хука (0/1/2, ORCH-036) — **байт-в-байт**. - **NFR-6 (область)** — изменение скоупится на self-hosting (`orchestrator`); поведение для прочих репо/синхронного деплоя агентом — не ухудшается. ## 6. Допущения и ограничения - Shared checkout и хост-хук физически разделяют один путь с контейнером через bind-mount (`repos_dir`↔`host_repos_dir`); хук исполняется на **хосте** по ssh (ORCH-036, detached). - Build-once путь (`SOURCE_IMAGE` retag) **не** зависит от содержимого рабочего дерева main clone — прод получает ровно staging-валидированный образ; значит дискард рабочего дерева base перед pull **безопасен для деплоимого артефакта**. (`--build-staging` собирается из **worktree**, не из main — отдельный контур.) - Источник истины кода — `origin/main`; локальные правки в deploy-базе **по определению** не должны существовать (это deploy-база, а не место работы). - Конкретный механизм (resilient pull через reset+clean со скоуп-исключениями / активный janitor / guard инварианта / комбинация) — **открытый вопрос для архитектуры**, решается в `06-adr/`. ## 7. Критерии успеха Self-deploy успешно выполняет `git pull` на ранее грязной shared-базе **без ручного вмешательства**; deploy-база сходится к чистому `origin/main`; rollback-состояние и sibling-артефакты сохранены; happy-path и весь конвейер — без регресса; обязательный регресс-тест **красный до фикса, зелёный после**. Детальные PASS/FAIL — `03-acceptance-criteria.md`. ## 8. Риски - Деструктивная гигиена (`reset --hard`/`clean`) в **прод-deploy-базе** рядом с рестартом прода — ошибка скоупа может удалить rollback-state/логи (см. NFR-2) → ADR обязателен. - Маскировка реальной первопричины: если в будущем какой-то код **начнёт** писать в main clone, «тихая автоочистка» это скроет → нужна наблюдаемость (BR-4). - Кросс-каттинг с ORCH-036 (self-deploy), ORCH-058 (image-freshness/provenance), ORCH-090 (cancel), ORCH-2 (worktree-модель). Детали/митигации — `10-tech-risks.md` (заполняет архитектор).