From 1d1208c13635ce72c7f1d6df50d3a56bbc579479 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Sun, 7 Jun 2026 12:22:46 +0000 Subject: [PATCH] architect(ET): auto-commit from architect run_id=297 --- docs/architecture/README.md | 12 +- docs/architecture/adr/README.md | 8 + .../adr/adr-0009-staging-infra-tolerance.md | 56 +++++ .../06-adr/ADR-001-staging-infra-tolerance.md | 222 ++++++++++++++++++ .../ORCH-061/07-infra-requirements.md | 37 +++ .../ORCH-061/08-data-requirements.md | 20 ++ docs/work-items/ORCH-061/10-tech-risks.md | 23 ++ 7 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 docs/architecture/adr/adr-0009-staging-infra-tolerance.md create mode 100644 docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md create mode 100644 docs/work-items/ORCH-061/07-infra-requirements.md create mode 100644 docs/work-items/ORCH-061/08-data-requirements.md create mode 100644 docs/work-items/ORCH-061/10-tech-risks.md diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 1cc866f..c578e55 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -42,6 +42,16 @@ created → analysis → architecture → development → review → testing → ### Условный staging-гейт (ORCH-35) `check_staging_status` реален только для self-hosting (`is_self_hosting_repo(repo)` → `orchestrator`); для остальных проектов → no-op `(True, "Staging gate N/A")`. Для orchestrator парсит `staging_status:` из `15-staging-log.md`; FAILED → откат на `development`. Подробнее: [ADR-0003](adr/adr-0003-staging-gate.md). +### Толерантность staging-вердикта к инфра-FAIL (ORCH-061 — design) +Self-hosting зацикливался на `deploy-staging`: `scripts/staging_check.py` давал ложный FAILED на C9a/C9b (ветка в sandbox / analyst-job в очереди), вызванный **отсутствием sandbox-настроек** (bot-аккаунты не члены SANDBOX-проекта), а не регрессом кода → откат `deploy-staging → development` → петля. ORCH-061 классифицирует проверки suite на **REAL** (pipeline) и **SANDBOX_INFRA** (узкий allowlist `{C9a, C9b}`) и делает вердикт толерантным к инфра-FAIL, сохраняя fail-closed для реальных проверок: +- Чистая логика — leaf-модуль `src/staging_verdict.py` (`classify_check`, `compute_staging_verdict`, never-raise). Упала хоть одна REAL → FAILED/exit1; упали ТОЛЬКО SANDBOX_INFRA и толерантность вкл → SUCCESS/exit0 (waived); waiver применяется только когда все REAL (вкл. C7/C8) зелёные. +- `scripts/staging_check.py` помечает проверки категориями, считает вердикт через `staging_verdict`, печатает `INFRA-WAIVED` (наблюдаемость). +- Kill-switch `staging_infra_tolerance_enabled` (env `ORCH_STAGING_INFRA_TOLERANCE_ENABLED`, дефолт `true`, в `.env.staging`); `false` → 1:1 прежнее строгое поведение. +- `check_staging_status` / `_parse_staging_status` / `STAGE_TRANSITIONS` / реестр `QG_CHECKS` — **без изменений** (новый QG-чек не вводится); условность ORCH-35 и схема БД сохранены. +- Инвариант: «no changes to commit» на action-стадиях (`deploy-staging`/`deploy`) не есть недовыполнение — продвижение определяется exit0 + гейт-вердиктом (launcher не откатывает; добавлена observability-строка). + +Подробнее: [adr-0009](adr/adr-0009-staging-infra-tolerance.md), детально — `docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md`. + ### Merge-gate: догон `main` + re-test + сериализация слияний (ORCH-043) Детерминированный под-гейт (`check_branch_mergeable`, без LLM) на ребре **`deploy-staging → deploy`**: исполняется ПОСЛЕ `check_staging_status` и ДО запуска deployer'а, который вливает PR в `main` (deployer мержит в начале стадии `deploy`). Стадии (`STAGE_TRANSITIONS`) НЕ меняются — это «под-гейт» ребра, а не отдельная стадия (триггер — то же событие «staging-deployer завершился»). @@ -201,4 +211,4 @@ never-raise на единицу работы; тишина при синхрон Схема БД, потоки данных, resilience-слой, детали Dockerfile — [internals.md](internals.md). --- -*Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой `deploy`, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, `docs/work-items/ORCH-060/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-060 (Guard 1 `developer_retry_count>=MAX_DEVELOPER_RETRIES` + Guard 2 `plane_sync.fetch_issue_state` Blocked/Needs-Input, флаг `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED`); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile).* +*Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой `deploy`, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, `docs/work-items/ORCH-060/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-060 (Guard 1 `developer_retry_count>=MAX_DEVELOPER_RETRIES` + Guard 2 `plane_sync.fetch_issue_state` Blocked/Needs-Input, флаг `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED`); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, `docs/work-items/ORCH-061/06-adr/ADR-001`) — design, ветка feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled).* diff --git a/docs/architecture/adr/README.md b/docs/architecture/adr/README.md index 529ac45..cac6d70 100644 --- a/docs/architecture/adr/README.md +++ b/docs/architecture/adr/README.md @@ -12,6 +12,14 @@ Per-work-item решения живут в `docs/work-items//06-adr/ADR-NNN- | adr-0005 | Контейнеры бегут под uid:gid хоста (1000:1000) | accepted | 2026-06-06 | ORCH-040 | | adr-0006 | Merge-gate (догон main + re-test + сериализация слияний) | proposed | 2026-06-06 | ORCH-043 | | adr-0007 | Reconciler застрявших стадий (sweeper потерянных webhook) | accepted | 2026-06-06 | ORCH-053 | +| adr-0007 | Исполняемый самодеплой стадии `deploy` (файл adr-0007-executable-self-deploy) | accepted | 2026-06-06 | ORCH-036 | +| adr-0008 | Провенанс staging-образа перед BUILD-ONCE retag | accepted | 2026-06-06 | ORCH-058 | +| adr-0009 | Толерантность staging-вердикта к инфраструктурным FAIL | accepted | 2026-06-07 | ORCH-061 | + +> ⚠️ Историческая коллизия: номер `0007` занят двумя файлами — +> `adr-0007-reconciler.md` (ORCH-053) и `adr-0007-executable-self-deploy.md` +> (ORCH-036). Оба accepted; для новых сквозных ADR использовать следующий +> свободный номер (текущий максимум — `0009`). ## Формат **Контекст → Решение → Альтернативы → Последствия → Связи.** Статус: proposed / accepted / superseded. diff --git a/docs/architecture/adr/adr-0009-staging-infra-tolerance.md b/docs/architecture/adr/adr-0009-staging-infra-tolerance.md new file mode 100644 index 0000000..5b576f0 --- /dev/null +++ b/docs/architecture/adr/adr-0009-staging-infra-tolerance.md @@ -0,0 +1,56 @@ +# adr-0009: Толерантность staging-вердикта к заведомо инфраструктурным FAIL + +- **Статус:** accepted +- **Дата:** 2026-06-07 +- **Задача:** ORCH-061 +- **Детально:** `docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md` + +## Контекст +Self-hosting `orchestrator` зацикливался на `deploy-staging`: `staging_check.py` +давал 2 ложных FAIL (C9a — ветка в sandbox, C9b — analyst-job в очереди), вызванных +отсутствием sandbox-настроек (bot-аккаунты не члены SANDBOX-проекта), а не регрессом +кода. `staging_check.py` делал `sys.exit(1)` при любом FAIL → deployer писал +`staging_status: FAILED` → `check_staging_status` FAILED → откат `deploy-staging → +development` → петля (жгла developer-ретраи и кредиты). Прод-деплой орка приходилось +доводить вручную — блокер автономного внедрения (ORCH-54). + +## Решение +Классифицировать проверки staging-suite на **REAL** (pipeline) и **SANDBOX_INFRA** +(заведомо инфраструктурные, узкий allowlist `{C9a, C9b}`) и сделать вердикт +толерантным к инфра-FAIL, сохранив fail-closed для реальных проверок: + +- Новый leaf-модуль `src/staging_verdict.py` (pure, never-raise, stdlib): + `classify_check(label)` + `compute_staging_verdict(items, infra_tolerant)`. + Правило: упала хоть одна REAL → FAILED/exit1; упали ТОЛЬКО SANDBOX_INFRA и + толерантность вкл → SUCCESS/exit0 (waived); толерантность выкл → legacy strict + (любой FAIL → FAILED). +- `scripts/staging_check.py` помечает проверки категориями, считает вердикт через + `staging_verdict`, печатает `INFRA-WAIVED` при вайвере (наблюдаемость). +- Kill-switch `staging_infra_tolerance_enabled` (env + `ORCH_STAGING_INFRA_TOLERANCE_ENABLED`, дефолт `True`; в `.env.staging`). +- `check_staging_status` / `_parse_staging_status` / `STAGE_TRANSITIONS` / реестр + `QG_CHECKS` — **без изменений**; новый QG-чек не вводится. Условность ORCH-35 + сохранена (не-self → no-op N/A). +- Инвариант FR-3: «no changes to commit» на action-стадиях (`deploy-staging`/`deploy`) + не есть недовыполнение — продвижение определяется exit0 + гейт-вердиктом + (launcher уже не откатывает; добавлена observability-строка). + +## Альтернативы +- Только починить sandbox-инфру (направление а) — хрупко, не структурно, вне + автономной досягаемости таска; оставлено как опциональное hardening. +- «Зелёный по умолчанию» при недоступности проверок — запрещён (fail-closed). +- Новый QG-чек / структурный артефакт `15-staging-log.md` — избыточно, меняло бы + контракты/реестр; толерантность размещена в suite до артефакта. + +## Последствия +- Петля устранена; страховка цела (реальный регресс → FAILED → откат). +- Чистая вердикт-логика юнит-тестируема без live staging/docker. +- Контракты гейтов/стадий/вердиктов/реестра и схема БД неизменны. +- Риск: узкое окно — реальный регресс именно в создании ветки/постановке + analyst-job может быть заваивен; митигировано allowlist'ом `{C9a,C9b}` + условием + «все REAL (вкл. C7/C8) зелёные» + INFRA-WAIVED-логом. Разблокирует ORCH-54. + +## Связи +adr-0003 (условный staging-гейт — база `is_self_hosting_repo` / `check_staging_status`), +adr-0006 (merge-gate), adr-0007 (исполняемый self-deploy), adr-0008 (провенанс +staging-образа). Блокирует ORCH-54. diff --git a/docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md b/docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md new file mode 100644 index 0000000..ac68910 --- /dev/null +++ b/docs/work-items/ORCH-061/06-adr/ADR-001-staging-infra-tolerance.md @@ -0,0 +1,222 @@ +# ADR-001 — Толерантность staging-вердикта к инфра-FAIL + инвариант «no-changes на action-стадии» + +- **Статус:** Accepted +- **Дата:** 2026-06-07 +- **Задача:** ORCH-061 (BUG, P0) · Репо: `orchestrator` (self-hosting) +- **Связи:** ORCH-35/adr-0003 (условный staging-гейт), ORCH-36/adr-0007 (исполняемый self-deploy), ORCH-58/adr-0008 (провенанс staging-образа), ORCH-43/adr-0006 (merge-gate); блокирует ORCH-54. +- **Сквозной ADR:** [adr-0009-staging-infra-tolerance](../../../architecture/adr/adr-0009-staging-infra-tolerance.md) + +--- + +## Контекст + +На стадии `deploy-staging` self-hosting `orchestrator` зацикливается: +`check_staging_status` даёт FAILED → `_handle_qg_failure_rollbacks` откатывает +`deploy-staging → development` → developer перезапускается → конвейер заново → +снова `deploy-staging` → снова FAILED. Петля жжёт developer-ретраи и LLM-кредиты, +а прод-деплой орка приходится доводить вручную (ORCH-58, ORCH-60). Это прямой +блокер автономного внедрения (ORCH-54). + +Подтверждены две независимые причины (BRD §2): + +**Причина №1 — ложный FAILED.** `scripts/staging_check.py` в sandbox даёт +8/10 PASS, 2 ложных FAIL на e2e-блоке C: +- **C9a** — ветка не появляется в `orchestrator-sandbox`; +- **C9b** — analyst-job не появляется в staging-очереди. + +Оба завязаны на отсутствие sandbox-настроек (bot-аккаунты `ORCH_PLANE_BOT_*` не +добавлены членами SANDBOX-проекта — проект создан после провижининга ботов). Это +**отсутствие инфраструктуры sandbox, а не регресс кода**. Но `staging_check.py` +суммирует `all_ok = passed == total` и делает `sys.exit(1)` при любом FAIL → +deployer пишет `staging_status: FAILED` → откат. + +**Причина №2 — «no changes to commit» на action-стадии.** Стадии деплоя по природе +действие (рестарт/retag), а не правка `src/`. Отсутствие git-изменений не должно +трактоваться как недовыполнение; критерий успеха action-стадии — exit0 + +health/staging-вердикт, а не наличие коммита. + +### Что есть сейчас в коде (точки дефекта) + +- `scripts/staging_check.py`: `Results.summary()` → `all_ok = passed == total`; + `main()` → `sys.exit(0 if all_ok else 1)`. Все проверки равнозначны — инфра-FAIL + неотличим от регресса. +- `src/qg/checks.py` → `check_staging_status` / `_parse_staging_status`: читает + `staging_status:` (SUCCESS|FAILED) из `15-staging-log.md`. Условный (ORCH-35): + для не-self репо → `(True, "Staging gate N/A …")`. +- `src/stage_engine.py` → `_handle_qg_failure_rollbacks`: ветка + `agent=="deployer" and qg=="check_staging_status"` → откат на `development`. +- `src/agents/launcher.py` → `_monitor_agent`: ветка «no changes to commit» (строка + ~583) **уже** просто логирует и идёт в `_try_advance_stage` (НЕ откатывает). + +## Рассмотренные направления (BRD §6) + +- **(а) Починить sandbox-инфру** — добавить bot-токены SANDBOX, чтобы C9a/C9b + проходили честно (10/10). + - *Минусы:* хрупко (зависит от членства ботов в Plane-проекте, поддерживается + руками вне кода); не предотвращает структурно будущие инфра-only FAIL; + автономный self-deploy-таск не может надёжно выполнить Plane-admin действия сам. + Не закрывает Причину №1 на уровне инварианта. +- **(б) Отвязать вердикт от заведомо инфраструктурных проверок** — классифицировать + проверки suite и сделать вердикт толерантным к инфра-FAIL, сохранив fail-closed + для реальных проверок. + - *Плюсы:* структурно, юнит-тестируемо (чистая вердикт-логика), управляемо + (kill-switch), наблюдаемо (FR-7); сохраняет страховку (FR-4) по построению. + +## Решение + +Выбран механизм **(б)** как основной, с явной фиксацией инварианта по Причине №2. +Направление (а) переведено в **необязательное hardening** (см. `07-infra-requirements.md`): +с (б) оно перестаёт быть блокером. + +### 1. Классификация проверок + толерантный вердикт (Причина №1, FR-2/FR-4) + +Новый **leaf-модуль `src/staging_verdict.py`** — чистая логика, без I/O, контракт +**never-raise**, только stdlib (импортируем и из orchestrator, и из +`staging_check.py`, который уже импортирует `src.*` внутри контейнера — паттерн B6/ORCH-048): + +``` +REAL = "real" # реальная pipeline-проверка +SANDBOX_INFRA = "sandbox_infra" # заведомо зависит от sandbox-инфры + +# Узкий allowlist известных инфра-проверок (по префиксу метки): +SANDBOX_INFRA_CHECKS = frozenset({"C9a", "C9b"}) + +def classify_check(label: str) -> str: + """SANDBOX_INFRA если метка начинается с известного инфра-префикса, иначе REAL. + Never-raise: на любом непонятном вводе → REAL (консервативно, fail-closed).""" + +def compute_staging_verdict(items, infra_tolerant: bool) -> StagingVerdict: + """items: список (label, passed: bool, category: str). + real_failed = [REAL-проверки с passed=False] + infra_failed = [SANDBOX_INFRA-проверки с passed=False] + - real_failed непусто -> FAILED, exit 1 (страховка) + - infra_failed непусто и infra_tolerant -> SUCCESS, exit 0 (waived) + - infra_failed непусто и НЕ infra_tolerant -> FAILED, exit 1 (legacy strict) + - иначе -> SUCCESS, exit 0 + Never-raise: на битом вводе → консервативный FAILED.""" +``` + +`StagingVerdict` несёт `status` (`"SUCCESS"|"FAILED"`), `exit_code` (`0|1`), +`waived` (список заваиверенных меток) и `summary` (человекочитаемая строка). + +**Ключевой инвариант страховки (FR-4):** любая упавшая REAL-проверка ⇒ exit 1 ⇒ +FAILED ⇒ откат. В частности C7 (создать issue) и C8 (триггер `/webhook/plane`) — +REAL. Waiver применяется к C9a/C9b **только** когда все REAL-проверки (включая +C7/C8) зелёные. Вход в конвейер по-прежнему валидируется C7/C8; C9a/C9b проверяют +лишь downstream-артефакты, которым нужна sandbox-инфра. Так blast-radius waiver'а +сведён к двум именованным проверкам. + +### 2. Правки `scripts/staging_check.py` + +- `Results.add(label, passed, detail="", category=None)` — при `category is None` + авто-классификация через `staging_verdict.classify_check(label)`; хранит категорию + в элементе. +- `Results.summary()` печатает разбивку по категориям (REAL / SANDBOX_INFRA). +- `main()`: + - резолвит флаг толерантности `_resolve_tolerance()` (см. ниже); + - `verdict = compute_staging_verdict(results.items, infra_tolerant)`; + - при `verdict.waived` печатает явную строку + `INFRA-WAIVED: (known sandbox-infra; real checks green)` (FR-7); + - `sys.exit(verdict.exit_code)`. +- `_resolve_tolerance()`: читает `settings.staging_infra_tolerance_enabled` (через + `from src.config import settings` — тот же паттерн, что B6). На ошибке импорта → + **strict (False)** (fail-safe: не вайвить при нечитаемом конфиге) + warning. + Опциональный CLI-флаг `--strict` принудительно выключает толерантность для ручных + «честных» прогонов. + +Прежние режимы (`--mode stub|full-real`) и проверки A/B/C7/C8 — без изменений. +«Всегда 0» исключено: упавшая REAL-проверка всегда даёт exit 1 (TRZ §7). + +### 3. Kill-switch (FR-6, AC-7) + +`src/config.py`: +```python +# ORCH-061: толерантность staging-вердикта к заведомо инфраструктурным FAIL +# (C9a/C9b) в sandbox. True -> упавшие ТОЛЬКО sandbox-инфра проверки вайверятся +# (real-проверки fail-closed). False -> 1:1 прежнее строгое поведение (любой FAIL +# -> staging_status FAILED -> откат). Env ORCH_STAGING_INFRA_TOLERANCE_ENABLED. +staging_infra_tolerance_enabled: bool = True +``` + +Дефолт **True** (как `merge_gate_enabled` / `image_freshness_enabled` / +`self_deploy_enabled`): инвариант страховки (FR-4) держится независимо от флага — +реальные провалы всё равно fail-closed; флаг существует, чтобы мгновенно вернуть +legacy-строгость без передеплоя кода. Флаг живёт в `.env.staging` контейнера +(`ORCH_` prefix), поэтому достижим скриптом внутри `orchestrator-staging`. +`False` → suite строгий → 1:1 поведение до ORCH-061 (AC-7). + +### 4. Что НЕ меняется (контракты, AC-8) + +- `check_staging_status` / `_parse_staging_status` — **без изменений**: читают + `staging_status:` (только YAML, `SUCCESS|FAILED`). Толерантность реализована + ДО артефакта (в exit-code suite → вердикт deployer), внутри существующего пути + staging-вердикта, не отдельной стадией (TRZ §6). +- **Новый QG-чек НЕ добавляется** → реестр `QG_CHECKS` и снапшот-тест + (`tests/test_qg_registry_snapshot.py`) неизменны (AC-8 / TC-11). +- `STAGE_TRANSITIONS`, `get_previous_stage`, exit-code хука деплоя (0/1/2), + `map_exit_code_to_status`, `check_deploy_status`, БАГ-8 — без изменений. +- Условность self-hosting (AC-6): `staging_check.py` канонически бежит только для + `orchestrator`; `check_staging_status` для не-self репо остаётся + `(True, "Staging gate N/A …")`. Поведение прочих репо байт-в-байт неизменно. + +### 5. Инвариант «no-changes на action-стадии» (Причина №2, FR-3/AC-4) + +`launcher._monitor_agent` **уже** не откатывает на «no changes to commit» (просто +логирует и идёт в `_try_advance_stage`; продвижение определяется гейтом). ORCH-061: +- **Фиксируем инвариант** как покрытый тестами контракт: на `deploy-staging`/`deploy` + для self-deploy продвижение определяется exit0 + гейт-вердиктом, НИКОГДА наличием + коммита (TC-06). +- **Наблюдаемость (FR-7/AC-11):** в ветке «no changes» логировать явную строку, + отличающую action-стадию (ожидаемо: артефакт-вердикт, не обязательно код) от + code-стадии. Резолв стадии задачи по `(repo, branch)`; при + `stage ∈ {deploy-staging, deploy}` и `self_deploy.self_deploy_applies(repo)` → + `staging/deploy: no code changes (expected on action stage)`. +- **Regression-guard (TC-07):** на `development` (code-стадия) поведение «no changes» + неизменно — изменение FR-3 не протекает на не-action стадию. + +Изменение минимальное (self-hosting safety, AC-12): не трогает прод-контейнер 8500, +сборки/recreate — только staging (8501). + +## Затронутые файлы (для developer) + +| Файл | Изменение | +|------|-----------| +| `src/staging_verdict.py` | **новый** leaf-модуль: `classify_check`, `compute_staging_verdict`, `StagingVerdict` (pure, never-raise). | +| `scripts/staging_check.py` | категории в `Results`, вердикт через `staging_verdict`, INFRA-WAIVED-лог, `--strict`. | +| `src/config.py` | флаг `staging_infra_tolerance_enabled` (env `ORCH_STAGING_INFRA_TOLERANCE_ENABLED`). | +| `src/agents/launcher.py` | observability-лог action-stage no-changes (без смены логики продвижения). | +| `.openclaw/agents/deployer.md` | уточнение: exit0 может включать «infra-waived»; контракт `staging_status:` SUCCESS\|FAILED неизменен. | +| `docs/operations/STAGING_CHECK.md` | поведение C9a/C9b, флаг, INFRA-WAIVED, `--strict`. | +| `docs/architecture/README.md` | пометка ORCH-061 в разделе staging-гейта (уже внесена архитектором). | +| `CHANGELOG.md` | запись ORCH-061. | +| `tests/` | TC-01…TC-14 (см. `04-test-plan.yaml`). | + +## Последствия + +**Плюсы** +- Петля устранена структурно: ложный инфра-FAIL → SUCCESS (waived) → нет отката (G1/G2). +- Страховка цела: любая реальная pipeline-проверка fail-closed → FAILED → откат (G4/FR-4). +- Чистая вердикт-логика юнит-тестируема без live staging/docker (NFR-тестируемость). +- Контракты гейтов/стадий/вердиктов/реестра не тронуты (AC-8); схема БД не меняется (AC-9). +- Мгновенный откат к legacy через kill-switch (AC-7). +- Разблокирует автономный self-deploy (ORCH-54). + +**Минусы / ограничения** +- C9a/C9b теперь могут заваиверить **реальный** даунстрим-регресс именно в создании + ветки / постановке analyst-job (узкий риск). Митигировано: waiver только когда C7/C8 + и все прочие REAL зелёные; allowlist жёстко = {C9a, C9b}; INFRA-WAIVED логируется и + виден оператору. См. `10-tech-risks.md` (R-1). +- Толерантность скрывает «нездоровье sandbox» как зелёное-с-допущением; отличимо + только по INFRA-WAIVED-логу/комментарию (наблюдаемость обязательна, FR-7). +- Honest 10/10 в sandbox (направление а) остаётся желательным hardening, но не блокером. + +## Альтернативы (отклонены) + +- **Только (а) — починить sandbox-инфру:** хрупко, не структурно, вне автономной + досягаемости таска. Оставлено как опциональное hardening. +- **«Зелёный по умолчанию» при недоступности проверок:** запрещён FR-4 (fail-closed). +- **Новый QG-чек `check_staging_infra_tolerant`:** избыточно — менял бы реестр + `QG_CHECKS` и снапшот; толерантность лучше живёт в suite/вердикте до артефакта. +- **Толерантность внутри `check_staging_status` через структурный артефакт:** + потребовал бы сменить контракт `15-staging-log.md` и научить deployer писать + per-check категории — больше движущихся частей; отклонено в пользу решения в suite. diff --git a/docs/work-items/ORCH-061/07-infra-requirements.md b/docs/work-items/ORCH-061/07-infra-requirements.md new file mode 100644 index 0000000..be702cc --- /dev/null +++ b/docs/work-items/ORCH-061/07-infra-requirements.md @@ -0,0 +1,37 @@ +# 07 — Требования к инфраструктуре: ORCH-061 + +Work Item: **ORCH-061** · Репо: `orchestrator` + +Топология/контейнеры/порты **не меняются** (TRZ §3, §9). Self-hosting-безопасность +сохранена: прод-контейнер `orchestrator` (8500) не перезапускается/не роняется в +рамках задачи; любые сборки/recreate — только staging (8501). См. +`docs/operations/INFRA.md`. + +## IR-1 — Конфиг-флаг (kill-switch) +Новый флаг `staging_infra_tolerance_enabled` (env +`ORCH_STAGING_INFRA_TOLERANCE_ENABLED`, дефолт `true`). + +- Должен присутствовать в окружении контейнера **`orchestrator-staging`** + (`.env.staging`), т.к. `scripts/staging_check.py` читает его через + `src.config.settings` при каноническом запуске `docker exec` внутри стенда. +- Для прод-инстанса (`.env`) флаг безвреден (на прод-пути staging-suite не + исполняется), но рекомендуется держать значения консистентными. +- `false` → мгновенный возврат к строгому (legacy) поведению без передеплоя кода. +- Канон секретов/env: значения в `.env`/`.env.staging` на хосте, в гит НЕ + коммитятся; задокументировать ключ в `.env.example` (канон ORCH-9). + +## IR-2 — Опциональное hardening sandbox (направление «а», НЕ блокер) +Первопричина ложных C9a/C9b — bot-аккаунты агентов (`ORCH_PLANE_BOT_*`) не добавлены +членами Plane-проекта **SANDBOX** (`8c5a3025-…`), созданного после провижининга +ботов. С выбранным механизмом (б) это перестаёт блокировать конвейер, но честный +10/10 в sandbox желателен: + +- Добавить bot-аккаунты агентов членами SANDBOX-проекта в Plane (даст честный + C9b: коммент analyst'а перестанет получать 403; и устранит инфра-причину C9a/C9b). +- Действие — ручное (Plane-admin), вне автоматической досягаемости таска; выполняется + оператором при возможности. После него C9a/C9b проходят честно и waiver не нужен. +- Это hardening, а не требование приёмки ORCH-061 (приёмка — на механизме «б»). + +## IR-3 — Без новой инфраструктуры +Новые сервисы/порты/тома/сетевые правила/cron — **не требуются**. Никаких +изменений в `docker-compose.yml`, образах, реестре проектов. diff --git a/docs/work-items/ORCH-061/08-data-requirements.md b/docs/work-items/ORCH-061/08-data-requirements.md new file mode 100644 index 0000000..0c67ced --- /dev/null +++ b/docs/work-items/ORCH-061/08-data-requirements.md @@ -0,0 +1,20 @@ +# 08 — Требования к данным / схеме БД: ORCH-061 + +Work Item: **ORCH-061** · Репо: `orchestrator` + +## DR-1 — Схема БД не меняется (AC-9) +Никаких миграций. Таблицы `events`, `tasks`, `agent_runs`, `jobs` — без изменений +колонок/индексов/таблиц. + +## DR-2 — Никакого нового персистентного состояния +Решение (ADR-001) — чистая вердикт-логика (`src/staging_verdict.py`) + конфиг-флаг + +правка exit-code suite. Состояние конвейера не вводится: +- толерантность вычисляется на лету при прогоне `staging_check.py`; +- restart-safe-состояние не требуется (вердикт фиксируется в существующем артефакте + `15-staging-log.md` через прежний контракт `staging_status: SUCCESS|FAILED`). + +## DR-3 — Артефакт-контракт неизменен +`15-staging-log.md` по-прежнему несёт frontmatter `staging_status: SUCCESS|FAILED` +(только YAML). `14-deploy-log.md` (`deploy_status:`) — без изменений. Гейты читают +ТОЛЬКО frontmatter. Толерантность реализована ДО записи артефакта (на уровне +exit-code suite → вердикт deployer), поэтому формат и парсинг артефактов не трогаются. diff --git a/docs/work-items/ORCH-061/10-tech-risks.md b/docs/work-items/ORCH-061/10-tech-risks.md new file mode 100644 index 0000000..f13db54 --- /dev/null +++ b/docs/work-items/ORCH-061/10-tech-risks.md @@ -0,0 +1,23 @@ +# 10 — Технические риски: ORCH-061 + +Work Item: **ORCH-061** · Репо: `orchestrator` (self-hosting) + +| # | Риск | Вероятн. | Влияние | Митигация | +|---|------|----------|---------|-----------| +| **R-1** | Waiver C9a/C9b маскирует **реальный** регресс именно в создании ветки / постановке analyst-job (ложно-зелёный staging). | Низкая | Высокое | Allowlist жёстко `{C9a, C9b}`; waiver применяется ТОЛЬКО когда ВСЕ REAL-проверки зелёные, включая C7 (создать issue) и C8 (триггер `/webhook/plane`) — вход в конвейер всегда валидируется реально. `INFRA-WAIVED`-строка в логе/комменте делает допущение видимым (FR-7). Honest 10/10 (IR-2) убирает риск совсем. | +| **R-2** | Ослабление страховки: реальный pipeline-FAIL пройдёт как SUCCESS. | Низкая | Критич. | Инвариант `compute_staging_verdict`: любая упавшая REAL → exit1 → FAILED → откат (FR-4/AC-3/TC-05). Покрыто юнит-тестом отдельным кейсом. | +| **R-3** | Флаг не достигает скрипта (читается не из того env) → толерантность «молча» не работает или, наоборот, не выключается. | Средняя | Среднее | Скрипт читает `settings.staging_infra_tolerance_enabled` через `from src.config import settings` — тот же канал, что B6/ORCH-048 (внутри `orchestrator-staging`, env `.env.staging`). На ошибке импорта — fail-safe в strict (False) + warning. Документировать ключ в `.env.staging`/`.env.example` (IR-1). Тест kill-switch (TC-09). | +| **R-4** | Классификатор ошибочно пометит REAL-проверку как SANDBOX_INFRA (расширение allowlist в будущем). | Низкая | Высокое | `classify_check` — узкий префиксный allowlist; добавление новой инфра-метки требует осознанного PR + теста (TC-03). По умолчанию неизвестная метка → REAL (консервативно). | +| **R-5** | Регресс совместимости: изменение exit-code suite ломает другие потребители (deploy-хук, ручные прогоны). | Низкая | Среднее | Exit-code семантика сохранена для honest-прогонов (всё PASS → 0; реальный FAIL → 1). Меняется лишь трактовка «только инфра-FAIL» (теперь 0 при толерантности). Deployer-маппинг exit0→SUCCESS/≠0→FAILED не меняется; deployer.md уточняется. `--strict` даёт ручной honest-режим. | +| **R-6** | never-raise нарушен: исключение из `staging_verdict`/классификатора. | Низкая | Среднее | `src/staging_verdict.py` — pure, без I/O; контракт never-raise (на битом вводе → консервативный FAILED). Логика вне пути `advance_stage` (исполняется в subprocess suite), поэтому в конвейер исключение структурно не попадает (AC-10). | +| **R-7** | FR-3: правка no-changes протекает на code-стадию (`development`) и маскирует «developer ничего не сделал». | Низкая | Среднее | Observability-строка ограничена `stage ∈ {deploy-staging, deploy}` и `self_deploy_applies(repo)`; логика продвижения launcher не меняется. Regression-guard TC-07. | +| **R-8** | Self-hosting: правки случайно затронут прод 8500 / не-self репо. | Низкая | Критич. | Изменения только на self-deploy-пути и в suite (бежит лишь для `orchestrator`-staging). `check_staging_status` для не-self репо неизменно `(True, N/A)` (AC-6/TC-08). Сборки/recreate — только 8501. Прод 8500 не трогается (AC-12). | + +## Контрактные инварианты (не нарушать) +- `STAGE_TRANSITIONS`, `get_previous_stage` — без изменений. +- Реестр `QG_CHECKS` — без изменений; новый QG-чек НЕ вводится (снапшот-тест зелёный, TC-11). +- Frontmatter `staging_status:` / `deploy_status:` — только YAML, `SUCCESS|FAILED`. +- Exit-code хука деплоя (0/1/2) и `map_exit_code_to_status` — без изменений. +- БАГ-8 (`deploy → development`) и ORCH-35 (`deploy-staging → development`) для + **реальных** провалов — сохранены. +- Схема БД — без миграций.