Files
orchestrator/docs/work-items/ORCH-112/01-brd.md

170 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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/<repo>` = `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/<repo>/<branch>` (`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 (`<host_repos_dir>/<repo>`) — 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-состояния
`<repos_dir>/.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` (заполняет архитектор).