11 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 | 1 |
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, качество кода, документация. Все 15 критериев приёмки
выполнены, критический инвариант NFR-1 («замена продюсера артефакта, не гейта») соблюдён
байт-в-байт, документация (включая витрину docs/overview/ и LLM-карту/roadmap/политику) обновлена
в том же PR. Полный регресс зелёный (2137 passed), новый tests/test_orch116_test_runner.py
зелёный (32 теста), LLM-анти-дрейф зелёный. P0/P1 findings отсутствуют → APPROVED.
Оси проверки
1. Соответствие ТЗ / Acceptance Criteria — PASS
Верифицировано по коду + тестам каждое AC:
- AC-1 (перехват до
_spawn) —launcher.launch_job:if job.get("agent")=="tester" and test_runner.should_intercept(job): return self._run_test_runner_job(job);_run_test_runner_jobведётjobsчерезmark_job, возвращаетNone(нетagent_runs). TC-05 воспроизводит без живого CLI. - AC-2 (контракт
13-test-report.md) —build_test_reportэмититresult: PASS|FAILUPPERCASE + полную 52c-схему; читается неизменённым_parse_tests_verdict(TC-04). - AC-3 (exit→verdict) —
map_exit_code_to_result— тонкий транслятор над единымself_deploy.map_exit_code_to_status(0→PASS, иначе/None/нечисло→FAIL); второй маппинг не введён (TC-02). - AC-4 (эквивалентность маршрутизации) —
_advance(finished_agent="tester", current_stage="testing"), без новых рёбер; PASS→advance, FAIL→существующий откатtesting→development(TC-07/TC-10). - AC-5 (two-level outcome) —
run_test_gate:suite_ran = returncode is not None and not timed_out; tool-error → bounded DEFER re-queuetester-джоба, не код-фейл-откат (TC-10). - AC-6 (анти-дрейф) —
git diffпоsrc/затрагивает толькоlauncher.py/config.py/main.py/test_runner.py;src/stages.py(STAGE_TRANSITIONS) иsrc/qg/checks.py(check_tests_passed/_parse_tests_verdict) не тронуты; новых таблиц нет (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не роняет воркер (TC-11). - AC-10 (self-hosting safety) —
build_test_commandбез литералов рестарта/compose/build/force-push/.env; smoke строго read-only GET (TC-13). - AC-11 (proc_group/worktree/timeout) — pytest в
get_worktree_pathчерезrun_in_process_group,_resolve_timeoutдефолтит на малформе (TC-12). - AC-12/AC-13/AC-14/AC-15 — гибрид off-control-path, блок
test_runnerв/queue, обновление LLM-карты/политики/витрины, полный регресс — все зелёные.
2. Соответствие ADR — PASS
Реализация дословно следует ADR-001 (D1–D12) и сквозному adr-0049. Ключевые места сверены:
- D6.1 (анти-коллизия 52c-
status:↔_parse_tests_verdict) — корректно:PASS → status: success(SUCCESSне позитивный и не негативный токен, позитивPASSберётся изresult:),FAIL → status: failed(FAILED— негативный токен, согласован). Прибито TC-04/test_tc04_status_field_never_false_ negatives_a_passчерез неизменённый парсер. Это была самая тонкая мина задачи — снята верно. - D5 DEFER — побайтовое зеркало
staging_runner._handle_tool_error, но re-queuetester-джоба (неdeployer) — проверено TC-10 (assert agent == "tester"). Счётчик restart-safe по маркеру вtask_content, без правки схемы БД. - Трассировка маркеров (TRACEABILITY): правки рядом с ORCH-115 (
launcher), ORCH-114 (advance_stage/transition-lease — берётся внутриadvance_stage, раннер не трогает, граница O1), ORCH-110 (proc_groupпереиспользуется как есть) — ни один зафиксированный инвариант не сломан. - Глобальные ADR (INV-4 «мерж только через PR», запрет рестарта прод-контейнера) — соблюдены: лог
пушится только в фичеветку, никакой работы с
main.
3. Качество кода — PASS
- Docstrings на всех публичных функциях; контракт never-raise выдержан сквозно (каждый внешний вызов
обёрнут, наблюдательные счётчики через
_bumpне ломают решение). - Тесты содержательные (32 TC, не тривиальные): покрыты kill-switch/scope/contract, маппинг по классам входа, рендер+парсер, перехват, two-level outcome, never-raise, timeout, smoke read-only, анти-дрейф, наблюдаемость.
- Сигнатуры зависимостей сверены:
db.enqueue_job(..., available_at_delay_s=),db.mark_job,self_deploy.map_exit_code_to_status,config.post_deploy_base_url(дефолтhttp://localhost:8500) — все существуют, AttributeError-риск smoke отсутствует. - Security: smoke — только
urllibGET к локальному оркестратору; мутирующих запросов нет. - Это не багфикс-трек (стадия
architectureотработана, full-cycle) → требование регресс-фиксатора ORCH-019/BR-4 неприменимо; тем не менее покрытие исчерпывающее.
4. Документация — PASS (обязательная проверка)
src/ изменён → документация обновлена в том же PR, проверено явно:
- Паспорт
CLAUDE.md— новый раздел «Детерминированный test-раннер (ORCH-116)». - Компонент-карта
docs/architecture/README.md— запись Test-runner + блок roadmap «второй срез реализован». - Внутренности
docs/architecture/internals.md— примечание о перехвате наtesting. - LLM-карта/политика/roadmap —
llm-call-sites.md(A5),llm-determinization-roadmap.md(rank 2 ✅),llm-usage-policy.md(§5); инвариант «ровно одинfirst_slice=yes» цел; анти-дрейф-тесты зелёные. - Витрина системы
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». CHANGELOG.md— запись[Unreleased].- ADR заведён (
06-adr/ADR-001+ сквознойadr-0049).
Обзорные доки / «Известные ограничения» README не затронуты этим PR (раннер — новая способность, не закрытие документированного ограничения) → дополнительных правок не требуется.
Findings
P0 — Blocker
- Нет.
P1 — Must fix
- Нет.
P2 — Should fix
- Нет.
P3 — Nice-to-have (не блокирует)
_http_getчитает только первые 8192 байта тела (resp.read(8192)), а smoke проверяет наличиеserial_gateв ответе/queue. Эмпирическиserial_gateлежит на офсете ~1481 байт (запас ~6.7 КБ до границы) — сейчас корректно, но при будущем разрастании блоков/queueпередserial_gateэто латентная хрупкость (ложный smoke-FAIL здорового прогона). Опционально: поднять лимит чтения для/queueили искать ключ потоково. Не блокирует — текущий запас комфортный.build_test_commandдобавляет флаг-q, отсутствующий в литеральной команде ADR (python -m pytest <target>). Безвредно и согласовано с прежним промптом tester (pytest tests/ -q); фиксирую лишь как расхождение текст↔код.
Документация
Обновлена в полном объёме в том же 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 findings; критический инвариант NFR-1 соблюдён байт-в-байт; документация = golden source
обновлена синхронно с кодом; полный регресс зелёный. verdict: APPROVED.