architect(ET): auto-commit from architect run_id=190
This commit is contained in:
@@ -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-файлы (`<repos_dir>/.deploy-state-<repo>/<wi>/`), без миграции БД.
|
||||
Подробнее: [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.*
|
||||
|
||||
64
docs/architecture/adr/adr-0007-executable-self-deploy.md
Normal file
64
docs/architecture/adr/adr-0007-executable-self-deploy.md
Normal file
@@ -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-файлы под
|
||||
`<repos_dir>/.deploy-state-<repo>/<wi>/` (как 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`.
|
||||
@@ -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 \$? > <result-sentinel>' >> <hook.log> 2>&1 </dev/null &"
|
||||
```
|
||||
ssh-команда возвращается сразу; remote-процесс detached. Запись sentinel `result`
|
||||
делает **обёртка** (`echo $? > 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 (`<repos_dir>/.merge-lease-<repo>.json`) состояние деплоя
|
||||
хранится в файлах под `<repos_dir>/.deploy-state-<repo>/<work_item_id>/` (вне 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`).
|
||||
48
docs/work-items/ORCH-036/07-infra-requirements.md
Normal file
48
docs/work-items/ORCH-036/07-infra-requirements.md
Normal file
@@ -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.** `<repos_dir>/.deploy-state-<repo>/` доступен на запись и хосту (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 — нет (принципы проекта).
|
||||
34
docs/work-items/ORCH-036/08-data-requirements.md
Normal file
34
docs/work-items/ORCH-036/08-data-requirements.md
Normal file
@@ -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-файлы**
|
||||
под `<repos_dir>/.deploy-state-<repo>/<work_item_id>/` (паттерн merge-lease
|
||||
`<repos_dir>/.merge-lease-<repo>.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).
|
||||
23
docs/work-items/ORCH-036/10-tech-risks.md
Normal file
23
docs/work-items/ORCH-036/10-tech-risks.md
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user