architect(ET): auto-commit from architect run_id=263
All checks were successful
CI / test (push) Successful in 16s
All checks were successful
CI / test (push) Successful in 16s
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
# ADR-001 (ORCH-058): Провенанс staging-образа перед BUILD-ONCE retag в прод
|
||||
|
||||
## Статус
|
||||
Accepted (design) — реализация в ветке `feature/ORCH-058-self-deploy-retag-staging`.
|
||||
Метка: `arch:major-change` (новая deploy-safety модель + новый QG + новый режим хука).
|
||||
|
||||
## Контекст
|
||||
|
||||
ORCH-36 сделал стадию `deploy` исполняемой для self-hosting (`orchestrator`): Phase B
|
||||
(`self_deploy.build_deploy_command`) запускает детачед host-хук, который шагом **2b**
|
||||
(BUILD-ONCE) делает `docker tag $SOURCE_IMAGE → $TARGET_IMAGE` **без `docker build`** —
|
||||
«прод получает ровно тот артефакт, что прошёл staging».
|
||||
|
||||
Дизайн-предпосылка BUILD-ONCE: **staging-образ свеж и собран из провалидированного кода**.
|
||||
На практике этой гарантии НЕТ (BRD §2):
|
||||
|
||||
- Стадия `deploy-staging` запускает только `scripts/staging_check.py` против **уже
|
||||
работающего** контейнера 8501 — что бы в нём ни крутилось. Пересборка staging-образа —
|
||||
ручная операция (STAGING.md / ORCH-34), вне конвейера.
|
||||
- Между «образ собран» и «retag в прод» нет провенанс-связи с провалидированным коммитом.
|
||||
|
||||
Инцидент (LESSONS_ORCH-036 п.4 — **самый опасный** из 4 багов bootstrap): staging-образ
|
||||
не пересобрали из нового `main` → `staging_check` прошёл против СТАРОГО кода → BUILD-ONCE
|
||||
retag промоутнул СТАРЫЙ образ в прод. Деплой «зелёный» (`result=0`, health ok), но прод
|
||||
**молча откатился** на код 2-дневной давности. Орк обслуживает все проекты из одного
|
||||
прод-инстанса → тихий регресс инструмента = групповой инцидент.
|
||||
|
||||
Текущая защита (staging-гейт, merge-gate, health-check хука) этот класс НЕ ловит: все
|
||||
гейты зелёные, потому что проверяют **не тот артефакт**, что уезжает в прод.
|
||||
|
||||
## Инвариант, который нужно обеспечить
|
||||
|
||||
`INV-FRESH` (ТЗ §1): образ, передаваемый хуку как `SOURCE_IMAGE` для BUILD-ONCE retag в
|
||||
прод, собран из ТОГО ЖЕ git-коммита, что прошёл `deploy-staging` для этой задачи. Если
|
||||
это недоказуемо — деплой fail-fast (`deploy_status: FAILED` → откат на `development`,
|
||||
БАГ-8), прод не трогается.
|
||||
|
||||
### Якорь «провалидированного коммита»
|
||||
|
||||
**SHA = `git rev-parse HEAD` в worktree ветки задачи ПОСЛЕ merge-gate** (т.е. после
|
||||
возможного `auto_rebase_onto_main` + `push --force-with-lease`). Это ровно тот tree,
|
||||
который merge-gate ре-тестировал зелёным и который сольётся в `main`. Один helper
|
||||
`validated_revision(repo, branch)` (never-raise) вычисляет SHA и служит ЕДИНСТВЕННЫМ
|
||||
источником и для штампа сборки (Стратегия A), и для ожидаемого ревижна (Стратегия B) —
|
||||
два потребителя одного якоря не могут разойтись.
|
||||
|
||||
## Решение: A + B (defense in depth)
|
||||
|
||||
Ни одна стратегия по отдельности не закрывает задачу:
|
||||
|
||||
- **B в одиночку** (fail-fast по провенансу) делает тихий промоут структурно невозможным,
|
||||
НО если staging-образ устарел — fail-fast'ит **навсегда** (нет пути к зелёному без
|
||||
ручной пересборки) → нарушает BR-5 / AC-6 (deadlock), воспроизводит ровно тот
|
||||
bootstrap-разрыв, который мы устраняем.
|
||||
- **A в одиночку** (пересборка из провалидированного коммита) закрывает петлю «валидируем =
|
||||
промоутим», НО не имеет утверждения В МОМЕНТ retag: гонка/отключение/сбой пересборки
|
||||
снова даст тихий промоут.
|
||||
|
||||
Поэтому берём **обе**, как взаимодополняющие слои:
|
||||
|
||||
### Стратегия A — пересборка staging-образа из провалидированного коммита (liveness, AC-4/AC-6)
|
||||
|
||||
Для self-hosting на ребре `deploy-staging → deploy`, **после merge-gate** (когда
|
||||
валидированный HEAD финализирован) и **до Phase A**, детерминированный код:
|
||||
|
||||
1. Вычисляет `sha = validated_revision(repo, branch)`.
|
||||
2. Пересобирает `orchestrator-orchestrator-staging` из **worktree ветки** (build-context =
|
||||
валидированный tree) с `--build-arg GIT_SHA=<sha>` и пересоздаёт контейнер 8501 на
|
||||
свежем образе (`--no-build`).
|
||||
3. Прогоняет `staging_check.py --mode stub` против свежего 8501.
|
||||
|
||||
Результат: ровно ЭТОТ образ (с лейблом `revision=<sha>`) становится `SOURCE_IMAGE` для
|
||||
прод-retag → петля замкнута, валидируем и промоутим один артефакт (AC-4). Пересборка/
|
||||
recreate трогают **ТОЛЬКО staging (8501)**, НИКОГДА прод (8500) (AC-9).
|
||||
|
||||
Исполнение — через host (ssh, синхронно): docker CLI / compose доступны на ХОСТЕ, не в
|
||||
контейнере (Dockerfile ставит только `openssh-client git`; staging_check уже гоняется
|
||||
`docker exec`-ом на хосте). Новый режим хука `--build-staging` (см. ниже) выполняет сборку
|
||||
и recreate. Синхронный ssh достаточен — рестарт staging не убивает прод-worker (в отличие
|
||||
от Phase B, где нужен detached + finalizer).
|
||||
|
||||
Реализуется как **детерминированный QG-под-чек `check_staging_image_fresh`** (по образцу
|
||||
`check_branch_mergeable`, ORCH-043): pure-условность + never-raise; для прочих репо →
|
||||
`(True, "N/A")`. Регистрируется в `QG_CHECKS` и в `tests/test_qg_registry_snapshot.py`.
|
||||
Вызов — на ребре через `_handle_image_freshness(...)` в `stage_engine` (рядом с
|
||||
`_handle_merge_gate`, ПОСЛЕ него, ДО Phase A). FAIL → откат на `development` + release
|
||||
merge-lease (как merge-gate). **`STAGE_TRANSITIONS` (набор стадий) НЕ меняется** — это
|
||||
под-гейт ребра.
|
||||
|
||||
### Стратегия B — fail-closed провенанс-guard в хуке (safety, AC-1/AC-2/AC-3)
|
||||
|
||||
1. **`Dockerfile`**: `ARG GIT_SHA` + `LABEL org.opencontainers.image.revision=$GIT_SHA`.
|
||||
Без build-arg лейбл пустой → fail-closed на стороне B (см. ниже).
|
||||
2. **`build_deploy_command`**: вычисляет `EXPECTED_REVISION = validated_revision(repo,
|
||||
branch)` и пробрасывает в env команды хука.
|
||||
3. **`orchestrator-deploy-hook.sh` шаг 2b** — ПЕРЕД `docker tag`:
|
||||
- читает лейбл `SOURCE_IMAGE`:
|
||||
`docker image inspect --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}' "$SOURCE_IMAGE"`;
|
||||
- сравнивает с `$EXPECTED_REVISION`;
|
||||
- несовпадение / пустой лейбл / пустой `EXPECTED_REVISION` / ошибка inspect →
|
||||
`log` + `exit 1` (**fail-closed**, никогда не промоутить «на авось»).
|
||||
- **Обратная совместимость:** при НЕзаданном `EXPECTED_REVISION` — текущее поведение
|
||||
(проверка пропускается), чтобы не сломать не-self репо и legacy-вызовы.
|
||||
4. `exit 1` уже маппится `map_exit_code_to_status → FAILED` (контракт не меняется), Phase C
|
||||
пишет `deploy_status: FAILED` → откат на `development` (БАГ-8). Прод не рестартуется на
|
||||
устаревший образ — guard срабатывает ДО `docker tag`/restart.
|
||||
|
||||
### Новый режим хука `--build-staging` (для Стратегии A)
|
||||
|
||||
`orchestrator-deploy-hook.sh --build-staging` (env: `GIT_SHA`, `BUILD_CONTEXT` = host-путь
|
||||
worktree, `TARGET_IMAGE=orchestrator-orchestrator-staging`, `TARGET_SERVICE`,
|
||||
`COMPOSE_PROFILE=staging`, `TARGET_PORT=8501`):
|
||||
`docker build --build-arg GIT_SHA=<sha> -t <TARGET_IMAGE> <BUILD_CONTEXT>` →
|
||||
`docker compose --profile staging up -d --no-build orchestrator-staging` → health 8501.
|
||||
Тот же exit-code-контракт (0=ok). Дефолты режима — STAGING-safe (как у `--deploy`).
|
||||
|
||||
Host-путь build-context выводится из container-пути worktree заменой
|
||||
`repos_dir → host_repos_dir` (как `host_state_dir` в `self_deploy.py`); требуется
|
||||
производный helper host-worktree-пути (или новая настройка `ORCH_HOST_WORKTREES_DIR`).
|
||||
|
||||
## Конфигурация (`src/config.py`, все с префиксом `ORCH_` — урок ORCH-36 п.2)
|
||||
|
||||
- `image_freshness_enabled: bool = True` — **единый** kill-switch ВСЕЙ фичи (A и B вместе).
|
||||
`False` → ни пересборки, ни проброса `EXPECTED_REVISION` → поведение ровно как ORCH-36
|
||||
(BUILD-ONCE без guard). A и B включаются/выключаются **как одно целое**, чтобы не было
|
||||
опасной полу-конфигурации «B без A» (вечный fail-fast).
|
||||
- `image_freshness_repos: str = ""` — CSV; пусто → только self-hosting (как
|
||||
`self_deploy_repos` / `merge_gate_repos`).
|
||||
|
||||
> **Инвариант конфигурации (AC-6):** B активен ТОЛЬКО когда активен A. По умолчанию
|
||||
> (`image_freshness_enabled=True`) валидный деплой всегда доходит до зелёного (A пересобирает
|
||||
> → лейбл == EXPECTED → B пропускает). Полное выключение → legacy ORCH-36 поведение.
|
||||
|
||||
## Порядок на ребре `deploy-staging → deploy` (self-hosting)
|
||||
|
||||
1. `check_staging_status` (существующий) — первичный staging-вердикт агента (smoke,
|
||||
что staging-инфра жива).
|
||||
2. merge-gate `check_branch_mergeable` (существующий) — финализирует валидированный HEAD
|
||||
(rebase если позади, ре-тест зелёный, lease HELD). DEFER на busy-lock → возврат без
|
||||
пересборки.
|
||||
3. **`check_staging_image_fresh` (НОВЫЙ, Стратегия A)** — пересборка из валидированного
|
||||
HEAD + recreate 8501 + `staging_check`. FAIL → откат на `development` + release lease.
|
||||
4. Phase A (существующий) → запрос approve.
|
||||
5. Phase B (human Approved) → `build_deploy_command` с `EXPECTED_REVISION` → хук-guard (B)
|
||||
→ BUILD-ONCE retag только при совпадении → restart прод → Phase C finalizer.
|
||||
|
||||
> Двойной прогон `staging_check` (агент на стадии + код на шаге 3) — **намеренный**: первый
|
||||
> валидирует УЖЕ работающий (потенциально устаревший) 8501 как soft pre-check; авторитетный
|
||||
> — шаг 3 против СВЕЖЕГО образа, который и уедет в прод. `--mode stub` быстр и без LLM-трат.
|
||||
|
||||
## Контракты, которые НЕ меняются (AC-7)
|
||||
|
||||
`STAGE_TRANSITIONS` (набор стадий), exit-code-контракт хука (0/1/2),
|
||||
`map_exit_code_to_status`, `check_deploy_status` / `_parse_deploy_status` (frontmatter-only),
|
||||
БАГ-8 rollback, terminal-sync `deploy → done`, merge-gate (ORCH-43), Phase A/B/C ORCH-36.
|
||||
**Схема БД — без миграций** (состояние свежести не персистится в БД; провенанс живёт в
|
||||
лейбле образа). Добавление `check_staging_image_fresh` в `QG_CHECKS` — ожидаемое расширение
|
||||
реестра (ТЗ §10), не входит в замороженный список AC-7.
|
||||
|
||||
## Last-line-of-defence / fail-closed (AC-2/AC-3)
|
||||
|
||||
Даже если A отключена/проиграла гонку/сбойнула — **B (хук-guard) делает тихий промоут
|
||||
устаревшего образа структурно невозможным**: рассинхрон лейбла и `EXPECTED_REVISION` →
|
||||
`exit 1` ДО retag → FAILED → откат. На любом сомнении (нет лейбла, пустой ожидаемый SHA,
|
||||
ошибка inspect) — трактуется как несоответствие. Прод никогда не трогается «на авось».
|
||||
|
||||
## never-raise (AC-8)
|
||||
|
||||
`validated_revision`, `rebuild_staging_image`, `check_staging_image_fresh`,
|
||||
`build_deploy_command` (проброс EXPECTED) — все защищены try/except, любая ошибка → безопасный
|
||||
вердикт (для A-под-чека: `(False, reason)` с release lease; пустой `EXPECTED_REVISION` на
|
||||
сомнении → B fail-closed). Исключение никогда не всплывает в `stage_engine`.
|
||||
|
||||
## Последствия
|
||||
|
||||
**Плюсы**
|
||||
- Класс «тихого регресса прод» закрыт структурно (B), а валидный деплой всегда доходит до
|
||||
зелёного (A) — bootstrap-разрыв «ручная пересборка staging» устранён.
|
||||
- Валидируем и промоутим один и тот же артефакт (AC-4); провенанс машиночитаем (лейбл).
|
||||
- Единый kill-switch, поэтапный раскат, условность только для self-hosting — без регрессий
|
||||
для не-self репо.
|
||||
|
||||
**Минусы / ограничения**
|
||||
- Латентность ребра растёт: +`docker build` staging + recreate 8501 + повторный
|
||||
`staging_check` перед Phase A. Приемлемо (выполняется в monitor-треде, как merge-gate
|
||||
re-test; bounded timeouts).
|
||||
- `staging_check` гоняется дважды (soft pre-check агента + авторитетный код) — осознанная
|
||||
плата за AC-4. Возможная будущая оптимизация: облегчить шаг 3 до health+revision-smoke,
|
||||
если merge-gate re-test признать достаточным для кода.
|
||||
- Требуется host-доступ к `docker build`/`compose` под slin (как для `--deploy`) и writable
|
||||
build-context (worktree) — заложено инфра-требованиями (07).
|
||||
- Новая под-компонента (QG `check_staging_image_fresh` + режим хука `--build-staging`) →
|
||||
`arch:major-change`.
|
||||
|
||||
## Альтернативы (отклонены)
|
||||
|
||||
- **Только B.** Deadlock без авто-пересборки (BR-5/AC-6). ❌
|
||||
- **Только A.** Нет утверждения в момент retag → гонка/отключение снова даёт тихий промоут
|
||||
(AC-2/AC-3). ❌
|
||||
- **Rebuild в хуке на Phase B (прод-сторона).** Уничтожает BUILD-ONCE (прод-rebuild) и
|
||||
промоутит образ, который staging-e2e никогда не валидировал. ❌
|
||||
- **Rebuild напрямую из контейнера через docker.sock.** В образе нет docker CLI/compose;
|
||||
staging-операции и так host-side (ssh). ❌
|
||||
|
||||
## Связанные ADR
|
||||
Глобальный: `docs/architecture/adr/adr-0008-staging-image-provenance.md`.
|
||||
`adr-0007-executable-self-deploy` (ORCH-36, BUILD-ONCE), `adr-0006-merge-gate` (ORCH-43,
|
||||
образец edge-под-гейта), `adr-0003-staging-gate` (ORCH-35, условность), `adr-0005`
|
||||
(run-as-host-uid).
|
||||
71
docs/work-items/ORCH-058/07-infra-requirements.md
Normal file
71
docs/work-items/ORCH-058/07-infra-requirements.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Инфра-требования — ORCH-058
|
||||
|
||||
Work Item ID: ORCH-058
|
||||
|
||||
Топология не меняется (тот же сервер mva154, те же контейнеры 8500/8501, общая БД). Меняется
|
||||
**что делает self-deploy на ребре `deploy-staging → deploy`** для self-hosting. Полная
|
||||
топология/риски — `docs/operations/INFRA.md` (обновить в том же PR).
|
||||
|
||||
## IR-1. Host-сборка staging-образа (Стратегия A)
|
||||
|
||||
Шаг свежести пересобирает `orchestrator-orchestrator-staging` на ХОСТЕ (docker CLI/compose
|
||||
есть на хосте, НЕ в контейнере — образ ставит только `openssh-client git`). Требуется:
|
||||
|
||||
- Рабочий ssh `slin@127.0.0.1` (уже есть, ORCH-36 / LESSONS п.1–2: passwd-запись uid 1000,
|
||||
ключ смонтирован, `ORCH_DEPLOY_*` префиксы).
|
||||
- На хосте под `slin` доступны `docker build` и `docker compose --profile staging`
|
||||
(recreate 8501). Группа docker (`group_add: "999"` / host-доступ к `docker.sock`) — уже
|
||||
настроено.
|
||||
- **Build-context = host-путь worktree** валидированной ветки
|
||||
(`/home/slin/repos/_wt/<repo>/<branch-slug>`), читаемый под `slin`. Worktree уже
|
||||
создаётся launcher'ом/merge-gate под slin (ADR-0005 run-as-host-uid) — права ок.
|
||||
- Лог-директория хука writable под slin (`/var/log/orchestrator`, LESSONS п.3) — уже.
|
||||
|
||||
## IR-2. Вывод host-пути worktree
|
||||
|
||||
В контейнере worktree виден как `ORCH_WORKTREES_DIR=/repos/_wt/...`; на хосте — как
|
||||
`/home/slin/repos/_wt/...`. Маппинг = замена `repos_dir → host_repos_dir` (как
|
||||
`self_deploy.host_state_dir`). Реализация: производный helper host-worktree-пути, либо новая
|
||||
настройка `ORCH_HOST_WORKTREES_DIR` (дефолт `/home/slin/repos/_wt`). Без неё — деривация из
|
||||
`host_repos_dir`.
|
||||
|
||||
## IR-3. OCI-лейбл происхождения (Стратегия B)
|
||||
|
||||
`Dockerfile`: `ARG GIT_SHA` + `LABEL org.opencontainers.image.revision=$GIT_SHA`. Сборки БЕЗ
|
||||
build-arg (ручные/legacy) дают пустой лейбл → B fail-closed (это by design, не регрессия:
|
||||
прод-retag без доказуемого провенанса должен падать). Любой существующий способ сборки прод/
|
||||
staging-образа (CI, ручной) при включённой фиче ОБЯЗАН передавать `--build-arg GIT_SHA=<sha>`,
|
||||
иначе деплой задачи fail-fast'нется на guard. Шаг A это делает автоматически.
|
||||
|
||||
## IR-4. ssh-режим хука `--build-staging`
|
||||
|
||||
Новый режим `orchestrator-deploy-hook.sh --build-staging` запускается синхронно (рестарт
|
||||
staging безопасен, detached/finalizer не нужны — в отличие от Phase B прод). Дефолты режима —
|
||||
STAGING-safe (`TARGET_PORT=8501`, `--profile staging`). Прод (8500) этим режимом НЕ
|
||||
затрагивается.
|
||||
|
||||
## IR-5. Конфигурация (env, префикс `ORCH_`)
|
||||
|
||||
- `ORCH_IMAGE_FRESHNESS_ENABLED` (дефолт true) — единый kill-switch A+B.
|
||||
- `ORCH_IMAGE_FRESHNESS_REPOS` (дефолт пусто → self-hosting).
|
||||
- (опц.) `ORCH_HOST_WORKTREES_DIR` (дефолт `/home/slin/repos/_wt`).
|
||||
|
||||
`EXPECTED_REVISION` для хука строится в `build_deploy_command` — отдельной настройки не
|
||||
требует. `deploy_prod_source_image` (= `orchestrator-orchestrator-staging`) переиспользуется.
|
||||
|
||||
## IR-6. Безопасность self-hosting (инварианты)
|
||||
|
||||
- Любые `docker build` / `compose up` / recreate — ТОЛЬКО staging (8501); прод (8500) не
|
||||
рестартуется в рамках шага свежести.
|
||||
- `main` не пушится; force-only — `--force-with-lease` на ветку задачи (merge-gate, без
|
||||
изменений). Шаг A не пушит ничего (только локальный `docker build`).
|
||||
- B-guard срабатывает ДО `docker tag`/restart — прод не трогается на сомнении.
|
||||
|
||||
## IR-7. Bootstrap-чеклист (урок ORCH-36 «сквозной»)
|
||||
|
||||
Перед мержем ORCH-058 — **реальный** прогон в staging-петле (не только бумажные гейты):
|
||||
сборка staging из worktree с GIT_SHA → лейбл присутствует
|
||||
(`docker image inspect ... revision`) → recreate 8501 → `staging_check` зелёный →
|
||||
`build_deploy_command` отдаёт непустой `EXPECTED_REVISION` → хук-guard пропускает при
|
||||
совпадении и `exit 1` при подмене `SOURCE_IMAGE` на устаревший. Зафиксировать в bootstrap-
|
||||
заметке (как LESSONS_ORCH-036).
|
||||
16
docs/work-items/ORCH-058/10-tech-risks.md
Normal file
16
docs/work-items/ORCH-058/10-tech-risks.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Технические риски — ORCH-058
|
||||
|
||||
Work Item ID: ORCH-058
|
||||
|
||||
| ID | Риск | Вероятность / Влияние | Митигация |
|
||||
|----|------|----------------------|-----------|
|
||||
| R-1 | **Полу-конфигурация «B без A»** → вечный fail-fast деплоя (B падает, никто не пересобирает) | Низк. / Высок. (deadlock, BR-5) | Единый kill-switch `image_freshness_enabled` включает/выключает A и B **как целое**; раздельных флагов A/B нет. Дефолт — оба включены. AC-6. |
|
||||
| R-2 | **Рассинхрон якоря**: merge-gate делает rebase ПОСЛЕ того, как агент прогнал staging_check → HEAD изменился | Сред. / Сред. | Якорь берётся ПОСЛЕ merge-gate; шаг A пересобирает из post-rebase HEAD; авторитетный staging_check — против свежего образа. Pre-check агента — soft. |
|
||||
| R-3 | **Гонка**: между пересборкой A и Phase B human-approve worktree HEAD сместился | Низк. / Высок. | B сверяет лейбл образа с `EXPECTED_REVISION`=validated_revision на момент Phase B; рассинхрон → fail-closed `exit 1`, прод не трогается. AC-2/AC-3. |
|
||||
| R-4 | **Пустой лейбл** (ручная/legacy/CI-сборка без `--build-arg GIT_SHA`) | Сред. / Высок. | Fail-closed: пустой лейбл → несоответствие → `exit 1`. By design. Шаг A всегда передаёт GIT_SHA. IR-3 фиксирует требование к любым сборкам. |
|
||||
| R-5 | **Латентность ребра**: +docker build staging +recreate +повторный staging_check перед approve | Высок. / Низк. | Bounded timeouts; выполняется в monitor-треде (как merge-gate re-test). `staging_check --mode stub` без LLM-трат. Приемлемо. |
|
||||
| R-6 | **Сборка/recreate случайно затронет прод (8500)** | Низк. / Критич. | Режим хука `--build-staging` со STAGING-safe дефолтами (8501, `--profile staging`); код шага A никогда не передаёт прод-параметры. AC-9. Тест-инвариант: цель != прод. |
|
||||
| R-7 | **docker build на хосте падает** (нет места, недоступен daemon, битый worktree) | Низк. / Сред. | never-raise: `check_staging_image_fresh` → `(False, reason)` + release lease → откат на `development` (не зависание, не тихий промоут). AC-8. |
|
||||
| R-8 | **Двойной staging_check** воспринят как баг/лишняя трата | Сред. / Низк. | Документировано как намеренное (soft pre-check агента vs авторитетный код против промоутимого образа). Будущая оптимизация — облегчить шаг A. |
|
||||
| R-9 | **Самохостинг-bootstrap**: фича не действует, пока сама не в проде (старый прод-образ без лейбла) | Высок. (однократно) / Сред. | Bootstrap-чеклист (IR-7): первый реальный staging-прогон + ручной разрыв; B обратносовместим (без `EXPECTED_REVISION` — старое поведение), раскат поэтапный через флаг. |
|
||||
| R-10 | **Деградация не-self репо** | Низк. / Высок. | Условность (`image_freshness_repos` пусто → только orchestrator); для прочих — `(True, "N/A")` + хук без `EXPECTED_REVISION` = прежний путь. AC-5. |
|
||||
Reference in New Issue
Block a user