architect(ET): auto-commit from architect run_id=151
This commit is contained in:
@@ -36,34 +36,53 @@ Exit code: **0** = все PASS, **non-zero** = есть FAIL.
|
||||
|
||||
## Способы запуска
|
||||
|
||||
### 1. Внутри контейнера (рекомендуемый)
|
||||
|
||||
```bash
|
||||
docker exec orchestrator-staging \
|
||||
python3 /repos/orchestrator/scripts/staging_check.py --mode stub
|
||||
```
|
||||
|
||||
### 2. С хоста (если есть токены в env)
|
||||
|
||||
```bash
|
||||
export ORCH_STAGING=true
|
||||
export ORCH_PLANE_API_TOKEN=...
|
||||
# ... остальные переменные ...
|
||||
|
||||
python3 scripts/staging_check.py \
|
||||
--base-url http://localhost:8501 \
|
||||
--mode stub
|
||||
```
|
||||
|
||||
### 3. Из docker exec с передачей URL
|
||||
### 1. Внутри контейнера (КАНОНИЧЕСКИЙ — обязателен для деплоера)
|
||||
|
||||
```bash
|
||||
docker exec orchestrator-staging \
|
||||
python3 /repos/orchestrator/scripts/staging_check.py \
|
||||
--base-url http://localhost:8501 \
|
||||
--mode stub
|
||||
--base-url http://localhost:8501 --mode stub
|
||||
```
|
||||
|
||||
Это единственный канонический способ для стадии `deploy-staging` (ORCH-048, ADR-001).
|
||||
Внутри контейнера env уже staging (`.env.staging`), а чек **B6** строит реестр проектов из
|
||||
собственного process-env инстанса (см. ниже). Путь к скрипту — `/repos/orchestrator/scripts/…`
|
||||
(bind-mount); `scripts/` **не** копируется в образ, поэтому `/app/scripts` не существует.
|
||||
|
||||
### 2. С хоста — НЕ рекомендуется
|
||||
|
||||
```bash
|
||||
# ⚠️ Воспроизводит баг ORCH-048: на хосте ORCH_PROJECTS_JSON не задан →
|
||||
# B6 строит реестр из дефолта (ET+ORCH) → ложный FAIL.
|
||||
# Допустимо ТОЛЬКО если env хоста полностью повторяет staging (включая ORCH_PROJECTS_JSON).
|
||||
export ORCH_STAGING=true
|
||||
export ORCH_PROJECTS_JSON=... # обязателен, иначе B6 даст ложный FAIL
|
||||
export ORCH_PLANE_API_TOKEN=...
|
||||
# ... остальные переменные ...
|
||||
|
||||
python3 scripts/staging_check.py --base-url http://localhost:8501 --mode stub
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Механика чека B6 (ORCH-048, ADR-001)
|
||||
|
||||
B6 «Registry: sandbox present, prod ET/ORCH absent» подтверждает изоляцию: в реестре
|
||||
работающего staging-инстанса есть только sandbox-проект и НЕТ боевых (ET/ORCH).
|
||||
|
||||
- B6 импортирует `known_plane_project_ids()` из `src.projects` **кода контейнера**
|
||||
(`/app/src` через `PYTHONPATH=/app`), env которого — `.env.staging`. Реестр отражает
|
||||
именно работающий staging-инстанс.
|
||||
- Прежний host-path хак (`sys.path.insert(0, "/repos/orchestrator")` + `importlib.reload`)
|
||||
удалён: он подхватывал env процесса-запускателя и при запуске с хоста давал ложный FAIL.
|
||||
- Логика вердикта вынесена в чистую функцию `_evaluate_b6(known) -> (passed, detail)`:
|
||||
`passed ⟺ SANDBOX ∈ known ∧ PROD_ET ∉ known ∧ PROD_ORCH ∉ known`. Покрыта юнит-тестами
|
||||
(`tests/test_staging_check_b6.py`) на оба исхода без поднятия инстанса/docker.
|
||||
- При недоступности источника реестра B6 даёт детерминированный FAIL (не ложный PASS,
|
||||
не необработанное исключение).
|
||||
|
||||
**Поэтому B6 достоверен только при каноническом запуске (способ 1).**
|
||||
|
||||
---
|
||||
|
||||
## Режимы (`--mode`)
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# ADR-001: B6 читает реестр через запуск suite ВНУТРИ staging-контейнера
|
||||
|
||||
## Статус
|
||||
Accepted
|
||||
|
||||
- **Задача:** ORCH-048
|
||||
- **Дата:** 2026-06-06
|
||||
- **Автор:** architect
|
||||
- **Решение варианта:** принято Владельцем проекта (Слава, 06.06) — вариант **(в)**. Архитектор фиксирует и обосновывает.
|
||||
|
||||
## Контекст
|
||||
|
||||
Чек **B6 «Registry: sandbox present, prod ET/ORCH absent»** в `scripts/staging_check.py`
|
||||
(блок B, ~строки 263–284) — страховка изоляции staging: подтверждает, что в реестре
|
||||
проектов работающего staging-инстанса есть только sandbox-проект и НЕТ боевых
|
||||
(enduro-trails / orchestrator).
|
||||
|
||||
B6 даёт **ложный FAIL** (`prod-ET=YES(BAD!)`, `prod-ORCH=YES(BAD!)`), хотя изоляция
|
||||
реестра в staging исправна. Root cause (подтверждён прямым запуском, 06.06):
|
||||
|
||||
```python
|
||||
sys.path.insert(0, "/repos/orchestrator") # host-worktree path
|
||||
import importlib
|
||||
if "src.projects" in sys.modules:
|
||||
importlib.reload(sys.modules["src.projects"]) # перечитывает env ТЕКУЩЕГО процесса
|
||||
from src.projects import known_plane_project_ids
|
||||
known = known_plane_project_ids()
|
||||
```
|
||||
|
||||
B6 — единственный чек, который не ходит к инстансу по HTTP, а импортирует Python-код
|
||||
локально и строит реестр из `ORCH_PROJECTS_JSON` **process-env того процесса, в котором
|
||||
исполняется скрипт**. Деплоер фактически запускает suite **с хоста**
|
||||
(`python3 scripts/staging_check.py --base-url http://localhost:8501`), где
|
||||
`ORCH_PROJECTS_JSON` не задан → `src.projects` грузит встроенный `_DEFAULT_PROJECTS`
|
||||
(ET + ORCH) → ложный FAIL. B6 проверяет реестр НЕ того окружения, реестр которого
|
||||
реально использует staging-инстанс.
|
||||
|
||||
### Топология (ключевой факт для решения)
|
||||
|
||||
- Контейнер `orchestrator-staging`: `WORKDIR /app`, `ENV PYTHONPATH=/app`; код приложения
|
||||
**скопирован** в образ (`Dockerfile: COPY src/ ./src/`) → живёт в `/app/src/`.
|
||||
- `.env.staging` (env_file контейнера) задаёт `ORCH_PROJECTS_JSON` = только sandbox.
|
||||
- `Dockerfile` **НЕ копирует** `scripts/` в образ. Скрипт доступен в контейнере только
|
||||
через bind-mount `/home/slin/repos:/repos` → `/repos/orchestrator/scripts/staging_check.py`.
|
||||
|
||||
Из этого следует: при запуске `docker exec orchestrator-staging python3
|
||||
/repos/orchestrator/scripts/staging_check.py` интерпретатор добавляет в `sys.path[0]`
|
||||
каталог скрипта (`/repos/orchestrator/scripts`), а `import src.projects` резолвится через
|
||||
`PYTHONPATH=/app` → `/app/src/projects.py` (собственный код контейнера) с env из
|
||||
`.env.staging`. Это ровно реестр работающего staging-инстанса — без HTTP, без host-path хака.
|
||||
|
||||
## Решение
|
||||
|
||||
Принят **вариант (в): канонизировать запуск suite ВНУТРИ `orchestrator-staging` и читать
|
||||
собственный process-env контейнера.**
|
||||
|
||||
Архитектурно фиксируется (детальная реализация — стадия development):
|
||||
|
||||
1. **Убрать из B6 host-path хак:** удалить `sys.path.insert(0, "/repos/orchestrator")` и
|
||||
`importlib.reload(sys.modules["src.projects"])`. Импорт `from src.projects import
|
||||
known_plane_project_ids` остаётся, но резолвится из кода контейнера (`/app/src` через
|
||||
`PYTHONPATH=/app`), env которого — staging (`.env.staging`).
|
||||
|
||||
2. **Канонизировать запуск suite внутри контейнера** (а не с хоста):
|
||||
```bash
|
||||
docker exec orchestrator-staging \
|
||||
python3 /repos/orchestrator/scripts/staging_check.py \
|
||||
--base-url http://localhost:8501 --mode stub
|
||||
```
|
||||
`--base-url http://localhost:8501` корректен изнутри контейнера: сеть `network_mode: host`.
|
||||
Путь к скрипту — `/repos/orchestrator/scripts/...` (mount), а НЕ `/app/scripts` (в образе
|
||||
scripts отсутствует).
|
||||
|
||||
3. **Синхронно обновить документацию запуска** (этот же PR), иначе host-запуск воспроизведёт
|
||||
баг:
|
||||
- `.openclaw/agents/deployer.md` — команда стадии `deploy-staging` через `docker exec`.
|
||||
- `docs/operations/STAGING_CHECK.md` — канонический способ запуска и описание механики B6.
|
||||
|
||||
4. **Логику вердикта B6 вынести в чистую функцию** `_evaluate_b6(known: set[str]) ->
|
||||
tuple[bool, str]`, инвариант (TR-2): `passed ⟺ SANDBOX ∈ known ∧ PROD_ET ∉ known ∧
|
||||
PROD_ORCH ∉ known`; `detail` сохраняет формат `sandbox=…, prod-ET=…, prod-ORCH=…` (TR-3).
|
||||
Функция юнит-тестируема без поднятия инстанса/docker (TC-01…TC-07).
|
||||
|
||||
5. **Детерминированная деградация (TR-4):** при недоступности источника реестра (ошибка
|
||||
импорта/построения `known`) B6 даёт FAIL с понятным detail, без необработанного исключения
|
||||
и без ложного PASS.
|
||||
|
||||
### Границы (scope guards — обязательны)
|
||||
|
||||
- **НЕ** добавлять HTTP-эндпоинт `GET /projects`; **НЕ** трогать прод-`src/main.py`,
|
||||
`src/webhooks/*`, `src/stage_engine.py`, `src/qg/*`.
|
||||
- **НЕ** изменять `src/projects.py`, `.env`, `.env.staging`, `.env.example`.
|
||||
- **НЕ** менять блоки A1–A3, B4, B5 и блок C (E2E): формат вывода и логика прежние.
|
||||
- Реестр QG и стадий не меняется; ADR-0003 (`check_staging_status`) в силе — меняется только
|
||||
достоверность одного чека внутри suite, чей агрегат пишется в `15-staging-log.md`.
|
||||
|
||||
## Альтернативы (отклонены)
|
||||
|
||||
### (а) HTTP-эндпоинт `GET /projects` работающего staging-инстанса — ОТКЛОНЁН
|
||||
Порождает «курицу-яйцо»: B6 ходит на эндпоинт **работающего** инстанса, а эндпоинт запечён
|
||||
в Docker-образ → на первом прогоне его в живом инстансе ещё нет (404) → ложный FAIL → откат.
|
||||
Требует ручного bootstrap-деплоя. Это ровно тот класс поломки автономности, который мы
|
||||
устраняем. Подтверждено на проде 06.06: `GET /projects` на 8501 → 404 → deploy-staging FAILED.
|
||||
(Предыдущая итерация архитектора выбрала (а); решение отклонено Владельцем, код и ADR(а)
|
||||
удалены, ветка откатана к analyst-артефактам.)
|
||||
|
||||
### (б) `docker exec` subprocess + парсинг stdout — ОТКЛОНЁН
|
||||
`docker exec orchestrator-staging python3 -c "..."` из процесса suite. Хрупкое экранирование
|
||||
(`docs/history/LESSONS_2026-06-05.md`), зависимость от наличия docker-CLI и имени контейнера
|
||||
в среде запуска, усложняет запуск «изнутри контейнера».
|
||||
|
||||
### (в) Запуск suite внутри контейнера + собственный process-env — ВЫБРАН
|
||||
B6 не зависит от того, что отдаёт инстанс по HTTP; `staging_check.py` берётся из mount (свежий
|
||||
код сразу, без ребилда образа); реестр читается из env самого `orchestrator-staging`. Курицы-яйца
|
||||
нет ни на первом прогоне, ни в будущем. Минимально инвазивно, прод-логика и `src/projects.py` не
|
||||
тронуты. Согласуется с «рекомендуемым способом запуска» (`STAGING_CHECK.md §Способы запуска.1`).
|
||||
|
||||
## Последствия
|
||||
|
||||
**Плюсы**
|
||||
- B6 достоверно отражает реестр работающего staging-инстанса; ложные FAIL/откаты устранены.
|
||||
- Автономность self-hosting не ломается: нет bootstrap-зависимости от запечённого в образ кода.
|
||||
- Свежий `staging_check.py` подхватывается из mount без ребилда образа.
|
||||
- Защитная функция B6 сохранена и покрыта юнит-тестами на оба исхода (PASS/FAIL).
|
||||
|
||||
**Минусы / ограничения**
|
||||
- Запуск suite **обязан** идти через `docker exec` внутри `orchestrator-staging`. Запуск с
|
||||
хоста воспроизведёт исходный баг (host-env без `ORCH_PROJECTS_JSON`). Это закреплено в
|
||||
`deployer.md` и `STAGING_CHECK.md`; способ «с хоста» остаётся возможен, только если env
|
||||
хоста корректно повторяет staging (не рекомендуется, помечено).
|
||||
- Деплоер должен иметь доступ к docker-CLI/сокету (есть: `/var/run/docker.sock` смонтирован в
|
||||
контейнер оркестратора, у которого deployer-агент исполняется; `deployer.md` tools: Bash docker).
|
||||
|
||||
## Связи
|
||||
- ADR-0003 (`docs/architecture/adr/adr-0003-staging-gate.md`) — staging-гейт, который этот чек
|
||||
обслуживает.
|
||||
- ORCH-6 / `src/projects.py` — реестр проектов (источник `known_plane_project_ids()`),
|
||||
НЕ изменяется.
|
||||
- `docs/history/LESSONS_2026-06-05.md` — обоснование отказа от варианта (б).
|
||||
Reference in New Issue
Block a user