15 KiB
verdict, work_item, stage, author_agent, status, created_at, model_used, type, work_item_id, version
| verdict | work_item | stage | author_agent | status | created_at | model_used | type | work_item_id | version |
|---|---|---|---|---|---|---|---|---|---|
| APPROVED | ORCH-116 | review | reviewer | approved | 2026-06-16 | claude-opus-4-8 | review | ORCH-116 | 3 |
Review ORCH-116 — детерминированный test-раннер вместо LLM-тестера
Summary
PR заменяет LLM-агента tester на стадии testing (self-hosting orchestrator) детерминированным
leaf src/test_runner.py, перехватываемым в launch_job до _spawn — точное зеркало уже
работающего в проде ORCH-115/staging_runner. Ревью выполнено независимо по всем четырём осям
(ТЗ/AC · ADR · качество кода · документация) с личным прогоном тестов и сверкой сигнатур
зависимостей. Критический инвариант NFR-1 («замена продюсера артефакта, не гейта») соблюдён
байт-в-байт: src/stages.py, src/qg/checks.py, src/staging_runner.py, src/transition_lease.py,
src/proc_group.py — не тронуты (подтверждено git diff --stat); в src/ изменены только
test_runner.py (новый), launcher.py/config.py/main.py (аддитивно). Документация = golden
source обновлена в том же PR (8 doc-файлов + CHANGELOG + ADR-001/adr-0049). Полный регресс
проверен лично: pytest tests/ -q → 2137 passed; ORCH-116 + LLM-анти-дрейф + system-docs →
74 passed. P0/P1/P2 findings отсутствуют → APPROVED.
Оси проверки
1. Соответствие ТЗ / Acceptance Criteria — PASS
Верифицировано по коду + тестам + личному прогону каждое AC:
- AC-1 (перехват до
_spawn) —launcher.launch_job:if job.get("agent")=="tester":→if test_runner.should_intercept(job): return self._run_test_runner_job(job)вставлено передreturn self._spawn(...);_run_test_runner_jobведётjobsчерезmark_job(done|failed), возвращаетNone(нетagent_runs). TC-05 воспроизводит без живого CLI (spawn.assert_not_called). - AC-2 (контракт
13-test-report.md) —build_test_reportэмититresult: PASS|FAILUPPERCASE- полную 52c-схему; читается неизменённым
_parse_tests_verdict(TC-04, лично подтверждено прогоном через реальный парсер).
- полную 52c-схему; читается неизменённым
- AC-3 (exit→verdict) —
map_exit_code_to_result— тонкий транслятор над единымself_deploy.map_exit_code_to_status(0→PASS, иначе/None/нечисло→FAIL, fail-closed); второй маппинг не введён (TC-02 сверяет с базовым контрактом). - AC-4 (эквивалентность маршрутизации) —
_advance(current_stage="testing", finished_agent="tester"), без новых рёбер; PASS→advance, FAIL→существующий откатtesting→development(TC-07 параметризован [PASS/FAIL]). - AC-5 (two-level outcome, анти-ORCH-110) —
suite_ran = (returncode is not None) and (not timed_out); tool-error → bounded DEFER re-queuetester-джоба (TC-10 проверяетagent == "tester"), не код-фейл-откат, не жжёт developer-retry; исчерпание бюджета → fail-closed FAIL + alert. - AC-6 (анти-дрейф) —
git diffпоsrc/затрагивает толькоlauncher.py/config.py/main.py/test_runner.py;src/stages.py,src/qg/checks.pyбайт-в-байт не тронуты (провереноgit diff --statлично); новых таблиц нет (TC-09). - AC-7/AC-8 (kill-switch / backward-compat) —
applies()гасит приenabled=False, скоупит CSV;_has_test_contract→Falseдля не-self репо даже при ручном добавлении в CSV (TC-01/TC-08). - AC-9 (never-raise) — все публичные функции в
try/except;run_test_gateимеет внешний guard;_run_test_runner_jobдополнительно оборачивает (TC-11: воркер не падает / не клинит). - AC-10 (self-hosting safety) —
build_test_commandбез литералов рестарта/compose/build/force-push/.env; smoke строго read-only GET; лог пушится только в фичеветку, никакой прямой работы сmain(TC-13). - AC-11 (proc_group/worktree/timeout) — pytest в
get_worktree_path(repo, branch)черезrun_in_process_group(tree-kill),_resolve_timeoutдефолтит на малформе + WARNING; без правкиreaper_max_running_s(TC-12). - AC-12/AC-13/AC-14/AC-15 — гибрид off-control-path, блок
test_runnerв/queue, обновление LLM-карты/политики/roadmap/витрины, полный регресс — все зелёные (AC-15: 2137 passed, проверено лично).
2. Соответствие ADR — PASS
Реализация дословно следует ADR-001 (D1–D12) и сквозному adr-0049. Сверено критическое:
- D6.1 (анти-коллизия 52c-
status:↔_parse_tests_verdict) — самая тонкая мина задачи. Сверено по неизменённому парсеру (src/qg/checks.py:226-282): он читает вердикт из ТРЁХ равноранговых полейverdict:/status:/result:c negative-token-priority.build_test_reportжёстко выравниваетsub_status = "success" if result=="PASS" else "failed": для PASS конкатенация"SUCCESS PASS"не содержит ни одного из_TESTS_NEGATIVE_TOKENS(BLOCKED/FAILED/FAIL/REQUEST_CHANGES/REJECT/RED), позитивPASSизresult:→True; для FAIL"FAILED FAIL"→ негативный токен →False. Прибито TC-04 (test_tc04_status_field_never_false_negatives_a_pass) через реальный парсер. Мина снята верно. - D5 DEFER — зеркало
staging_runner._handle_tool_error, но re-queuetester-джоба (неdeployer!), счётчик restart-safe по маркеруtest-runner infra-retryвtask_content(без правки схемы БД). Проверено TC-10. - D4 единый маппинг — переиспользован
self_deploy.map_exit_code_to_status(BR-4), второй несогласованный маппинг не введён. - Трассировка маркеров (TRACEABILITY): правки рядом с ORCH-115 (
launcherврезка), ORCH-114 (transition-lease берётся внутриadvance_stage— раннер не трогает, граница O1), ORCH-110 (proc_groupпереиспользуется как есть) — ни один зафиксированный инвариант не сломан. - Глобальные ADR (INV-4 «мерж только через PR», запрет рестарта прод-контейнера) — соблюдены:
лог пушится только в фичеветку (
git push origin <branch>, не force, неmain); смоук read-only.
3. Качество кода — PASS
- Docstrings на всех публичных функциях; контракт never-raise выдержан сквозно (каждый
внешний вызов обёрнут; счётчики через
_bumpне ломают решение). - Сигнатуры зависимостей сверены лично (риск latent false-FAIL устранён):
db.enqueue_job(agent, repo, task_content, task_id=, available_at_delay_s=),db.mark_job,self_deploy.map_exit_code_to_status,git_worktree.get_worktree_path(repo, branch),qg.checks.is_self_hosting_repo,config.post_deploy_base_url(дефолтhttp://localhost:8500),notifications.send_telegram/link_for,proc_group.run_in_process_group/ProcResult— все существуют ровно как используются. - Тесты содержательные (TC-01…TC-15, ~32 тест-функции, не тривиальные): kill-switch/scope/contract,
маппинг по классам входа, рендер+неизменённый парсер, перехват до
_spawn, two-level outcome, never-raise, timeout-resolve, smoke read-only (включая транзиент-ретрай и missing-serial_gate), анти-дрейф конвейера, наблюдаемость, структурный лог. - Security: smoke — только
urllibGET к локальному оркестратору; мутирующих запросов нет. - Багфикс-трек неприменим: это не
Bug-задача (full-cycle, стадияarchitectureотработана, есть ADR) → требование регресс-фиксатора ORCH-019/BR-4 не применяется; покрытие тем не менее исчерпывающее.
4. Документация — PASS (обязательная проверка)
src/ изменён → документация обновлена в том же PR, проверено явно по git diff (наличие
правок подтверждено по каждому файлу):
- Паспорт
CLAUDE.md— новый раздел «Детерминированный test-раннер вместо LLM-тестера (ORCH-116)». - Компонент-карта
docs/architecture/README.md(3 hits) + внутренностиinternals.md(5 hits) — перехват наtestingрядом с ORCH-115. - LLM-карта/политика/roadmap —
llm-call-sites.md(A5),llm-determinization-roadmap.md(rank 2 ✅ реализован; инвариант «ровно одинfirst_slice=yes» цел —yesу rank 1 deployer,no/hybrid_needed=yesу rank 2 tester),llm-usage-policy.md(§5); анти-дрейф-тестыtest_llm_call_site_inventory.py/test_llm_determinization_docs.py— зелёные (лично прогнаны). - Витрина системы
docs/overview/(ORCH-011) — обновленыtech-pipeline.md,tech-agents.md,tech-quality-security.md;tests/test_system_docs.py— зелёный (лично прогнан). - Промпт
.openclaw/agents/tester.md— врезка «детерминированный раннер ведёт стадию; LLM — fallback под выключенным флагом / для репо без контракта»; канон 52d (5 секций, ключresult:) цел. CHANGELOG.md— развёрнутая запись[Unreleased].- ADR заведён (
06-adr/ADR-001-deterministic-test-runner.md+ сквознойadr/adr-0049-deterministic-test-runner.md).
Обзорные доки / README «Известные ограничения» (ORCH-079): раннер — новая способность, не закрытие документированного ограничения → дополнительных правок README не требуется.
Findings
P0 — Blocker
- Нет.
P1 — Must fix
- Нет.
P2 — Should fix
- Нет.
P3 — Nice-to-have (не блокирует)
_http_getчитает первые 8192 байта тела; smoke ищетserial_gateв ответе/queue. Лично измерено:serial_gateна смещении 1471 байт при полном ответе ~3.6 КБ → запас огромный, сейчас корректно. Латентная хрупкость лишь при экстремальном разрастании ранних блоков; можно опционально поднять лимит чтения для/queueили искать ключ потоково.build_test_commandдобавляет-q, отсутствующий в литеральной команде ADR/CHANGELOG (python -m pytest <target>). Безвредно (exit-код, а не парс вывода, формирует вердикт; согласовано с прежним промптомpytest tests/ -q); фиксирую лишь как расхождение текст↔код.- Tracked-scratch
.task-dev.mdперезаписан с ORCH-115 на ORCH-116 (нормальная churn dev-раннера, предсуществующий паттерн, не введён этим PR; вне осей ревью).
Контекст (не finding)
- В истории ветки присутствует
15-staging-log.mdсоstaging_status: FAILED(exit_code: None— staging-сюита не исполнилась, tool-error/инфра, бюджет инфра-ретраев исчерпан → fail-closed). Это исход стадии deploy-staging (downstream от review), относится к staging-раннеру ORCH-115 и является инфра-сбоем, а не дефектом кода ORCH-116 — вне осей код-ревью. Поведение гейта корректно (fail-closed на исчерпании инфра-бюджета); правок в коде задачи не требует. Отмечено как контекст для последующих стадий.
Документация
Обновлена в полном объёме в том же PR (см. ось 4): CLAUDE.md, docs/architecture/README.md,
internals.md, llm-call-sites.md, llm-determinization-roadmap.md, llm-usage-policy.md,
docs/overview/{tech-pipeline,tech-agents,tech-quality-security}.md, .openclaw/agents/tester.md,
CHANGELOG.md, ADR-001 + сквозной adr-0049. Анти-дрейф-тесты документации
(test_llm_call_site_inventory.py, test_llm_determinization_docs.py, test_system_docs.py) —
зелёные (лично прогнаны). Невыполненных требований к обновлению документации нет.
Вердикт
Нет P0/P1/P2 findings; критический инвариант NFR-1 соблюдён байт-в-байт (stages.py/qg/checks.py
не тронуты — проверено лично); документация = golden source обновлена синхронно с кодом; полный
регресс зелёный лично проверен (2137 passed). verdict: APPROVED.