From 1acbb1d28d70755597cd3f2a58287bc43bbf2b5f Mon Sep 17 00:00:00 2001 From: claude-bot Date: Sat, 6 Jun 2026 19:25:16 +0000 Subject: [PATCH] architect(ET): auto-commit from architect run_id=190 --- docs/architecture/README.md | 30 ++- .../adr/adr-0007-executable-self-deploy.md | 64 ++++++ .../06-adr/ADR-001-executable-self-deploy.md | 184 ++++++++++++++++++ .../ORCH-036/07-infra-requirements.md | 48 +++++ .../ORCH-036/08-data-requirements.md | 34 ++++ docs/work-items/ORCH-036/10-tech-risks.md | 23 +++ 6 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 docs/architecture/adr/adr-0007-executable-self-deploy.md create mode 100644 docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md create mode 100644 docs/work-items/ORCH-036/07-infra-requirements.md create mode 100644 docs/work-items/ORCH-036/08-data-requirements.md create mode 100644 docs/work-items/ORCH-036/10-tech-risks.md diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 27710b4..109aebc 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -52,6 +52,34 @@ created → analysis → architecture → development → review → testing → Подробнее: [adr-0006](adr/adr-0006-merge-gate.md), детально — `docs/work-items/ORCH-043/06-adr/ADR-001-merge-gate.md`. +### Исполняемый самодеплой стадии `deploy` (ORCH-36) +`deploy` перестаёт быть «бумажной»: для self-hosting (`is_self_hosting_repo`) стадия +РЕАЛЬНО деплоит прод (8500) через хост-хук `scripts/orchestrator-deploy-hook.sh`, +а `deploy_status: SUCCESS` означает доказанный health-ok, не декларацию LLM. Три фазы +(детерминированно, без LLM в критическом пути self-restart): +- **Фаза A (вход в `deploy`)** — при `deploy_require_manual_approve=true` вместо запуска + прод-deployer выставляется approval-pending статус Plane + запрос approve + (Plane-коммент + Telegram). Перехват в `advance_stage` ПОСЛЕ `check_staging_status` + и merge-gate. +- **Фаза B (Plane → `Approved`)** — `advance_stage(deploy, finished_agent=None)` + запускает **detached host-процесс** (ssh + setsid → хук с прод-параметрами + + build-once retag `SOURCE_IMAGE`) и ставит детерминированный **finalizer-job**; + маркер `initiated` — идемпотентность. Возврат БЕЗ advance (вердикта ещё нет). +- **Фаза C (finalizer)** — новый контейнер после рестарта читает sentinel `result` + (exit-code хука), маппит `0→SUCCESS / иначе→FAILED`, пишет `14-deploy-log.md`, + вызывает `advance_stage(deploy, finished_agent="deployer")` → существующие контракты: + `SUCCESS → done`, `FAILED → откат БАГ-8 на development`. + +Approve = смена статуса Plane на `Approved` (status-only verdict model; комментарии +не управляют конвейером). На старте — обязательный ручной approve (флаг `true`); полный +авто — отдельная задача (ORCH-54). Условность как ORCH-35: реально для `orchestrator`, +прочие репо — прежний синхронный ssh-деплой агентом. Контракты не меняются: +`STAGE_TRANSITIONS`, реестр QG, `check_deploy_status`/`_parse_deploy_status`, БАГ-8, +terminal-sync, merge-gate, exit-code-контракт хука. Restart-safe состояние — +sentinel-файлы (`/.deploy-state-//`), без миграции БД. +Подробнее: [adr-0007](adr/adr-0007-executable-self-deploy.md), детально — +`docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md`. + ## Откаты - Reviewer REQUEST_CHANGES → откат на `development` + retry (`MAX_DEVELOPER_RETRIES = 3`). - Tester `check_tests_passed` FAIL → откат на `development` + retry. @@ -109,4 +137,4 @@ created → analysis → architecture → development → review → testing → Схема БД, потоки данных, resilience-слой, детали Dockerfile — [internals.md](internals.md). --- -*Актуально на 2026-06-06. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. ORCH-043: merge-gate — design (см. adr-0006), реализация в ветке feature/ORCH-043.* +*Актуально на 2026-06-06. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. ORCH-043: merge-gate — design (см. adr-0006), реализация в ветке feature/ORCH-043. ORCH-036: исполняемый самодеплой стадии `deploy` — design (см. adr-0007), реализация в ветке feature/ORCH-036.* diff --git a/docs/architecture/adr/adr-0007-executable-self-deploy.md b/docs/architecture/adr/adr-0007-executable-self-deploy.md new file mode 100644 index 0000000..1b8192c --- /dev/null +++ b/docs/architecture/adr/adr-0007-executable-self-deploy.md @@ -0,0 +1,64 @@ +# ADR-0007: Исполняемый самодеплой стадии `deploy` (Вариант B, ORCH-36) + +## Статус +Accepted (design) — реализация в ветке `feature/ORCH-036`. + +## Контекст +Стадия `deploy` была «бумажной»: deployer-агент писал `deploy_status:` в +`14-deploy-log.md`, гейт `check_deploy_status` парсил вердикт и двигал +`deploy → done`. Реального деплоя не было. ORCH-36 делает стадию исполняемой для +self-hosting (`orchestrator`), сохраняя прежний ssh-путь для остальных репо. + +Три ограничения формируют дизайн (детально — `docs/work-items/ORCH-036/06-adr/ADR-001`): +1. **Self-restart**: рестарт прод-контейнера 8500 убивает in-container процесс → + рестарт делает ВНЕШНИЙ host-процесс. +2. **Status-only verdict model**: approve = смена статуса Plane на `Approved` + (комментарии не управляют конвейером). +3. **Гонка гейта**: вердикт нельзя читать до завершения асинхронного хука. + +## Решение +Для self-hosting стадия `deploy` исполняется в три фазы детерминированным кодом +(без LLM в критическом пути self-restart): + +- **Фаза A (вход в `deploy`)** — для self + `deploy_require_manual_approve=true` + вместо запуска прод-deployer выставляется approval-pending статус Plane + запрос + approve (Plane-коммент + Telegram). Перехват в `advance_stage` на ребре + `deploy-staging → deploy` (после `check_staging_status` и merge-gate). +- **Фаза B (Plane → Approved)** — `advance_stage(deploy, finished_agent=None)` + запускает **detached host-процесс** (ssh + setsid → `orchestrator-deploy-hook.sh` + с прод-параметрами и build-once retag) и ставит **детерминированный finalizer-job** + с задержкой; маркер `initiated` — идемпотентность. Возврат БЕЗ advance. +- **Фаза C (finalizer)** — после рестарта новый контейнер дочитывает sentinel + `result` (exit-code хука), маппит `0→SUCCESS / иначе→FAILED`, пишет + `14-deploy-log.md`, вызывает `advance_stage(deploy, finished_agent="deployer")` + → существующие контракты: `SUCCESS → done`, `FAILED → откат БАГ-8 на development`. + +### Ключевые инварианты (НЕ меняются) +`STAGE_TRANSITIONS`, реестр QG, `check_deploy_status` / `_parse_deploy_status` +(frontmatter only), откат БАГ-8, terminal-sync `deploy → done`, merge-gate (ORCH-43), +exit-code-контракт хука (0/1/2). + +### Новое (сквозное) +- **Детерминированный job-kind** `deploy-finalizer` в очереди (reserved-agent, не + LLM): read-result | defer | map+write+advance. Зеркалит детерминизм merge-gate. +- **Approve-флаг** `deploy_require_manual_approve` (дефолт `true`; полный авто — + отдельная задача после набора метрик доверия, ORCH-54). +- **Build-once**: опциональный `SOURCE_IMAGE` retag в хуке (обратно совместимо). +- **Restart-safe состояние** деплоя — sentinel-файлы под + `/.deploy-state-//` (как merge-lease), БЕЗ миграции БД. + +### Условность +Вся логика — только для `is_self_hosting_repo(repo)` (как ORCH-35). Прочие репо +деплоятся прежним синхронным ssh-путём агентом. + +## Последствия +- `deploy_status: SUCCESS` доказан реальным health-ok; критический путь self-restart + детерминирован. +- Вводится новая под-компонента (finalizer job-handler) → изменение помечено + `arch:major-change`. +- Approve вписан в status-only модель: restart-safe, аудируемо, идемпотентно. +- На старте — обязательный ручной approve; молчаливых деплоев нет (Plane+Telegram). + +## Связанные ADR +`adr-0003` (staging-gate), `adr-0006` (merge-gate), `adr-0005` (run-as-host-uid). +Детальный per-work-item: `docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md`. diff --git a/docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md b/docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md new file mode 100644 index 0000000..c88d0a3 --- /dev/null +++ b/docs/work-items/ORCH-036/06-adr/ADR-001-executable-self-deploy.md @@ -0,0 +1,184 @@ +# ADR-001: Исполняемый самодеплой — стадия `deploy` дёргает хост-хук (Вариант B) + +Work Item: ORCH-036 +Stage: architecture +Автор: architect +Дата: 2026-06-06 + +## Статус +Accepted + +## Контекст + +Стадия `deploy` сейчас «бумажная»: deployer-агент (LLM) пишет в `14-deploy-log.md` +`deploy_status: SUCCESS|FAILED`, а гейт `check_deploy_status` (`src/qg/checks.py:464`) +парсит этот вердикт и двигает `deploy → done`. Реального docker-деплоя нет (прод +катается руками). BRD ORCH-36 требует превратить стадию в РЕАЛЬНЫЙ самодеплой с +обязательным ручным approve, build-once и авто-rollback (BR-1…BR-10). + +Три твёрдых ограничения, разведанных в коде, определяют дизайн: + +1. **Self-restart (BR-2).** Прод-контейнер `orchestrator` (8500) — ОДИН на все + проекты, и в нём же исполняется deployer. `docker compose up -d orchestrator` + из контейнера убьёт процесс агента/воркера на середине. Реальный рестарт обязан + делать ВНЕШНИЙ процесс на хосте, переживающий гибель контейнера. +2. **Status-only verdict model.** Комментарии Plane НЕ управляют конвейером — + механизм `:approved:`/`:rejected:` был удалён (`src/webhooks/plane.py:544`, + bug-3 «echo self-hit»). Единственный человеческий гейт — **смена статуса Plane + на `Approved`** (`handle_verdict` → `_try_advance_stage` → `advance_stage`). +3. **Гонка чтения гейта.** Так как реальный рестарт асинхронный и убивает контейнер, + `check_deploy_status` нельзя выполнять на выходе агента — вердикта ещё нет; его + преждевременное чтение → ложный FAILED → ложный откат. + +Контракты, которые НЕ меняются (BR-9, AC-10): `STAGE_TRANSITIONS`, +`check_deploy_status` / `_parse_deploy_status` (frontmatter only), откат БАГ-8 +(`deploy → development`), terminal-sync `deploy → done`, merge-gate (ORCH-43), +exit-code-контракт хука (0/1/2). + +## Решение + +Деплой стадии `deploy` для self-hosting (`orchestrator`) разбивается на **три фазы**, +оркеструемые детерминированным кодом (без LLM в критическом пути self-restart). Для +НЕ-self репо (enduro-trails и пр.) поведение НЕ меняется — прежний синхронный +ssh-деплой агентом. + +### Условность по репо +Вся новая логика гейтится `is_self_hosting_repo(repo)` (как ORCH-35). Не-self репо +идут существующим путём: deployer-агент на стадии `deploy` делает ssh-деплой +синхронно, пишет `14-deploy-log.md`, гейт срабатывает на выходе агента. + +### Фаза A — запрос approve (вход в `deploy`) +В `advance_stage` на ребре `deploy-staging → deploy` (ПОСЛЕ зелёного +`check_staging_status` и merge-gate ORCH-43), для self-hosting + `deploy_require_ +manual_approve=true`: +- **НЕ** ставить в очередь прод-deployer (перехватить штатный + `enqueue_job(get_agent_for_stage("deploy-staging"))`); +- выставить issue в approval-pending статус (паттерн `set_issue_in_review`), + написать Plane-коммент «approve для прод-деплоя» + Telegram (BR-5); +- записать restart-safe маркер `approve-requested` (sentinel-файл, см. ниже). + +Задача остаётся на стадии `deploy` и ждёт человека. `STAGE_TRANSITIONS` не меняется. + +При `deploy_require_manual_approve=false` (вне объёма, флаг НЕ выключается в ORCH-36 — +AC-12) Фаза A сразу переходит к Фазе B без человеческого гейта. Структурная ветка +закладывается, но дефолт `true`. + +### Фаза B — инициация деплоя (смена статуса Plane → Approved) +Человек ставит issue в `Approved`. `handle_verdict(approved=True)` → +`_try_advance_stage` → `advance_stage(current_stage="deploy", finished_agent=None)`. +Новая ветка-перехват в `advance_stage`: +- условие: `current_stage=="deploy"` И `finished_agent is None` (человеческий путь) + И self-hosting И approve-флаг И маркер `initiated` ОТСУТСТВУЕТ; +- действие: запустить **внешний detached host-процесс** (см. ниже) и поставить в + очередь детерминированный **finalizer-job** с задержкой; записать маркер + `initiated` (идемпотентность: повторный Approved не запускает деплой дважды); + Plane-коммент «прод-деплой стартовал» + Telegram (BR-5); +- **вернуться БЕЗ advance** (НЕ запускать `check_deploy_status` — вердикта ещё нет). + +Дискриминатор `finished_agent` разводит Фазу B (человек, `None`) и Фазу C +(finalizer, `"deployer"`), поэтому повторное использование `advance_stage` безопасно. + +### Фаза C — фиксация вердикта (детерминированный finalizer) +Finalizer-job (claim'ится воркером уже в НОВОМ контейнере после рестарта): +- читает sentinel `result` (exit-code хука, записан host-процессом); +- если `result` ещё нет и бюджет попыток не исчерпан → **defer** (повторный + finalizer-job с `available_at_delay_s`, как merge-gate defer); бюджет считается + из `jobs` (`LIKE '%deploy-finalize%'`, restart-safe); +- если `result` есть → **маппинг exit-code → deploy_status** (детерминированный, + unit-тестируемый): `0 → SUCCESS`, `1|2|иное → FAILED`; записать + `14-deploy-log.md` (frontmatter `deploy_status:`), смержить в `main` (паттерн + лога), затем вызвать `advance_stage(current_stage="deploy", finished_agent="deployer")`; +- далее срабатывают СУЩЕСТВУЮЩИЕ контракты: `SUCCESS` → terminal-sync `deploy → done` + + release merge-lease; `FAILED` → откат БАГ-8 `deploy → development` + + `set_issue_blocked` + Plane/Telegram (BR-3, AC-4). `_parse_deploy_status` НЕ меняется. + +### Механизм detached-запуска: ssh + setsid +Выбор: **ssh на хост (`slin@DEPLOY_SSH_HOST`) с setsid-detached исполнением** хука. +Обоснование: ssh-ключи уже смонтированы (INFRA P-2), не-self репо уже деплоятся по +ssh (единый путь), хук живёт на хосте и под `slin` имеет полный доступ к docker вне +контейнера → переживает рестарт 8500 (BR-2). `setsid`/`nohup` + redirect отвязывает +удалённый процесс от ssh-канала, чтобы он пережил гибель ssh-клиента при рестарте +контейнера. Отвергнуто: вызов через docker.sock изнутри контейнера = ровно мина +«убей себя на середине вызова». + +Эскиз (точная сборка — за разработчиком): +``` +ssh -o StrictHostKeyChecking=no slin@$DEPLOY_SSH_HOST \ + "setsid bash -c 'cd /home/slin/repos/orchestrator && \ + SOURCE_IMAGE=orchestrator-orchestrator-staging \ + TARGET_SERVICE=orchestrator TARGET_PORT=8500 \ + TARGET_IMAGE=orchestrator-orchestrator COMPOSE_PROFILE= \ + PREV_IMAGE_FILE=.deploy-prev-image-prod \ + bash scripts/orchestrator-deploy-hook.sh --deploy; \ + echo \$? > ' >> 2>&1 result`), а НЕ хук — контракт хука нетронут. + +### Build-once (BR-6, AC-7) +Прод обязан подняться на ОБРАЗЕ, прошедшем staging (а не на пересборке). Решение: +расширить хук **опциональным** `SOURCE_IMAGE` (обратно совместимо: не задан → +текущее поведение). При заданном `SOURCE_IMAGE` хук ПЕРЕД `up -d --no-build` +делает `docker tag $SOURCE_IMAGE $TARGET_IMAGE`. Для прод-self: +`SOURCE_IMAGE=orchestrator-orchestrator-staging` → `TARGET_IMAGE=orchestrator-orchestrator`. +Это единственное допустимое изменение хука; exit-code-контракт и дефолтное +staging-поведение не меняются. `git pull` хука обновляет рабочее дерево хоста для +будущих сборок, но РАЗВЁРНУТЫЙ артефакт = перетегированный staging-образ. + +### Restart-safe состояние: sentinel-файлы (без миграции БД) +По образцу merge-lease (`/.merge-lease-.json`) состояние деплоя +хранится в файлах под `/.deploy-state-//` (вне git, +видны и хосту, и контейнеру через mount `/home/slin/repos ↔ /repos`): +- `approve-requested` — Фаза A выполнена; +- `initiated` — Фаза B запущена (idempotency-guard); +- `result` — exit-code хука (пишет host-обёртка). +Бюджет finalize-defer считается из `jobs` (restart-safe), новых таблиц/колонок НЕТ +(TRZ §4). + +## Последствия + +### Плюсы +- `deploy_status: SUCCESS` становится ДОКАЗАННЫМ (реальный health-ok хука), не + декларацией LLM (BR-1). +- Self-restart безопасен: рестарт 8500 делает внешний host-процесс; орк себя не + убивает (BR-2). Вердикт фиксирует НОВЫЙ контейнер после рестарта. +- Критический путь self-restart **детерминирован** (без LLM) — главный выигрыш по + безопасности self-hosting; зеркалит детерминизм merge-gate ORCH-43. +- Approve вписан в существующую status-only модель — restart-safe, аудируемо в Plane, + идемпотентно (маркер `initiated`). +- Гонка чтения гейта закрыта: гейт читает РЕАЛЬНЫЙ итог через finalizer-defer. +- Build-once гарантирует «что тестировали — то в проде». +- Нетронуты: `STAGE_TRANSITIONS`, реестр QG, `_parse_deploy_status`, БАГ-8, + terminal-sync, merge-gate, контракт хука (exit-code). + +### Минусы / ограничения +- Вводится **новый детерминированный job-handler** в очереди (reserved-agent + `deploy-finalizer`, не-LLM) — расширение dispatch воркера/лаунчера. Контейнированное, + но это новая под-компонента → задача помечается `arch:major-change`. +- Перехваты в `advance_stage` усложняют стадию `deploy` (три ветки по + `finished_agent`/маркерам). Требуется аккуратное покрытие тестами (TC-04…TC-09). +- Build-once зависит от того, что deploy-staging оставил валидный образ + `orchestrator-orchestrator-staging`; при rebase merge-gate возможен дрейф + образ↔main (см. 10-tech-risks R-3). +- Approve = смена статуса Plane на `Approved`; человек должен понимать, что на + стадии `deploy` `Approved` означает «деплой в прод» (документируется в deployer.md + и INFRA.md). + +### Что обязан сделать developer +1. `src/config.py`: `deploy_require_manual_approve: bool = True` + прод-параметры + хука/ssh + `deploy_finalize_delay_s` / `deploy_finalize_max_attempts`. +2. `src/stage_engine.py`: перехваты Фазы A/B + ветка finalizer (Фаза C через + `advance_stage(..., finished_agent="deployer")`). +3. Очередь: reserved-agent `deploy-finalizer` (детерминированный handler: + read-result | defer | map+write+advance). Маппинг exit→status — отдельная + чистая функция (unit TC-01/02/03). +4. `scripts/orchestrator-deploy-hook.sh`: опциональный `SOURCE_IMAGE` retag + (обратно совместимо) + прод `PREV_IMAGE_FILE`. +5. Уведомления (Plane+Telegram) на initiate/success/rollback (BR-5). +6. Документация: `deployer.md`, `INFRA.md`, `DEPLOY_HOOK.md`, `CHANGELOG.md`. +7. Отладка — только на staging-цели хука; прод 8500 в разработке не трогать. + +## Связанные решения +- Глобальный ADR: `docs/architecture/adr/adr-0007-executable-self-deploy.md`. +- ORCH-35 staging-gate (`adr-0003`), ORCH-43 merge-gate (`adr-0006`), + ORCH-21 auto-rollback, ORCH-34 хук, ORCH-40 run-as-host-uid (`adr-0005`). diff --git a/docs/work-items/ORCH-036/07-infra-requirements.md b/docs/work-items/ORCH-036/07-infra-requirements.md new file mode 100644 index 0000000..63981a0 --- /dev/null +++ b/docs/work-items/ORCH-036/07-infra-requirements.md @@ -0,0 +1,48 @@ +# Инфраструктурные требования — ORCH-036 + +Work Item: ORCH-036 +Stage: architecture +Автор: architect + +> Топология не меняется (та же mva154, те же два контейнера). Меняется ПРОЦЕДУРА +> прод-деплоя орка: из ручной → исполняемая через хост-хук с ручным approve. + +## 1. Контейнеры / порты — без изменений +- prod `orchestrator` (8500), staging `orchestrator-staging` (8501) — как в INFRA.md. +- Образы (имена для build-once): prod `orchestrator-orchestrator`, + staging `orchestrator-orchestrator-staging`. + +## 2. Хост-предусловия (Owner, в git не коммитятся) +- **HP-1.** ssh-доступ из контейнера на хост: `ssh slin@$DEPLOY_SSH_HOST` работает + под uid 1000 ключом из `~/.orchestrator-ssh` (INFRA P-2). Без него detached-запуск + Фазы B невозможен. +- **HP-2.** `/.deploy-state-/` доступен на запись и хосту (host-обёртка + пишет `result`), и контейнеру (finalizer читает) — обеспечивается mount + `/home/slin/repos ↔ /repos` (как merge-lease). +- **HP-3.** `PREV_IMAGE_FILE` для прод — отдельный путь + (`.deploy-prev-image-prod`), чтобы не путать снапшоты prod/staging. +- **HP-4 (P-4 из INFRA).** Прод-рестарт self — только в окно тишины; общий инстанс + с enduro-trails. На старте — под ручным approve (флаг `true`). + +## 3. Переменные окружения (карта; значения — на хосте, в git только дескрипторы) +| Переменная | Назначение | Дефолт | +|-----------|-----------|--------| +| `ORCH_DEPLOY_REQUIRE_MANUAL_APPROVE` | ручной approve перед прод-деплоем | `true` | +| `DEPLOY_SSH_USER` / `DEPLOY_SSH_HOST` | ssh-цель хост-хука | — (INFRA-карта) | +| `DEPLOY_HOOK_SCRIPT` | путь к хуку на хосте | `scripts/orchestrator-deploy-hook.sh` | +| прод `TARGET_SERVICE/PORT/IMAGE`, `COMPOSE_PROFILE` | override прод-профиля хука | `orchestrator`/`8500`/`orchestrator-orchestrator`/пусто | +| `SOURCE_IMAGE` (новый параметр хука) | образ для build-once retag | пусто → текущее поведение | +| `ORCH_DEPLOY_FINALIZE_DELAY_S` | задержка перед первым finalize-поллом | > 60с (health-loop хука) | +| `ORCH_DEPLOY_FINALIZE_MAX_ATTEMPTS` | бюджет finalize-defer | bounded (anti-livelock) | + +Прописать дескрипторы в `.env.example` / INFRA.md. Реальные значения не коммитить. + +## 4. Сетевые / процессные требования +- Detached host-процесс (ssh + setsid) обязан пережить рестарт прод-контейнера 8500. +- Finalizer-job исполняется в НОВОМ контейнере после рестарта (очередь restart-safe). +- MTTR авто-rollback < 60с (health-loop хука 10×6с уже укладывается, BR-8/AC-9). + +## 5. Что НЕ требуется +- Новых контейнеров/портов/сервисов — нет. +- Изменений `docker-compose.yml` — не требуется (build-once через retag, не профиль). +- Multi-node / облако / message-queue — нет (принципы проекта). diff --git a/docs/work-items/ORCH-036/08-data-requirements.md b/docs/work-items/ORCH-036/08-data-requirements.md new file mode 100644 index 0000000..4cdca89 --- /dev/null +++ b/docs/work-items/ORCH-036/08-data-requirements.md @@ -0,0 +1,34 @@ +# Требования к данным / схеме БД — ORCH-036 + +Work Item: ORCH-036 +Stage: architecture +Автор: architect + +## Решение: миграция БД НЕ требуется + +Схема SQLite (`events`, `tasks`, `agent_runs`, `jobs`) не меняется. Обоснование: + +1. **Вердикт деплоя** — в `14-deploy-log.md` (frontmatter `deploy_status:`), как + сейчас. `_parse_deploy_status` не трогаем (AC-10). +2. **Approve / initiated / result-состояние** — restart-safe через **sentinel-файлы** + под `/.deploy-state-//` (паттерн merge-lease + `/.merge-lease-.json`), а не через новую таблицу/колонку: + - `approve-requested` — Фаза A; + - `initiated` — Фаза B (idempotency-guard); + - `result` — exit-code хука (пишет host-обёртка). +3. **Бюджет finalize-defer** считается из существующей таблицы `jobs` + (`task_content LIKE '%deploy-finalize%'`), как `_merge_defer_count` для merge-gate + — restart-safe, без новых полей. +4. **Finalizer-job** использует существующую структуру `jobs` (agent, repo, + task_content, task_id, available_at). Reserved-agent `deploy-finalizer` — это + значение в колонке `agent`, схема не меняется. + +## Почему файлы, а не БД +- Sentinel должен быть виден И хосту (пишет `result`), И контейнеру (читает finalizer); + файл на общем mount это обеспечивает, SQLite-запись из host-обёртки — нет. +- Зеркалит уже принятый паттерн merge-lease (ORCH-43) — единообразие, restart-safe, + crash-реклейм по возрасту файла. + +Если разработчик при реализации сочтёт необходимым поле статуса approve в БД — +это требует обновления данного ADR с обоснованием; по умолчанию — без миграции +(согласовано с TRZ §4). diff --git a/docs/work-items/ORCH-036/10-tech-risks.md b/docs/work-items/ORCH-036/10-tech-risks.md new file mode 100644 index 0000000..c89a142 --- /dev/null +++ b/docs/work-items/ORCH-036/10-tech-risks.md @@ -0,0 +1,23 @@ +# Технические риски — ORCH-036 + +Work Item: ORCH-036 +Stage: architecture +Автор: architect + +| ID | Риск | Влияние | Вероятность | Митигация | +|----|------|---------|-------------|-----------| +| R-1 | Detached host-процесс не пережил рестарт 8500 (ssh-канал убит вместе с контейнером) | Деплой не завершён, `result` не записан, finalizer вечно defer'ит | Средняя | `setsid`/`nohup` + redirect отвязывает remote-процесс от ssh; интеграционная проверка на staging-цели (TC-08); finalize-defer bounded → по исчерпании `set_issue_blocked` + Telegram | +| R-2 | Преждевременное чтение `check_deploy_status` (вердикта ещё нет) | Ложный FAILED → ложный откат на development | Средняя | Фаза B возвращается БЕЗ advance; гейт запускает только finalizer (Фаза C) после появления `result`; defer пока `result` отсутствует | +| R-3 | Дрейф образ↔main: merge-gate сделал rebase, но staging-образ собран до rebase → build-once тегирует «не тот» код | В прод уезжает не точно то, что в `main` | Низкая | merge-gate (ORCH-43) делает re-test после rebase; build-once = «что валидировано на staging», что и есть контракт; задокументировано как осознанное ограничение; усиление (rebuild+revalidate staging после rebase) — отдельная задача | +| R-4 | Двойной Approved (человек кликнул дважды / дубль webhook) запускает деплой дважды | Двойной рестарт прода, гонка | Средняя | Маркер `initiated` (idempotency-guard); event-dedup webhook'ов Plane уже есть | +| R-5 | exit 2 хука (rollback тоже упал) → 8500 лежит → finalizer/новый контейнер не поднялся | Конвейер всех проектов встал | Низкая | health-loop + авто-rollback хука минимизируют; `restart: unless-stopped` поднимет контейнер на ПРЕДЫДУЩЕМ образе если retag не случился; exit 2 → `deploy_status: FAILED` + откат + Telegram-алерт; ручной `--rollback` хука как backstop | +| R-6 | Reserved-agent `deploy-finalizer` ошибочно уйдёт в LLM-путь лаунчера (`_spawn` → ValueError) | Finalizer не отработает | Низкая | Перехват ДО `_spawn` в `launch_job`; unit-тест маршрутизации | +| R-7 | sentinel-файлы не видны контейнеру/хосту (mount/uid) | Фазы B/C не синхронизируются | Низкая | Тот же mount и uid-модель, что у merge-lease (ORCH-40/43); HP-2 в 07-infra | +| R-8 | Approve через смену статуса Plane конфликтует с auto-advance других стадий | Случайный `Approved` на `deploy` ничего не ломает, но семантика неочевидна | Низкая | Перехват по `current_stage=="deploy"` + `finished_agent is None` + маркеры; задокументировать в deployer.md/INFRA, что `Approved` на `deploy` = «деплой в прод» | +| R-9 | Самодеплой ORCH ломает прод во время разработки самой ORCH-36 | Групповой простой (enduro-trails) | Низкая | Вся отладка — на staging-цели хука (8501); прод 8500 не трогать (AC: DoD); флаг approve=true | + +## Сводный приоритет +- **Блокеры дизайна:** R-1, R-2 — закрыты архитектурой (setsid-detached + finalizer-defer). +- **Безопасность self-hosting:** R-5, R-9 — закрыты обязательным approve + staging-отладкой + + авто-rollback + `restart: unless-stopped`. +- **Корректность:** R-3, R-4 — осознанные ограничения / idempotency-guard.