From 860de5b0a5761936f890efdef667c2e616a1d8a6 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Mon, 15 Jun 2026 14:11:46 +0300 Subject: [PATCH] analyst(ET): auto-commit from analyst run_id=701 --- docs/work-items/ORCH-112/01-brd.md | 169 ++++++++++++++++++ docs/work-items/ORCH-112/02-trz.md | 110 ++++++++++++ .../ORCH-112/03-acceptance-criteria.md | 128 +++++++++++++ docs/work-items/ORCH-112/04-test-plan.yaml | 83 +++++++++ 4 files changed, 490 insertions(+) create mode 100644 docs/work-items/ORCH-112/01-brd.md create mode 100644 docs/work-items/ORCH-112/02-trz.md create mode 100644 docs/work-items/ORCH-112/03-acceptance-criteria.md create mode 100644 docs/work-items/ORCH-112/04-test-plan.yaml diff --git a/docs/work-items/ORCH-112/01-brd.md b/docs/work-items/ORCH-112/01-brd.md new file mode 100644 index 0000000..58a26ab --- /dev/null +++ b/docs/work-items/ORCH-112/01-brd.md @@ -0,0 +1,169 @@ +--- +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` (заполняет архитектор). diff --git a/docs/work-items/ORCH-112/02-trz.md b/docs/work-items/ORCH-112/02-trz.md new file mode 100644 index 0000000..bc0f72e --- /dev/null +++ b/docs/work-items/ORCH-112/02-trz.md @@ -0,0 +1,110 @@ +--- +work_item: ORCH-112 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-15 +model_used: claude-opus-4-8 +--- + +# 02 — ТЗ (TRZ): ORCH-112 — failed/cancelled task artifacts must be cleaned from shared checkout + +Work Item: **ORCH-112** · Repo: **orchestrator** · Стадия: analysis + +> ТЗ описывает **требования и ограничения к реализации**, выведенные из BRD и фактического кода. +> Архитектурное **решение** (какой механизм гигиены/изоляции выбрать) — задача архитектора (`06-adr/`), +> т.к. задача эскалирована в full-cycle (`01-brd.md` → `escalate: full-cycle`). + +## 1. Сводка изменения +Сделать self-deploy устойчивым к **грязной shared deploy-базе** и гарантировать сходимость базы к +чистому `origin/main` после failed/cancelled/брошенных задач. Корень симптома — голый +`git pull origin main` в `scripts/orchestrator-deploy-hook.sh` (строка 226), исполняемый в +`$REPO` (= `settings.deploy_host_repo_path` = main clone), который падает при любой локальной правке +tracked-файла. Требуется: (а) приведение deploy-базы к чистому `origin/main` перед/в момент pull +**без ручного вмешательства**, со строгим сохранением deploy-rollback-состояния; (б) документирование ++ (по возможности) энфорс инварианта «main checkout — deploy-база, не workspace»; (в) наблюдаемость. + +## 2. Задействованные модули / пути +| Путь | Действие | Примечание | +|------|----------|-----------| +| `scripts/orchestrator-deploy-hook.sh` | изменить | строки 224-226: голый `git pull origin main` в `$REPO` — точка отказа (FR-1) | +| `src/self_deploy.py` | возможно изменить | `build_deploy_command` / `initiate_deploy` / `rebuild_staging_image` строят инвокацию хука — возможная точка передачи гигиены/флага (решает архитектор) | +| `src/stage_engine.py` | возможно изменить | `cancel_task` (шаг 3d, ~2330-2343) — каскад cancel; расширение гигиены на shared-базу (FR-2, если выбран этот путь) | +| `src/git_worktree.py` | возможно изменить | модель main clone ↔ worktree; возможный helper приведения базы к чистоте / верификация инварианта (BR-3) | +| `src/config.py` | изменить | новый kill-switch + флаги области (FR-5) | +| `src/.py` (напр. `checkout_hygiene.py`) | возможно создать | чистый never-raise leaf политики гигиены (по образцу `serial_gate`/`cancel`) — **создавать ли** решает архитектор | +| `docs/operations/INFRA.md` | изменить | инвариант «shared checkout — deploy-база, не workspace» (BR-3) | +| `docs/architecture/README.md` | изменить | описать политику гигиены/жизненного цикла deploy-базы | +| `CHANGELOG.md`, `CLAUDE.md` | изменить | правило «docs = golden source» (CLAUDE.md §2) | +| `tests/test_<...>.py` | создать | регресс + покрытие (см. `04-test-plan.yaml`) | + +## 3. Функциональные требования + +### FR-1 — Устойчивый self-deploy `git pull` (BR-1, BR-5) +- На пути self-deploy (`scripts/orchestrator-deploy-hook.sh`, шаг «2. Pull latest code») + `git pull origin main` **не должен падать** из-за грязного рабочего дерева `$REPO`. +- Перед обновлением база приводится к чистому, актуальному `origin/main` (приведение tracked- и + untracked-изменений к состоянию `origin/main`), **с сохранением** артефактов из NFR-2. +- На **уже чистой** базе результат — обычный fast-forward; наблюдаемое поведение и exit-коды + (0/1/2, ORCH-036) — **байт-в-байт прежние** (BR-5). +- Контракт: never-break — сбой шага гигиены не должен ухудшать исход относительно текущего голого + pull (fail-safe). + +### FR-2 — Сходимость shared-базы после failed/cancelled/брошенной задачи (BR-2) +- После терминации задачи (`failed` job-исход / `cancelled` через STOP / брошенный WIP) в shared + checkout **не остаётся** рабочих остатков, способных заблокировать будущий деплой/git-операцию. +- Допустимая трактовка «сходимости» (на выбор архитектора, **не** прескриптивно здесь): автоочистка + непосредственно в self-deploy перед pull (FR-1) **и/или** активный «дворник», приводящий + `/` к чистому `origin/main`. +- Каскад `cancel_task` (ORCH-090) уже чистит **worktree + remote-ветку**; расширение на shared-базу + (если выбрано) делается тем же never-raise best-effort способом. + +### FR-3 — Инвариант deploy-базы (BR-3) +- Задокументировать: `/` — deploy/worktree-management база; рабочие изменения + туда **не пишутся** конвейером/агентами (агенты — worktree `git_worktree`; build — worktree-контекст; + fallback'и гейтов — read-only `git show origin/main`). +- Верифицировать, что текущий код этот инвариант соблюдает (анализ ORCH-112: соблюдает; единственные + обращения к main clone — read-only/fetch/worktree-управление). Где осуществимо — добавить + лёгкий guard/проверку (решает архитектор), **без** изменения горячих путей. + +### FR-4 — Наблюдаемость (BR-4) +- Обнаружение грязной deploy-базы и факт автоочистки (число/имена сброшенных путей) или **отказ** + гигиены — лог (структурная запись) + Telegram-алерт (`send_telegram`, кликабельный номер задачи, + best-effort, never-raise). Опционально — read-only снапшот в `GET /queue` (решает архитектор). + +### FR-5 — Условность / kill-switch (BR-5, NFR-3, NFR-6) +- Новое поведение под **kill-switch** (env `ORCH_*`, по образцу `serial_gate_enabled`/`stop_status_enabled`); + выключенный флаг → деплой байт-в-байт прежний (голый `git pull origin main`). +- Область — self-hosting (`orchestrator`); прочие репо/синхронный деплой агентом — не ухудшаются. +- `applies(repo)` (локальный, без сети) проверяется первым. + +## 4. Изменения API +**Нет** обязательных. Опционально (на усмотрение архитектора) — read-only блок (напр. `checkout_hygiene`) +в существующем `GET /queue` для наблюдаемости. Новых управляющих эндпоинтов не требуется. + +## 5. Изменения схемы БД +**Нет.** Состояние гигиены, если нужно, — in-memory / sentinel-файлы (паттерн `self_deploy`/`merge_gate`), +без миграции БД. Аддитивная таблица не требуется. + +## 6. Требования к новым/изменённым QG checks +**Нет.** Это **не** Quality Gate и не стадия — это устойчивость deploy-пути и политика гигиены. +`STAGE_TRANSITIONS` / реестр `QG_CHECKS` / семантика и имена `check_*` / machine-verdict ключи +(`deploy_status:`/`staging_status:`/…) — **байт-в-байт не тронуты** (NFR-5). + +## 7. Совместимость / регресс +- **Обратная совместимость:** kill-switch off → голый `git pull origin main` (1:1 до ORCH-112); + чистая база → fast-forward без изменений (BR-5). +- **Область раската:** self-hosting `orchestrator`; enduro/прочие — нулевая регрессия. +- **Обратимость:** выключение флага мгновенно возвращает прежнее поведение. +- **Сохранность (жёсткое ограничение, NFR-2):** гигиена **не удаляет** `$REPO/.deploy-prev-image-*` + (rollback), `deploy-hook.log`, sibling `/.deploy-state-*` / `.merge-lease-*.json`, + админ-записи `.git/worktrees`. Любой `clean`-скоуп обязан их исключать. +- **Self-hosting инварианты (NFR-1):** никогда не трогать `main` на remote, не force-push, не + рестартить прод вне гейта, не сносить worktree/ветки других активных задач, оперировать только + настроенным путём deploy-базы. Exit-code-контракт хука (0/1/2) сохранён. +- **Артефакты pipeline:** создаются/обновляются обычные docs work item (`01..04` этой задачи, + `06-adr/` на стадии architecture после эскалации, `14-deploy-log.md` при деплое). Новых + pipeline-артефактов задача не вводит. +- **Трассировка (CLAUDE.md §9 / ORCH-078):** правки маркированных блоков (ORCH-036 self-deploy, + ORCH-058 image-freshness, ORCH-090 cancel) — сверять с их `06-adr/` перед изменением, инварианты + не ломать. diff --git a/docs/work-items/ORCH-112/03-acceptance-criteria.md b/docs/work-items/ORCH-112/03-acceptance-criteria.md new file mode 100644 index 0000000..b17d612 --- /dev/null +++ b/docs/work-items/ORCH-112/03-acceptance-criteria.md @@ -0,0 +1,128 @@ +--- +work_item: ORCH-112 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-15 +model_used: claude-opus-4-8 +--- + +# 03 — Критерии приёмки (Acceptance Criteria): ORCH-112 — failed/cancelled task artifacts must be cleaned from shared checkout + +Work Item: **ORCH-112** · Repo: **orchestrator** · Стадия: analysis + +Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** +(что считается провалом). Reviewer/CI проверяют их буквально по файлам репозитория и тестам. + +--- + +## AC-1 — Грязная tracked-правка не блокирует self-deploy pull (регресс ORCH-104/ORCH-111) + +**Условие:** shared deploy-база имеет локальную модификацию tracked-файла (напр. `src/config.py`), +self-deploy выполняет шаг pull. +- **PASS:** база приводится к чистому актуальному `origin/main` без ручного вмешательства; шаг pull + не возвращает «local changes would be overwritten by merge»; деплой продолжается; есть тест, + воспроизводящий точный сценарий ORCH-111 (**красный до фикса, зелёный после**). +- **FAIL:** pull/деплой падает на грязной tracked-правке; или сценарий не покрыт тестом. + +--- + +## AC-2 — Untracked WIP-файлы не блокируют и не «протекают» в деплой + +**Условие:** в shared-базе лежат untracked-файлы failed/брошенной задачи (напр. +`scripts/install_lite.py`, `tests/test_install_lite.py`, `docs/deployment/lite-install.example.yaml`). +- **PASS:** база сходится к чистому `origin/main`; untracked-остатки не блокируют операцию и не + попадают в деплоимый/собираемый артефакт. +- **FAIL:** untracked-остатки блокируют операцию либо остаются и клинят будущий деплой. + +--- + +## AC-3 — Сохранность deploy-rollback-состояния и sibling-артефактов (жёсткое ограничение) + +**Условие:** в `$REPO`/рядом присутствуют `.deploy-prev-image-*` (rollback), `deploy-hook.log`, +`/.deploy-state-*`, `.merge-lease-*.json`, `.git/worktrees/*`. +- **PASS:** после гигиены **все** перечисленные артефакты на месте; rollback по + `.deploy-prev-image-*` остаётся работоспособным; есть тест, доказывающий их неудаление. +- **FAIL:** гигиена удаляет хотя бы один из этих артефактов (особенно `.deploy-prev-image-*`). + +--- + +## AC-4 — Happy-path без регресса (чистая база) + +**Условие:** shared-база чистая, self-deploy выполняет pull. +- **PASS:** поведение и exit-коды (0/1/2, ORCH-036) — байт-в-байт прежние (обычный fast-forward); + полный `pytest tests/ -q` зелёный. +- **FAIL:** изменилось наблюдаемое поведение/exit-код на чистой базе, либо красный регресс. + +--- + +## AC-5 — Self-hosting safety + +**Условие:** исполнение пути гигиены/деплоя. +- **PASS:** нет операций над веткой `main` на remote, нет force-push, нет рестарта прод-контейнера + вне штатного гейта, нет удаления worktree/веток других активных задач; операции строго в пределах + настроенного пути deploy-базы; тест/анализ это подтверждает. +- **FAIL:** любое из перечисленных нарушений присутствует или возможно. + +--- + +## AC-6 — Kill-switch и обратимость + +**Условие:** новый флаг выключен. +- **PASS:** деплой ведёт себя байт-в-байт как до ORCH-112 (голый `git pull origin main`); включение + флага активирует устойчивое поведение; область скоупится на self-hosting (прочие репо не затронуты). +- **FAIL:** выключенный флаг меняет поведение, либо нет kill-switch, либо затронуты прочие репо. + +--- + +## AC-7 — Сходимость после cancel/failed + +**Условие:** задача отменена (STOP/ORCH-090) или job завершился `failed`, оставив остатки в +рабочем дереве deploy-базы. +- **PASS:** shared-база сходится к чистому `origin/main` (через автоочистку в деплое и/или дворник), + и последующий self-deploy проходит без ручного вмешательства; покрыто интеграционным тестом. +- **FAIL:** остатки сохраняются и блокируют последующий деплой/git-операцию. + +--- + +## AC-8 — Наблюдаемость + +**Условие:** обнаружена грязная deploy-база. +- **PASS:** факт обнаружения и автоочистки (или отказ) — в логах (структурно) и в Telegram-алерте + (кликабельный номер); алерт best-effort, never-raise (его сбой не валит деплой). +- **FAIL:** тихая очистка без следа в логах/уведомлениях, либо сбой алерта роняет деплой. + +--- + +## AC-9 — Инвариант конвейера и БД не тронуты + +**Условие:** диф задачи. +- **PASS:** `STAGE_TRANSITIONS` / реестр `QG_CHECKS` / семантика и имена `check_*` / machine-verdict + ключи / схема БД / exit-code-контракт хука — байт-в-байт; структурные тесты конвейера зелёные. +- **FAIL:** любой из перечисленных контрактов изменён. + +--- + +## AC-10 — Документация (golden source) + +**Условие:** изменён функционал deploy-базы/гигиены. +- **PASS:** обновлены `docs/operations/INFRA.md` (инвариант deploy-база ≠ workspace) и + `docs/architecture/README.md`; `CHANGELOG.md`/`CLAUDE.md` отражают изменение; ADR заведён на + стадии `architecture` (после эскалации `escalate: full-cycle`). +- **FAIL:** функционал изменён, доки/ADR не обновлены (reviewer → finding ≥P1, CLAUDE.md §6). + +--- + +## Сводная матрица AC ↔ FR/BR +| AC | Покрывает | +|----|-----------| +| AC-1 | BR-1 / FR-1 | +| AC-2 | BR-1, BR-2 / FR-1, FR-2 | +| AC-3 | NFR-2 / FR-1 (ограничение) | +| AC-4 | BR-5 / FR-1 | +| AC-5 | NFR-1 / FR-1, FR-2 | +| AC-6 | NFR-3, NFR-6 / FR-5 | +| AC-7 | BR-2 / FR-2 | +| AC-8 | BR-4 / FR-4 | +| AC-9 | NFR-5 / FR-1…FR-5 | +| AC-10 | BR-3 / FR-3 | diff --git a/docs/work-items/ORCH-112/04-test-plan.yaml b/docs/work-items/ORCH-112/04-test-plan.yaml new file mode 100644 index 0000000..507de3a --- /dev/null +++ b/docs/work-items/ORCH-112/04-test-plan.yaml @@ -0,0 +1,83 @@ +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