feat(staging-check): ORCH-048 B6 reads registry via GET /projects, not local import
All checks were successful
CI / test (push) Successful in 13s
CI / test (pull_request) Successful in 12s

B6 built the project registry by importing src.projects locally (host-path hack
+ importlib.reload), so it evaluated ORCH_PROJECTS_JSON from the launcher's
process-env. On the deployer's canonical host run that var is unset → built-in
default (ET+ORCH) → false FAIL even when staging isolation is healthy.

- Add read-only additive endpoint GET /projects (src/main.py) returning
  known_plane_project_ids + {plane_project_id, repo, work_item_prefix, name}
  of the live process; no secrets. Existing routes unchanged.
- Rewrite B6 to fetch GET {base}/projects via the same stdlib _get helper as
  A/B4/B5/C; drop the host-path hack and importlib.reload (launch-invariant).
- Isolate the verdict in pure _evaluate_b6(known) -> (passed, detail); contract
  unchanged (PASS iff SANDBOX in known and prod ET/ORCH absent). Endpoint
  degradation (non-200 / missing key / bad body / network) → deterministic FAIL.
- src/projects.py and .env* untouched.

Docs (golden source): API table + staging-gate B6 mechanic in
docs/architecture/README.md; B6 description + isolation row in
docs/operations/STAGING_CHECK.md; CHANGELOG entry.

Tests: tests/test_staging_check_b6.py (TC-01..TC-07), tests/test_projects_endpoint.py.

Refs: ORCH-048

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 05:25:45 +00:00
parent f77825b3c4
commit 2cf873a777
7 changed files with 369 additions and 27 deletions

View File

@@ -41,6 +41,8 @@ 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-чеков пишет деплоер прогоном `scripts/staging_check.py` против живого `orchestrator-staging` (8501). Чек **B6 «Registry: sandbox present, prod ET/ORCH absent»** читает реестр работающего инстанса по HTTP (`GET /projects`), а не локальным импортом `src.projects` в process-env скрипта — иначе host-запуск без `ORCH_PROJECTS_JSON` давал ложный FAIL (ORCH-048, `docs/work-items/ORCH-048/06-adr/ADR-001-b6-registry-via-http-endpoint.md`). Вердикт изолирован в чистой функции `_evaluate_b6(known)`; недоступность эндпоинта → детерминированный FAIL (без ложного PASS).
## Откаты
- Reviewer REQUEST_CHANGES → откат на `development` + retry (`MAX_DEVELOPER_RETRIES = 3`).
- Tester `check_tests_passed` FAIL → откат на `development` + retry.
@@ -84,6 +86,7 @@ created → analysis → architecture → development → review → testing →
| GET | `/health` | health check |
| GET | `/status` | активные задачи (stage != done) |
| GET | `/queue` | очередь: counts + max_concurrency + последние jobs |
| GET | `/projects` | read-only реестр проектов живого инстанса: `known_plane_project_ids` + список `{plane_project_id, repo, work_item_prefix, name}` (без секретов). Источник достоверности для staging-чека B6 (ORCH-048) |
| POST | `/webhook/plane` | Plane webhook |
| POST | `/webhook/gitea` | Gitea webhook (push, PR, CI status) |
@@ -97,4 +100,4 @@ created → analysis → architecture → development → review → testing →
Схема БД, потоки данных, resilience-слой, детали Dockerfile — [internals.md](internals.md).
---
*Актуально на 2026-06-05 (main `f1b3146`). Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py.*
*Актуально на 2026-06-06 (ORCH-048: + GET /projects). Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py.*

View File

@@ -9,7 +9,7 @@
| Блок | Название | Что проверяет |
|------|----------|---------------|
| A | SMOKE | `/health`, `/queue`, `ORCH_STAGING=true` |
| B | ACCESS | Plane sandbox (R), Gitea sandbox (R+push), реестр проектов |
| B | ACCESS | Plane sandbox (R), Gitea sandbox (R+push), реестр проектов (B6 — по HTTP `GET /projects`) |
| C | E2E | Создать задачу → триггер конвейера → ветка + коммент → cleanup |
Exit code: **0** = все PASS, **non-zero** = есть FAIL.
@@ -64,6 +64,13 @@ docker exec orchestrator-staging \
--mode stub
```
> **B6 инвариантен к способу запуска (ORCH-048).** Чек реестра больше не импортирует
> `src.projects` локально (раньше: host-path хак `/repos/orchestrator` + reload, который
> читал env *процесса-запускателя* — на host-запуске без `ORCH_PROJECTS_JSON` это давало
> ложный FAIL). Теперь B6 запрашивает `GET {base}/projects` у работающего инстанса, поэтому
> одинаково корректен и с хоста, и из `docker exec`. ADR:
> `docs/work-items/ORCH-048/06-adr/ADR-001-b6-registry-via-http-endpoint.md`.
---
## Режимы (`--mode`)
@@ -116,7 +123,7 @@ hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
| Проверка | Гарантия |
|---------|---------|
| A3 `ORCH_STAGING=true` | При false — abort до деструктивных блоков |
| B6 Реестр без боевых | ET/ORCH project_id absent в `known_plane_project_ids()` |
| B6 Реестр без боевых | ET/ORCH project_id absent в `known_plane_project_ids` живого инстанса (читается по HTTP `GET /projects`, не локальным импортом) |
| C: only SANDBOX project_id | Webhook payload указывает только `8c5a3025-...` |
| C: only orchestrator-sandbox repo | Gitea operations на `admin/orchestrator-sandbox` |
| C: cleanup в finally | Артефакты удаляются даже при ошибке |