feat(staging): deterministic staging-runner replacing LLM deployer on deploy-staging (ORCH-115)
Replace the LLM `deployer` agent on the `deploy-staging` stage (self-hosting orchestrator) with a deterministic staging-runner intercepted in launch_job BEFORE _spawn (the deploy-finalizer / post-deploy-monitor reserved-agent precedent). The runner executes the SAME staging suite, maps the exit-code to `staging_status:` via the existing self_deploy.map_exit_code_to_status contract, writes 15-staging-log.md, and initiates the UNCHANGED check_staging_status gate exactly as a finished LLM-deployer would. Invariant (NFR-1): this replaces only the *producer* of the artifact — the artifact contract, the gate / _parse_staging_status / check_staging_status name, STAGE_TRANSITIONS, the machine-verdict key `staging_status:` and the DB schema are byte-for-byte unchanged. Additive, under a kill-switch + repo-scope CSV, never-raise, fail-safe back to the LLM path. Two-level outcome (D5, anti ORCH-110): suite executed -> verdict -> advance (FAILED -> the existing deploy-staging -> development rollback + developer-retry, same as a FAILED LLM verdict); tool-error (suite did not execute) -> bounded DEFER -> fail-closed FAILED + alert on exhaustion (infra != code fault; never a silent advance / false green). First implemented slice of the LLM determinization roadmap (ORCH-118 A6, replace-deterministic-now). - New leaf src/staging_runner.py (never-raise; proc_group tree-kill + timeout) - launch_job intercept + _run_staging_runner_job (mirror _run_deploy_finalizer_job) - config: ORCH_STAGING_RUNNER_* keys (enabled/repos/timeout/infra-retry budget) - GET /queue staging_runner observability block - docs: llm-call-sites/roadmap/usage-policy (A6 implemented; machine blocks + single-transport invariant intact), deployer.md (LLM branch -> fallback), CLAUDE.md, CHANGELOG.md, overview (tech-pipeline/tech-agents/tech-quality-security), .env.example - tests/test_orch115_staging_runner.py (TC-01..TC-13); LLM anti-drift green (TC-14) Refs: ORCH-115 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
| **A3** | `.openclaw/agents/developer.md` | стадия `development` | developer | код + PR | — | `check_ci_green:82` (+ `check_branch_mergeable:657`) — CI/merge | ~150–400k / 10–40 мин | да (через S0) | **P** | `keep-LLM` | написание кода — настоящее суждение; гейт судит CI/merge, не самоотчёт |
|
||||
| **A4** | `.openclaw/agents/reviewer.md` | стадия `review` | reviewer | `12-review.md` | `verdict:` | `check_reviewer_verdict:336` (`verdict:`) | ~100–300k / 5–25 мин | да (через S0) | **C** | `keep-LLM` | control path, но вердикт «приемлемость кода/решения» **НЕ деривируем** из exit-кода — настоящее суждение |
|
||||
| **A5** | `.openclaw/agents/tester.md` | стадия `testing` | tester | `13-test-report.md` | `result:` | `check_tests_passed:182` → `_parse_tests_verdict:226` (`result:`) | ~60–150k / 5–20 мин | да (через S0) | **C** | `needs-hybrid-fallback` | **avoidable**: PASS/FAIL = exit-code `pytest`+smoke (деривируем); LLM нужен лишь на триаж падений / маппинг TC↔критерии |
|
||||
| **A6** | `.openclaw/agents/deployer.md` | стадии `deploy-staging` / `deploy` | deployer | `15-staging-log.md` / `14-deploy-log.md` | `staging_status:` / `deploy_status:` | `check_staging_status:599` → `_parse_staging_status:538` (`staging_status:`); `check_deploy_status:473` → `_parse_deploy_status:413` (`deploy_status:`) | ~40–120k / 3–15 мин | да (через S0) | **C** | `replace-deterministic-now` | **avoidable**: staging-вердикт = маппинг exit-кода `staging_check.py`; прод уже детерминирован Phase A/B/C (ORCH-036) |
|
||||
| **A6** | `.openclaw/agents/deployer.md` | стадии `deploy-staging` / `deploy` | deployer | `15-staging-log.md` / `14-deploy-log.md` | `staging_status:` / `deploy_status:` | `check_staging_status:599` → `_parse_staging_status:538` (`staging_status:`); `check_deploy_status:473` → `_parse_deploy_status:413` (`deploy_status:`) | ~40–120k / 3–15 мин | да (через S0; для in-scope репо на `deploy-staging` — **нет**, перехват до `_spawn`) | **C** | `replace-deterministic-now` | **avoidable, СРЕЗ РЕАЛИЗОВАН (ORCH-115):** на `deploy-staging` для self-hosting `orchestrator` вердикт `staging_status:` производит детерминированный `src/staging_runner.py` (перехват в `launch_job` до `_spawn`, как D1/D2) — маппинг exit-кода `staging_check.py`; прод уже детерминирован Phase A/B/C (ORCH-036). LLM-ветвь остаётся fallback'ом под выключенным флагом / для не-self репо |
|
||||
| **D1** | `src/agents/launcher.py:389` (перехват в `launch_job` **до** `_spawn`; «Not an LLM spawn» `407`) | post-deploy edge | deploy-finalizer | jobs-row | — | — | — (детерминированный) | **нет** (слот, перехват до `_spawn`) | — | `already-deterministic` (эталон) | Занимает слот агента, но LLM не консультируется — рабочий прецедент замены |
|
||||
| **D2** | `src/agents/launcher.py:394` (перехват в `launch_job` **до** `_spawn`; «Not an LLM spawn» `428`) | post-deploy observation | post-deploy-monitor | jobs-row | — | — | — (детерминированный) | **нет** (слот, перехват до `_spawn`) | — | `already-deterministic` (эталон) | Тик наблюдения; LLM не консультируется |
|
||||
|
||||
@@ -147,8 +147,14 @@
|
||||
|
||||
- **Docs + tests only (ORCH-118).** `STAGE_TRANSITIONS` / реестр и имена `QG_CHECKS`/`check_*` /
|
||||
machine-verdict-ключи (`verdict:`/`result:`/`staging_status:`/`deploy_status:`/`security_status:`/
|
||||
`coverage_status:`) / схема БД — **байт-в-байт не тронуты**. Раннеры замен **не** реализованы
|
||||
(см. roadmap — follow-up'ы по роли, без Plane-ID).
|
||||
`coverage_status:`) / схема БД — **байт-в-байт не тронуты** (это инвариант самой карты).
|
||||
- **Реализованные срезы.** Первый срез roadmap'а — **deployer (staging-status)** — реализован
|
||||
**ORCH-115** (`src/staging_runner.py`, перехват в `launch_job` до `_spawn`): на `deploy-staging`
|
||||
для in-scope репо вердикт `staging_status:` производит детерминированный код, не LLM. Это
|
||||
`replace-deterministic-now` без ввода второго транспорта (раннер LLM не зовёт) — карта/инвариант
|
||||
«единственный транспорт S0» соблюдены. Машинный `ORCH-118-INVENTORY-BLOCK` сохраняет deployer как
|
||||
`avoidable=yes`/`axis=C` (LLM-ветвь жива как fallback под выключенным флагом / для не-self репо).
|
||||
Второй кандидат (`tester`) остаётся follow-up'ом по роли.
|
||||
- **Анти-дрейф тесты:** `tests/test_llm_call_site_inventory.py` (TC-01…TC-06, TC-09, TC-12, TC-13,
|
||||
TC-14) и `tests/test_llm_determinization_docs.py` (TC-07/08/11). Дискриминатор всех проверок —
|
||||
**«консультирует LLM», а не «спавнит subprocess»**.
|
||||
|
||||
@@ -15,7 +15,17 @@
|
||||
|
||||
---
|
||||
|
||||
## 1. Рекомендованный первый срез — **deployer (staging-status)**
|
||||
## 1. Рекомендованный первый срез — **deployer (staging-status)** — ✅ РЕАЛИЗОВАН (ORCH-115)
|
||||
|
||||
> **Статус: реализовано.** Срез выполнен в **ORCH-115** — `src/staging_runner.py` (перехват в
|
||||
> `launch_job` до `_spawn`, как `D1`/`D2`): на стадии `deploy-staging` для self-hosting `orchestrator`
|
||||
> вердикт `staging_status:` производит детерминированный код (маппинг exit-кода `staging_check.py`
|
||||
> через `self_deploy.map_exit_code_to_status`), а не LLM. Под kill-switch `staging_runner_enabled` +
|
||||
> скоуп `staging_runner_repos` (пусто → self-hosting only); LLM-ветвь остаётся fallback'ом.
|
||||
> Контракт артефакта/гейта `check_staging_status`/`STAGE_TRANSITIONS`/схема БД — не тронуты. Детали —
|
||||
> `docs/work-items/ORCH-115/06-adr/ADR-001-deterministic-staging-runner.md`, сквозной
|
||||
> `docs/architecture/adr/adr-0048-deterministic-staging-runner.md`. Запись `rank 1` в машинном блоке
|
||||
> §4 сохраняется (`first_slice = yes`, инвариант карты) как историческая фиксация первого среза.
|
||||
|
||||
Обоснование (самый низкорисковый «чисто деривируемый» control path):
|
||||
|
||||
|
||||
@@ -94,3 +94,8 @@ Call-site является **avoidable LLM control path** тогда и толь
|
||||
`launcher._spawn` (S0). Ввод второго транспорта (новый `_spawn`, импорт `anthropic`/`openai`/иного
|
||||
LLM-SDK, прямой HTTP Anthropic/Claude, второй model-invoking subprocess) запрещён без явного ADR;
|
||||
прибито тестами TC-01/TC-12.
|
||||
- **Реализованный срез (ORCH-115).** Снятие C-консультации с деривируемым вердиктом — это разрешённое
|
||||
`replace-deterministic-now`, а не ввод новой LLM-консультации. ORCH-115 снял A6/staging-status:
|
||||
детерминированный `src/staging_runner.py` производит `staging_status:` без `_spawn` (перехват до
|
||||
него, как `D1`/`D2`) — раннер **LLM не зовёт** и **второй транспорт не вводит**, поэтому инвариант
|
||||
«единственный транспорт S0» соблюдён (TC-12 зелёный). Это образец для последующих срезов roadmap'а.
|
||||
|
||||
Reference in New Issue
Block a user