# 02 — Техническое задание (ТЗ / TRZ) **Work Item:** ORCH-048 **Title:** staging B6 check reads registry from host worktree, not staging container **Stage:** analysis **Author:** analyst **Date:** 2026-06-06 > Это ТЗ фиксирует требования и инварианты. Выбор одного из трёх архитектурных вариантов (§4) — за архитектором (ADR). Анализ варианты НЕ выбирает. --- ## 1. Задействованные модули | Путь | Роль | Характер изменений | |------|------|--------------------| | `scripts/staging_check.py` | Suite живых staging-проверок; блок B6 (~строки 263–284) | **Изменяется** — переписать механику получения реестра в B6 | | `tests/` (новый файл, напр. `tests/test_staging_check_b6.py`) | Unit-тест корректности B6 | **Создаётся** | | `docs/operations/STAGING_CHECK.md` | Док запуска suite | **Обновляется** (описание B6 + способ запуска) | | `docs/architecture/README.md` / `CHANGELOG.md` | Golden source | **Обновляется** при необходимости | ### НЕ изменять (жёсткий инвариант scope) - `src/projects.py` — реестр работает корректно. - `.env`, `.env.staging`, `.env.example` — конфиг верен. - Прод-логику оркестратора (`src/main.py` прод-роуты, `src/webhooks/*`, `src/stage_engine.py`, `src/qg/*`) — кроме случая варианта (а), если архитектор решит добавить read-only эндпоинт (см. §4а, отдельно обоснованный риск). - Блоки A1–A3, B4, B5 и блок C E2E в `staging_check.py`. ## 2. Текущее поведение (то, что чиним) Блок B6 (`scripts/staging_check.py`): ```python sys.path.insert(0, "/repos/orchestrator") # хост-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() ``` Проблема: реестр строится из `ORCH_PROJECTS_JSON` **process-env того процесса, в котором исполняется скрипт**. При запуске деплоером с хоста (`python3 scripts/staging_check.py --base-url http://localhost:8501`) переменная не задана → `_DEFAULT_PROJECTS` (ET+ORCH) → ложный FAIL. B6 не отражает реестр работающего staging-инстанса. ## 3. Требуемое поведение (контракт B6) | ID | Требование | |----|------------| | TR-1 | B6 определяет набор «известных staging-инстансу Plane project id» из источника, который **гарантированно отражает окружение работающего staging-инстанса** (порт 8501 / контейнер `orchestrator-staging`), а не из локального импорта в process-env скрипта. | | TR-2 | B6 PASS ⟺ `SANDBOX_PROJECT_ID ∈ known` И `PROD_ET_PROJECT_ID ∉ known` И `PROD_ORCH_PROJECT_ID ∉ known`. Идентификаторы — те же константы, что уже в скрипте. | | TR-3 | B6 сохраняет формат вывода `Results.add(label, passed, detail)` с человекочитаемым detail (`sandbox=…, prod-ET=…, prod-ORCH=…`). | | TR-4 | При недоступности источника реестра B6 даёт **детерминированный FAIL** с понятным detail (не падает с необработанным исключением, не даёт ложный PASS). | | TR-5 | Скрипт остаётся на stdlib (без сторонних зависимостей), если выбранный вариант это допускает. | | TR-6 | Удаляется зависимость B6 от хардкод-пути `/repos/orchestrator` для построения реестра (host-path хак), несовместимого с целью проверки. | ## 4. Варианты реализации (выбор — архитектор, в ADR) Бизнес-запрос предлагает три варианта. Анализ перечисляет их с известными плюсами/минусами; решение и обоснование — в `06-adr/`. ### (а) HTTP-эндпоинт staging-инстанса B6 запрашивает реестр у работающего staging-инстанса по HTTP (как делают A/B4/B5/C). - **Сейчас подходящего эндпоинта НЕТ.** `/health`, `/status`, `/queue` реестр проектов не отдают (`src/main.py`). - Потребуется добавить read-only эндпоинт (напр. `GET /projects`, отдающий `known_plane_project_ids()` или список репо/prefix). Это касается прод-`main.py` → выходит за «не трогать прод-логику», но изменение read-only и низкорисковое — архитектор взвешивает. - Плюс: B6 гарантированно читает реестр именно того процесса, что обслуживает webhooks. Единый HTTP-стиль с остальными чеками. ### (б) Subprocess в окружении staging-контейнера B6 выполняет `docker exec orchestrator-staging python3 -c "from src.projects import known_plane_project_ids; ..."` и парсит stdout. - Плюс: не трогает прод-`main.py`; читает env контейнера напрямую. - Минус: требует доступности docker-CLI и имени контейнера из среды запуска suite; усложняет запуск «изнутри контейнера»; есть нюансы экранирования (см. `docs/history/LESSONS_2026-06-05.md`). ### (в) Запуск suite внутри контейнера + чтение собственного process-env Канонизировать запуск `staging_check.py` ВНУТРИ `orchestrator-staging` (`docker exec orchestrator-staging python3 …`), убрать `sys.path.insert(0, "/repos/orchestrator")`, импортировать `src.projects` из кода контейнера (его cwd/PYTHONPATH), env уже staging. - Плюс: минимально инвазивно, не трогает прод-логику и `src.projects`; согласуется с «рекомендуемым способом запуска» в `STAGING_CHECK.md §Способы запуска.1`. - Условие: деплоер должен запускать suite через `docker exec` (а не с хоста). Нужно синхронно обновить `.openclaw/agents/deployer.md` и `STAGING_CHECK.md`, иначе host-запуск воспроизведёт баг. - Нюанс: внутри контейнера код лежит в `/app` (Dockerfile `COPY`), а `/repos/orchestrator` — отдельный mount; импорт должен резолвиться из кода, чьим env реально живёт инстанс. ## 5. Изменения API - Варианты (б) и (в): **нет** изменений API. - Вариант (а): новый read-only эндпоинт (напр. `GET /projects`) — точная схема ответа определяется архитектором. Если выбран — задокументировать в `docs/architecture/README.md` (таблица API) и `CHANGELOG.md`. ## 6. Изменения схемы БД Нет. ## 7. Требования к новым QG checks Нет новых QG. Поведение `check_staging_status` (ADR-0003) не меняется — меняется только достоверность одного из чеков suite, чей агрегат пишется в `15-staging-log.md`. ## 8. Артефакты pipeline, создаваемые/обновляемые - Код: `scripts/staging_check.py` (B6), новый тест в `tests/`. - Док: `docs/operations/STAGING_CHECK.md`; при выборе варианта (а) — `docs/architecture/README.md` (API) и `CHANGELOG.md`; при выборе (в) — `.openclaw/agents/deployer.md` (способ запуска) и `STAGING_CHECK.md`. - ADR: `docs/work-items/ORCH-048/06-adr/ADR-001-*.md` — обоснование выбранного варианта. ## 9. Тестируемость - Логика «PASS/FAIL по набору known id» B6 должна быть выделена в чистую, юнит-тестируемую функцию (напр. `_evaluate_b6(known: set[str]) -> tuple[bool, str]`), чтобы тест проверял оба исхода без поднятия staging-инстанса/docker. План — `04-test-plan.yaml`. ## 10. Definition of Done - BR-1…BR-5 (01-brd) выполнены. - staging-прогон → B6 PASS; `pytest tests/ -q` зелёный. - Док и (при необходимости) ADR обновлены в том же PR.