fix(staging): host-side ssh execution + env classification for staging-runner (ORCH-123)
The ORCH-115 deterministic staging-runner ran `docker exec` FROM INSIDE the prod `orchestrator` container, which ships only `openssh-client git curl` — no `docker` CLI (Dockerfile:11). `Popen(["docker", ...])` hit FileNotFoundError -> a PERMANENT environment defect that was mis-routed as a code-fail rollback `deploy-staging -> development` (burning developer-retries). Incident ORCH-116: every self-hosting task reaching deploy-staging was doomed to a false rollback. Fix (adr-0049, additive, flag-gated, never-raise, self-hosting scope; the gate / artifact contract / STAGE_TRANSITIONS / DB schema are byte-for-byte unchanged): - D1: build_staging_command() wraps the SAME `docker exec ... staging_check.py ... --mode stub` in `ssh <user@host> '<...>'` so it runs HOST-SIDE over the existing trusted ssh channel (mirror self_deploy / image_freshness). New flag staging_runner_exec_host_side (default True). No docker CLI/SDK added to the image, docker.sock not used in-container (D2 security). - D3: three-way classify_staging_outcome (suite-ran / permanent-env / transient-infra), disambiguating the exit=1 collision by scanning stderr. - D4: invariant "infra != code-fail" — permanent-env / exhausted transient-infra end in an infra-HOLD (no rollback, no developer-retry), NOT a false FAILED rollback (supersedes ORCH-115 D5). A really-executed failing suite still rolls back (anti-over-tolerance). R-2 verified: a held deploy-staging task is not rolled back by the reconciler. - D5: prod-like preflight() of the host-side channel at startup (main.lifespan, best-effort, never blocks). - D8: snapshot adds permanent_env / exec_host_side / preflight. Docs (golden source, same PR): INFRA.md execution-boundary section, architecture/README.md, CLAUDE.md, CHANGELOG.md, .env.example. Tests: tests/test_orch123_staging_runner_exec.py (TC-01 mandatory regression red->green; TC-02..TC-14 + R-2). ORCH-115 anti-drift green (3 tests updated for the D1/D4/D8 supersession). Full suite: 2131 passed. Refs: ORCH-123 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
36
CLAUDE.md
36
CLAUDE.md
@@ -456,6 +456,42 @@ Plane, **не** Quality Gate и **не** стадия).
|
||||
`tests/test_orch115_staging_runner.py` (TC-01…TC-13) + зелёные LLM-анти-дрейф тесты (TC-14). Детали —
|
||||
`docs/work-items/ORCH-115/06-adr/ADR-001-deterministic-staging-runner.md`, сквозной
|
||||
`docs/architecture/adr/adr-0048-deterministic-staging-runner.md`.
|
||||
- **ORCH-123 (host-side исполнение + классификация environment-дефекта, bug→escalate full-cycle,
|
||||
adr-0049):** фикс инцидента **ORCH-116** — раннер ORCH-115 вызывал `docker exec` **изнутри**
|
||||
прод-контейнера, где **нет docker CLI** (`Dockerfile:11` несёт только `openssh-client git curl`;
|
||||
`docker.sock` смонтирован, клиента нет) → `FileNotFoundError` → постоянный environment-дефект
|
||||
ложно маршрутизировался как код-фейл-откат `deploy-staging → development` (с расходом
|
||||
developer-retry); любая self-hosting задача на `deploy-staging` была обречена. Аддитивно, под
|
||||
флагами, never-raise, скоуп self-hosting; `STAGE_TRANSITIONS`/реестр `QG_CHECKS`/семантика и имена
|
||||
`check_staging_status`/`_parse_staging_status`/machine-verdict-ключи/схема БД — **байт-в-байт не
|
||||
тронуты** (замена *стратегии исполнения продюсера*, **не** гейта; зеркало ORCH-115 NFR-1).
|
||||
**(D1)** `build_staging_command` обёртывает ту же `docker exec orchestrator-staging … staging_check.py
|
||||
… --mode stub` в `ssh -o StrictHostKeyChecking=no <deploy_ssh_user>@<deploy_ssh_host> '<…>'`
|
||||
(зеркало `self_deploy`/`image_freshness`; канал доверенный `ORCH_DEPLOY_SSH_HOST=127.0.0.1`,
|
||||
ssh-ключ `:ro`, `openssh-client` в образе — новых секретов/привилегий нет; сюита по-прежнему бежит
|
||||
**внутри** `orchestrator-staging` 8501, меняется лишь инициатор `docker exec`). **(D2 security)**
|
||||
docker CLI/SDK в контейнер **не** добавляется, `docker.sock` **не** используется изнутри (это
|
||||
root-эквивалентное расширение поверхности атаки, доступное и LLM-агентам). **(D3)** двухуровневый
|
||||
исход ORCH-115 заменён **трёхсторонней** чистой `classify_staging_outcome` (зеркало
|
||||
`merge_gate.classify_retest_failure`): `suite-ran` (распознанный exit-код ≠255 **без** env-маркера в
|
||||
stderr → доверяем коду `0→SUCCESS`/`≠0→FAILED`; анти-over-tolerance BR-3), `permanent-env`
|
||||
(spawn-error `rc=None`/нет ssh-target/`rc∈{126,127}`/env-маркер `No such container`/`Cannot connect
|
||||
to the Docker daemon` → ретрай бессмыслен), `transient-infra` (timeout/ssh `rc=255`/неизвестно →
|
||||
ретрай осмыслен); дизамбигуация `exit=1`-коллизии — скан stderr на env-маркеры, не голый код;
|
||||
fail-safe → `transient-infra`. **(D4 инвариант «инфра ≠ код-фейл»)** `permanent-env` → немедленный
|
||||
**infra-HOLD** (DEFER пропускается, `15-staging-log.md` НЕ пишется, advance НЕ зовётся, developer-retry
|
||||
НЕ жжётся, alert «НЕ дефект кода»); `transient-infra` → bounded DEFER, на исчерпании — тоже
|
||||
infra-HOLD+alert (**супершид ORCH-115 D5** fail-closed-FAILED-отката). «Сюита **не** исполнилась»
|
||||
(env ИЛИ инфра) **никогда** не оканчивается код-фейл-откатом — закрывает RCA-класс ORCH-110 на
|
||||
staging-ребре; задача держится на `deploy-staging` (reconciler `advance_if_gate_passed` на red-гейте
|
||||
→ `None`, без отката, R-2). **(D5)** `preflight()` пробит host-side канал на старте `main.lifespan`
|
||||
(best-effort, never-blocks). **(D6)** новый флаг `staging_runner_exec_host_side=True`
|
||||
(env `ORCH_STAGING_RUNNER_EXEC_HOST_SIDE`); откат — `=false` (in-container 1:1) или
|
||||
`ORCH_STAGING_RUNNER_ENABLED=false` (LLM-deployer 1:1). **(D8)** счётчик `permanent_env` + `exec_host_side`
|
||||
+ `preflight` в блоке `staging_runner` `GET /queue`. Покрытие — `tests/test_orch123_staging_runner_exec.py`
|
||||
(TC-01 обязательный регресс ORCH-116 red→green; TC-02…TC-14 + R-2) + зелёный анти-дрейф ORCH-115.
|
||||
Детали — `docs/work-items/ORCH-123/06-adr/ADR-001-host-side-staging-execution-and-env-classification.md`,
|
||||
сквозной `docs/architecture/adr/adr-0049-host-side-docker-execution-boundary.md`.
|
||||
|
||||
## Машинный журнал уроков (ORCH-098)
|
||||
Шаг 1 («Фундамент», F2) эпика саморазвития: формализует свободнотекстовые «уроки» из `memory/` в
|
||||
|
||||
Reference in New Issue
Block a user