Compare commits
15 Commits
f4b055e76a
...
feature/OR
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1875773ed | ||
|
|
2fb6dc32f6 | ||
| e5c3774bc5 | |||
| b97ffae7a1 | |||
| b26a391fa3 | |||
| e9038182a1 | |||
| dd09e3da89 | |||
| cc3ed42041 | |||
| dc1cb87818 | |||
| 13e9618bd2 | |||
| d141280390 | |||
| ed04f71fd1 | |||
| 11551572e9 | |||
| 1289d728a8 | |||
| 2c801d8759 |
@@ -8,6 +8,7 @@
|
||||
- **CLI `scripts/onboard_project.py` (D4–D7, D11, FR-4/FR-5):** режимы `plan` (дефолт, GET-only, ноль мутаций сети/диска) / `apply` (идемпотентный ensure: существующее → `skipped(exists)`, delete-операций нет вовсе) / `verify` (round-trip реестра, резолв всех 22 статусов включая fail-closed `Confirm Deploy`/`STOP`, лейблы, webhook активен, полнота kit в репо, скан неразрешённых плейсхолдеров). Закрытый список read-only импортов из `src` (нулевой дрейф по построению): `projects._parse_projects_json`, `plane_sync._PLANE_NAME_TO_KEY`, `config.settings`. Канонические группы статусов фиксированы ADR D5 (код-критично: `STOP`→`cancelled` ORCH-090; терминальные группы только у Done/Cancelled/STOP — иначе terminal-detection ORCH-068 ложно терминалит). Gitea: репо `auto_init=false` + per-repo webhook (`push`/`pull_request`/`status`, **переиспользует** глобальный `ORCH_GITEA_WEBHOOK_SECRET` — новый сломал бы HMAC существующих, TR-6); initial push — **только** в свежесозданный пустой репо (INV-4 не затрагивается). Реестр: merged-вывод `ORCH_PROJECTS_JSON` через фактический парсер; скрипт `.env` НЕ правит, прод НЕ рестартит, ничего не удаляет (NFR-2); секреты маскируются (NFR-3); Plane CE API-пробел → `manual-step` со ссылкой на runbook (fail-safe, TR-8). Отчёт `created/skipped(exists)/manual-step` + `--json`; exit-коды 0/2/1.
|
||||
- **Runbook `docs/operations/ONBOARDING.md` (FR-6):** полный чеклист (предусловия → Plane → Gitea → kit → регистрация с self-hosting-предупреждением → верификация → откат), каждый ручной шаг с командой проверки; smoke — на **staging-контуре** (8501, изолированная БД) с одноразовым sandbox-проектом (D8), журнал smoke-прогонов. `docs/operations/SETUP_WEBHOOKS.md` обобщён per-repo (без хардкода enduro-trails).
|
||||
- **Анти-дрейф (NFR-4):** структурные канон-тесты kit `tests/test_onboarding_kit.py` (TC-01…08, 19–20), рендер/планы/идемпотентность `tests/test_onboarding_script.py` (TC-02, 09–18, моки, без сети), инварианты `tests/test_onboarding_invariants.py` (TC-21: снапшоты `STAGE_TRANSITIONS`/`QG_CHECKS`, закрытый список импортов CLI, эталонные промпты `.openclaw/agents/` не тронуты).
|
||||
- **fix(tests): герметизация ORCH-41-тестов model/effort от хост-env (разблокировка merge-gate):** re-test merge-gate бежит в прод-окружении орка, где оператор легитимно включил `ORCH_AGENT_FALLBACK_MODEL` и сменил `ORCH_AGENT_MODEL_DEFAULT`/`ORCH_AGENT_EFFORT_*` — `test_resolve_agent_model.py::test_fallback_model_disabled_by_default` и `test_resolve_agent_effort.py::test_flags_present_when_configured` ассертили **заводские** дефолты через env-backed singleton `settings` (в чистом env Gitea CI зелёные → мина на `main`; ветка ORCH-009 `src/` и эти тесты не трогает, детонация от смены прод-env). Фикс: autouse-фикстуры обоих файлов пиняют shipped-дефолты model/fallback-полей (зеркально друг другу), ассерт «G4 выключен по умолчанию» переведён на **класс-дефолт поля** (`type(settings).model_fields["agent_fallback_model"].default == ""` — подлинный инвариант ORCH-074 ADR-001 Решение 3), never-break ассерты `is_valid_model` — байт-в-байт. В чистом CI поведение байт-эквивалентно (фикстуры ставят ровно то, что даёт пустой env). Полный регресс: 1713 passed (было 2 failed / 1711 passed на re-test).
|
||||
- **Машинный журнал уроков `lessons`** (ORCH-098, `feat`): шаг 1 («Фундамент», F2) эпика саморазвития — формализует свободнотекстовые «уроки» из `memory/` в **машинную структурированную таблицу отклонений конвейера** `lessons`, фундамент для будущих ретроспективщика (E2), приоритизатора RICE (E3) и Стрим. Чистый **observer-leaf** `src/lessons.py` (never-raise, kill-switch, паттерн `serial_gate`/`coverage_gate`/`metrics`): `record()`/`get()`/`update()`/`snapshot()`. **Инвариант:** журнал — наблюдатель, **не** Quality Gate — `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict/схемы существующих таблиц байт-в-байт не тронуты; enduro не затронут. ADR: `docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md`, сквозной `docs/architecture/adr/adr-0034-lessons-journal.md`.
|
||||
- **Таблица (D1, FR-1):** аддитивная идемпотентная `lessons` (`CREATE TABLE IF NOT EXISTS` в `db.init_db()` + три индекса, restart-safe) — контекст (`work_item_id`/`task_id`/`stage`/`agent`/`repo`), анализ (`root_cause`/`suggestion`), статус (`status`/`related_task`), **колонки атрибуции — сразу и нуллабельно** (`attribution`/`target_repo`/`target_domain`, требование Славы 10.06 / NFR-6, заполняется позже через update; `_ensure_column` форвард-safe на старой таблице) + `source`/`detail`; без `enum`-констрейнтов (слаги forward-compatible). Хелперы `db.record_lesson`/`get_lessons`/`update_lesson`/`lessons_snapshot`/`lessons_recent_dup_exists`.
|
||||
- **НЕ скоупится по репо (D2):** журнал observer-only → единственный регулятор — глобальный kill-switch `lessons_enabled` (env `ORCH_LESSONS_ENABLED`, дефолт `True`); **`lessons_repos` НЕ вводится**. Recorder пишет уроки про **любой** репо (включая enduro-trails); репо-разрез — на **выборке** (`get(repo=…)`).
|
||||
|
||||
@@ -8,138 +8,160 @@ created_at: 2026-06-10
|
||||
model_used: claude-fable-5
|
||||
type: review
|
||||
work_item_id: ORCH-009
|
||||
version: 1
|
||||
version: 2
|
||||
---
|
||||
|
||||
# Review ORCH-009 — Turnkey-онбординг проектов (kit + CLI + runbook)
|
||||
# Review ORCH-009 — Turnkey-онбординг проектов (kit + CLI + runbook) — re-review (цикл 2)
|
||||
|
||||
Ветка: `feature/ORCH-009-turnkey-plane` · Diff vs `origin/main`: 41 файл, +5120/−10.
|
||||
Ветка: `feature/ORCH-009-turnkey-plane` · Diff vs `origin/main`: 46 файлов, +5478/−12.
|
||||
Состав: kit `onboarding/repo-skeleton/` (28 файлов), CLI `scripts/onboard_project.py` (1090 строк),
|
||||
runbook `docs/operations/ONBOARDING.md`, 3 тест-модуля (83 теста), golden-source доки, ADR×2.
|
||||
runbook `docs/operations/ONBOARDING.md`, 3 онбординг-тест-модуля (83 теста), golden-source доки,
|
||||
ADR×2 + индекс.
|
||||
|
||||
**Контекст цикла:** review v1 (`APPROVED`, 3×P2/2×P3) → testing `PASS` → re-test merge-gate упал на
|
||||
**средовых** не-герметичных тестах ORCH-41-эры (прод-env `ORCH_AGENT_FALLBACK_MODEL`/
|
||||
`ORCH_AGENT_MODEL_DEFAULT`) → откат на development → фикс `e903818` (герметизация
|
||||
`tests/test_resolve_agent_{model,effort}.py`) + регенерация `17-security-report.md` (`b26a391`).
|
||||
Этот review: независимая проверка дельты цикла + выборочная верификация клеймов v1.
|
||||
|
||||
## Summary
|
||||
|
||||
PR реализует ТЗ полностью и точно по ADR-001 (D1–D11), с нулевым касанием рантайма. Вердикт
|
||||
**APPROVED**: P0/P1 findings нет; найдены 3×P2 (харднинг краевых путей CLI) и 2×P3 (косметика) —
|
||||
не блокируют, рекомендованы к follow-up. Документация обновлена в том же PR по всем требуемым
|
||||
точкам.
|
||||
**APPROVED.** P0/P1 нет. Дельта цикла (фикс герметичности тестов) корректна, трассирована к
|
||||
ORCH-074 ADR-001 Решение 3 и сохраняет его инвариант; полный регресс теперь зелёный **под
|
||||
фактическим прод-env** (перепроверено мной: 1713 passed, exit 0 — ровно та среда, что валила
|
||||
merge-gate до фикса). Клеймы review v1 выборочно перепроверены и подтверждены. Переносятся 3×P2
|
||||
(харднинг краевых путей CLI, не фикшены — легитимно, follow-up) + 3×P3. Документация обновлена в
|
||||
том же PR по всем точкам, включая отдельную CHANGELOG-запись про сам фикс тестов.
|
||||
|
||||
**Проверено по 4 осям:**
|
||||
## Оси проверки
|
||||
|
||||
### Ось 1 — Соответствие ТЗ (`02-trz.md`, `03-acceptance-criteria.md`) — ✅
|
||||
|
||||
| Требование | Статус | Чем подтверждено |
|
||||
|---|---|---|
|
||||
| FR-1 состав kit | ✅ | AC-1/TC-01: все 19 элементов на месте; `_templates`/`_standards` в kit НЕ хранятся (анти-форк тест) |
|
||||
| FR-2 канон 52d/92 промптов | ✅ | AC-2/TC-03…06: 5 XML-секций в нормативном порядке, «❌→✅», `<escalation>` у dev/reviewer/tester, `<thinking>` — паритет с эталоном (architect/reviewer/tester/deployer в обоих деревьях), 52c-схема, verdict-ключи байт-в-байт, даты/модели — плейсхолдеры |
|
||||
| FR-2 reviewer-gate доки | ✅ | AC-3/TC-07: «документация НЕ обновлена → `REQUEST_CHANGES`» в kit reviewer.md |
|
||||
| FR-3 INFRA.md шаблон | ✅ | AC-10/TC-19: топология/контейнеры/env-карта/границы доступа/риски общего хоста/деплой |
|
||||
| FR-4 CLI plan/apply/verify | ✅ | AC-7/8/9, TC-13…18: 22 статуса из `_PLANE_NAME_TO_KEY`, группы по D5, лейблы из конфига, dry-run без единой мутации (мутации materialize/push замоканы на AssertionError), идемпотентный ensure, delete-операций нет, `docker`/`systemctl`/`compose`/запись `.env` отсутствуют в исходнике (TC-18-тест по AST/grep) |
|
||||
| FR-5 verify | ✅ | round-trip реестра фактическим парсером, резолв всех 22 имён (включая fail-closed `Confirm Deploy`/`STOP` — отдельный негативный тест), лейблы, webhook active, полнота kit, скан `{{…}}` |
|
||||
| FR-6 runbook | ✅ | AC-11/TC-20: все слои в порядке, 🖐-маркировка ручных шагов + команды проверки, self-hosting-предупреждение о групповом окне рестарта, workspace-webhook — «существует, только проверка» |
|
||||
| §4/§5 нет API/БД изменений | ✅ | diff: `src/**` пуст |
|
||||
| §9 инварианты | ✅ | см. ось 2 |
|
||||
| AC-12 регресс | ✅ | 1794 passed локально; 2 падения (`test_resolve_agent_model`/`_effort`) — **средовые, pre-existing**: вызваны `ORCH_AGENT_FALLBACK_MODEL`/`ORCH_AGENT_EFFORT_*` в env агент-раннера, с очищенной средой 49/49 зелёные; файлы этих тестов PR не трогает — не регресс этого PR (авторитетен CI с чистой средой) |
|
||||
| AC-13 операторский smoke | ⏳ | По построению выполняется на приёмке (tester/оператор): runbook §5.2 + «Журнал smoke-прогонов» (плейсхолдер первого прогона), D8 требует ссылку из `13-test-report.md`. **Handoff-заметка стадии testing** — см. «Для следующей стадии» |
|
||||
| FR-1 состав kit (19 элементов, анти-форк канона) | ✅ | TC-01/02 зелёные; `docs/_templates|_standards` в kit не хранятся — live-copy в `materialize_kit` (`LIVE_COPY_DIRS`, прочитан код) |
|
||||
| FR-2 канон 52d/92 промптов (5 секций, ❌→✅, `<escalation>`, 52c-схема, verdict-ключи байт-в-байт, плейсхолдерные даты/модели) | ✅ | TC-03…06 зелёные; verdict-ключи в kit-промптах сверены grep'ом (`verdict:`/`staging_status:`/`deploy_status:`/`security_status:` на месте) |
|
||||
| FR-2 reviewer-gate доки (AC-3) | ✅ | kit `reviewer.md:65`: «документация НЕ обновлена → вердикт ОБЯЗАТЕЛЬНО `REQUEST_CHANGES`» — прочитано лично |
|
||||
| FR-3 INFRA.md шаблон | ✅ | TC-19 зелёный (топология/env/границы/риски общего хоста); INFRA орка не тронут (diff пуст) |
|
||||
| FR-4 CLI plan/apply/verify | ✅ | Код прочитан полностью: `plan` GET-only (рендер in-memory), `apply` идемпотентный ensure без delete, 22 статуса из `_PLANE_NAME_TO_KEY` + `STATE_GROUPS` 1:1 c ADR D5, CE-отказ → `ManualStep` → `manual-step` (fail-safe); TC-13…18 зелёные |
|
||||
| FR-5 verify | ✅ | round-trip фактическим парсером, резолв всех 22 имён, лейблы, webhook active, полнота kit (`VERIFY_KIT_FILES`), скан `{{…}}`, канон ≥16/≥3 |
|
||||
| FR-6 runbook | ✅ | `ONBOARDING.md` прочитан: слои 0→6 в порядке BR-1, каждый 🖐-шаг с командой проверки, self-hosting-предупреждение «групповое окно» (§4.2), workspace-webhook — «существует, только проверка» (§1.5), откат §6 |
|
||||
| §4/§5 нет API/БД изменений | ✅ | diff `src/**` пуст (проверено лично) |
|
||||
| §9 инварианты | ✅ | `git diff origin/main...HEAD -- src/ .openclaw/ docs/_templates/ docs/_standards/ docs/operations/INFRA.md requirements.txt` — **пусто**; сетевых вызовов в тестах нет (фейк-клиенты); новых pip-зависимостей нет |
|
||||
| AC-12 полный регресс | ✅ | **1713 passed, 0 failed, exit 0** — мой прогон в worktree ветки под фактическим хост-env (до фикса здесь было 2 failed) |
|
||||
| AC-13 операторский smoke | ⏳ | По построению операторский (ADR D8); «Журнал smoke-прогонов» — плейсхолдер. Обязателен ДО `Confirm Deploy` — см. handoff |
|
||||
|
||||
### Ось 2 — Соответствие ADR (`06-adr/ADR-001`, сквозной `adr-0035`) — ✅
|
||||
### Ось 2 — Соответствие ADR (`06-adr/ADR-001` D1–D11, сквозной `adr-0035`) — ✅
|
||||
|
||||
- **D1–D11 реализованы без отступлений**: раскладка top-level `onboarding/` (D1); `{{NAME}}` +
|
||||
`str.replace` + обязательный пост-скан + биекция словаря (D2, тест); live-copy verbatim
|
||||
`_templates`(≥16)/`_standards`(≥3) (D3, тест байт-сравнения); закрытый список импортов `src` —
|
||||
AST-тест `test_tc21_cli_src_imports_stay_in_closed_list` (D4); таблица групп `STATE_GROUPS` 1:1
|
||||
с таблицей D5, код-критичные констрейнты загвождены тестом (`STOP`→`cancelled`; терминальные
|
||||
группы == {Done, Cancelled, STOP}; set-равенство с `_PLANE_NAME_TO_KEY` ловит будущий дрейф);
|
||||
`auto_init=false` + переиспользование глобального секрета + push только в пустой репо (D6 —
|
||||
сверил с приёмником `src/webhooks/gitea.py:38-41`: действительно ОДИН глобальный секрет);
|
||||
merged-full-array + round-trip + «.env не правим» (D7); smoke на staging 8501 (D8); 5 ru +
|
||||
deployer en c «Do NOT translate»-гардом (D9, тест на кириллицу); runbook фиксирует «branch
|
||||
protection НЕ включать» (D10); plan/apply/verify, чистый `build_plan`, инжектируемые клиенты,
|
||||
отчёт `created/skipped(exists)/manual-step/planned/error`, exit-коды 0/2/1 (D11).
|
||||
- **Инварианты NFR-1/INV-4**: `git diff origin/main...HEAD -- src/ .openclaw/ docs/_templates/
|
||||
docs/_standards/ docs/operations/INFRA.md` — пусто; снапшот-тесты `STAGE_TRANSITIONS`/`QG_CHECKS`
|
||||
зелёные; push — только initial в свежесозданный пустой репо (вне конвейера до регистрации),
|
||||
PR-merge API не затрагивается.
|
||||
- **Трассировка (TRACEABILITY.md)**: правка `docs/operations/SETUP_WEBHOOKS.md` обобщает
|
||||
enduro-хардкод, **усиливая** (не ломая) инвариант одного глобального HMAC-секрета — сверено с
|
||||
кодом приёмника; правка `docs/architecture/adr/README.md` — реестр сквозных ADR (общий индекс),
|
||||
бэкфилл строк 0032–0034 сверен: все три файла существуют в main, номера/задачи корректны,
|
||||
«текущий максимум 0035» верен. Чужие маркированные инварианты не задеты.
|
||||
- **D1** top-level `onboarding/` ✅; **D2** `{{NAME}}` + `str.replace` + обязательный пост-скан
|
||||
(`PLACEHOLDER_RE`, ValueError в `materialize_kit`) + биекция словаря тестом ✅; **D3**
|
||||
live-copy verbatim, существующие файлы не перезаписываются ✅; **D4** закрытый список импортов
|
||||
`src` — в скрипте ровно три (`settings`, `_PLANE_NAME_TO_KEY`, `_parse_projects_json`),
|
||||
загвожден AST-тестом TC-21 ✅; **D5** `STATE_GROUPS` 1:1 с таблицей ADR (22 имени, set-равенство
|
||||
с `_PLANE_NAME_TO_KEY` тестом; `STOP`→`cancelled`; терминальные группы только
|
||||
Done/Cancelled/STOP; `Rejected`→`started`) ✅; **D6** `auto_init=False`, переиспользование
|
||||
глобального HMAC-секрета, push только в свежесозданный/пустой репо ✅; **D7** merged-full-array
|
||||
+ round-trip + `.env` read-only ✅; **D8** smoke на staging 8501, журнал в runbook ✅; **D9**
|
||||
5 ru + deployer en с «Do NOT translate»-гардом и рамкой shared-host-гардрейлов (прочитано
|
||||
лично) ✅; **D10** runbook §2.3 «branch protection НЕ включать» ✅; **D11** plan/apply/verify,
|
||||
чистый `build_plan`, инжектируемые клиенты, отчёт `created/skipped(exists)/manual-step/planned/
|
||||
error`, exit-коды 0/2/1 ✅.
|
||||
- **Трассировка (`docs/_standards/TRACEABILITY.md`) — дельта цикла:**
|
||||
- `tests/test_resolve_agent_{model,effort}.py` несут маркеры **ORCH-41/ORCH-074** — сверено с
|
||||
`docs/work-items/ORCH-074/06-adr/ADR-001-model-name-validation.md` **Решение 3 (G4)**:
|
||||
инвариант = «**shipped-дефолт** `agent_fallback_model` остаётся `""`». Фикс переводит ассерт
|
||||
с env-backed singleton на **класс-дефолт поля** (`model_fields[...].default == ""`) — это
|
||||
и есть подлинный инвариант ADR (заводской дефолт, а не рантайм-конфиг оператора);
|
||||
never-break ассерты `is_valid_model` — байт-в-байт. Инвариант **сохранён и уточнён**,
|
||||
обоснование — в коммит-месседже и инлайн-комментариях со ссылкой на ADR. Чужой инвариант
|
||||
не сломан → finding нет.
|
||||
- `docs/architecture/adr/README.md` — бэкфилл строк 0032–0035 сверен: все 4 файла
|
||||
(`adr-0032-bug-fast-track`, `adr-0033-sidecar-watchdog`, `adr-0034-lessons-journal`,
|
||||
`adr-0035-turnkey-project-onboarding`) существуют, привязки задач корректны, «текущий
|
||||
максимум 0035» верен.
|
||||
- `docs/operations/SETUP_WEBHOOKS.md` — обобщение per-repo **усиливает** инвариант одного
|
||||
глобального HMAC-секрета (явное предупреждение про ротацию на всех репо разом).
|
||||
- **Инварианты NFR-1/INV-4:** снапшот-тесты `STAGE_TRANSITIONS`/`QG_CHECKS` зелёные; push —
|
||||
только initial в пустой репо вне конвейера; PR-merge API не затрагивается.
|
||||
|
||||
### Ось 3 — Качество кода — ✅ (с P2-findings ниже)
|
||||
### Ось 3 — Качество кода — ✅ (с переносными P2 ниже)
|
||||
|
||||
Docstrings на всех публичных функциях; чистое ядро отделено от I/O; единственная точка
|
||||
subprocess (только `git`, cwd = temp-материализация, токен в логе маскируется); секрет в отчёте
|
||||
`***` + явный тест non-leak (`test_secret_never_leaks_into_report`); тесты содержательные
|
||||
(recording-фейки мутаций, негативные сценарии CE-отказов, AST-проверка импортов, monkeypatch-мины
|
||||
на мутации в dry-run) — не тривиальные. Багфикс-трек не применим (задача не `Bug`).
|
||||
- CLI: чистое разделение слоёв (ядро без I/O / тонкие клиенты / режимы), docstrings на всех
|
||||
публичных функциях, единственная точка subprocess (только `git`, токен в логе маскируется
|
||||
`://***@`), `ManualStep` fail-safe вместо падений, delete-операций нет вовсе, секрет в отчёте
|
||||
`***` + тест non-leak. Тесты содержательные: AST-проверка закрытого списка импортов,
|
||||
monkeypatch-мины на мутации в dry-run, негативные CE-сценарии, set-равенство против дрейфа
|
||||
констант — не тривиальные.
|
||||
- **Фикс герметичности (дельта цикла) — корректен:** autouse-фикстуры пиняют shipped-дефолты
|
||||
(зеркально между файлами-сиблингами), в чистом env поведение байт-эквивалентно; класс среды
|
||||
merge-gate re-test (прод-env) теперь покрыт. Перепроверено прогоном: 1713 passed под хост-env.
|
||||
Правка существующих тестов вне инвентаря ТЗ §2 — легитимна: инвентарь «рабочее предложение»,
|
||||
ни один инвариант §9 не запрещает правку тестов; без неё PR непроходим через merge-gate
|
||||
(латентная мина `main`, детонированная сменой прод-env).
|
||||
- Багфикс-трек (ORCH-019, BR-4): не применим — задача не `Bug`, маршрут полный.
|
||||
|
||||
### Ось 4 — Документация — ✅ ОБНОВЛЕНА В ТОМ ЖЕ PR
|
||||
|
||||
| Точка | Статус |
|
||||
|---|---|
|
||||
| `CLAUDE.md` — раздел «Turnkey-онбординг (ORCH-009)» | ✅ |
|
||||
| `docs/architecture/README.md` — раздел + ссылки на ADR | ✅ |
|
||||
| `CHANGELOG.md` — запись `feat` (детальная) | ✅ |
|
||||
| `CLAUDE.md` — раздел «Turnkey-онбординг проектов (ORCH-009)» | ✅ |
|
||||
| `docs/architecture/README.md` — раздел + ссылки на оба ADR | ✅ (diff прочитан, фактам соответствует) |
|
||||
| `CHANGELOG.md` — детальная `feat`-запись **+ отдельная под-запись про фикс герметичности тестов** | ✅ (дельта цикла задокументирована — образцово) |
|
||||
| ADR per-WI `06-adr/ADR-001` + сквозной `adr-0035` + индекс `adr/README.md` | ✅ |
|
||||
| `docs/operations/ONBOARDING.md` (новый runbook) | ✅ |
|
||||
| `docs/operations/SETUP_WEBHOOKS.md` — обобщён per-repo (ТЗ §2) | ✅ |
|
||||
| `docs/operations/SETUP_WEBHOOKS.md` — обобщён per-repo | ✅ |
|
||||
| `onboarding/README.md` — устройство kit, словарь, анти-форк | ✅ |
|
||||
| README «Известные ограничения» (ORCH-079) | N/A — онбординг в списке открытых ограничений не значится, обновление не требуется (проверено) |
|
||||
| `08-data-requirements.md` отсутствует | Легитимно: гейт `check_analysis_complete` требует только 01–04; ТЗ §5 «изменений БД нет» |
|
||||
| README «Известные ограничения» (ORCH-079) | **N/A — проверено лично:** открыты 3 пункта (Telegram 48h / intra-repo deps ORCH-026 / пакетный автоном Этап 1) — ни один этим PR не закрывается |
|
||||
| `17-security-report.md` | ✅ `security_status: PASS` (0 secrets, 0 blocking) |
|
||||
| `08-data-requirements.md` отсутствует | Легитимно: гейт `check_analysis_complete` требует 01–04; ТЗ §5 «изменений БД нет» |
|
||||
|
||||
## Findings
|
||||
|
||||
### P0 — Blocker
|
||||
Нет.
|
||||
- (нет)
|
||||
|
||||
### P1 — Must fix
|
||||
Нет.
|
||||
- (нет)
|
||||
|
||||
### P2 — Should fix (follow-up, не блокирует)
|
||||
### P2 — Should fix (перенос из review v1 — не фикшены, перепроверены: всё ещё в коде; follow-up, не блокируют)
|
||||
- [ ] **Quoted-значение в `.env` → тихая потеря существующих записей в merged-выводе.**
|
||||
`read_existing_registry` (fallback-парс `.env`) возвращает значение после `=` как есть; если
|
||||
строка в `.env`/`--env-file` взята в кавычки (`ORCH_PROJECTS_JSON='[...]'`), `json.loads`
|
||||
в `merged_projects_json` молча даёт `existing=[]` → инструкция оператору содержит массив ТОЛЬКО
|
||||
с новым проектом, а runbook §4.1 велит «заменить строку» → риск выпадения enduro/orchestrator
|
||||
из реестра. Доминирующий путь безопасен (pydantic `env_file=".env"` снимает кавычки, фоллбек
|
||||
срабатывает только при cwd вне корня), потому P2, не P1. Рекомендация: в фоллбеке `strip("'\"")`
|
||||
+ предупреждение/GAP, если строка в `.env` есть, а распарсенный existing пуст. (AC-6-периметр;
|
||||
ADR D7 «существующие не теряются».)
|
||||
`read_existing_registry` (строка ~355) возвращает значение после `=` как есть; кавычки →
|
||||
`json.loads` в `merged_projects_json` молча даёт `existing=[]` → merged-массив только с новым
|
||||
проектом, а runbook §4.1 велит «заменить строку». Доминирующий путь безопасен (pydantic
|
||||
снимает кавычки), потому P2. Рекомендация: `strip("'\"")` в фоллбеке + GAP-warning, если строка
|
||||
в `.env` есть, а existing пуст. (ADR D7 «существующие не теряются».)
|
||||
- [ ] **`GiteaClient.create_repo`: фоллбек `POST /user/repos` может создать репо в чужом
|
||||
namespace.** При `--gitea-owner`, не являющемся ни org, ни юзером токена, отказ org-маршрута
|
||||
ведёт в `/user/repos` → репо рождается под юзером токена, последующие webhook/push по
|
||||
`owner/repo` дают 404/manual-step. Не деструктивно и видимо в отчёте, но это непрошенная
|
||||
мутация не туда. Рекомендация: сверять `owner.login` ответа с запрошенным owner; расхождение →
|
||||
`manual-step` (комментарий в коде уже упоминает admin-маршрут — либо реализовать
|
||||
`/admin/users/{owner}/repos`, либо честно деградировать).
|
||||
namespace** (строки ~474–477): owner не org и не юзер токена → репо рождается под юзером
|
||||
токена, последующие шаги по `owner/repo` дают 404/manual-step. Рекомендация: сверять
|
||||
`owner.login` ответа с запрошенным; расхождение → `manual-step`.
|
||||
- [ ] **CE-деградация Plane + успешный Gitea в одном apply запекает литерал
|
||||
`<assigned-on-apply>` в запушенный паспорт.** Если `plane.project` ушёл в `manual-step`, а репо
|
||||
создан — kit рендерится с `PLANE_PROJECT_ID="<assigned-on-apply>"` и пушится; повторный apply
|
||||
с `--plane-project-id` уже не перезапишет (репо непустой). Скан ловит только `{{…}}`-синтаксис.
|
||||
Рекомендация: при неразрешённом PLANE_PROJECT_ID деградировать `kit.materialize`/`kit.push` в
|
||||
`manual-step` (push после получения uuid) ИЛИ добавить `<assigned-on-apply>` в скан verify.
|
||||
`<assigned-on-apply>` в запушенный паспорт** (`build_params` → `PLANE_PROJECT_ID`); скан ловит
|
||||
только `{{…}}`. Рекомендация: при неразрешённом `PLANE_PROJECT_ID` деградировать
|
||||
`kit.materialize`/`kit.push` в `manual-step` ИЛИ добавить `<assigned-on-apply>` в скан verify.
|
||||
|
||||
### P3 — Nice to have
|
||||
- [ ] `--env-file` молча игнорируется в `plan`-режиме (`_registry_instructions(report, params,
|
||||
None)`): превью merged-массива в plan может расходиться с apply при нестандартном env-файле.
|
||||
- [ ] Push-URL с `oauth2:<token>@` остаётся в `.git/config` временного каталога материализации
|
||||
после успешного apply (temp-dir не чистится). Рекомендация: cleanup на успехе (на ошибке
|
||||
сохранять для дебага, как сейчас).
|
||||
- [ ] `--env-file` игнорируется в `plan` (`run_plan` → `_registry_instructions(report, params,
|
||||
None)`; `main()` его в `run_plan` и не передаёт): превью merged-массива может расходиться с apply.
|
||||
- [ ] Push-URL с `oauth2:<token>@` остаётся в `.git/config` temp-каталога после успешного apply
|
||||
(cleanup нет). Рекомендация: чистить на успехе, на ошибке сохранять для дебага.
|
||||
- [ ] *(новое)* `run_apply`: шаг `registry.emit` добавляется со статусом `CREATED` **до**
|
||||
`_registry_instructions`, который на ошибке round-trip добавляет второй шаг `registry.emit`
|
||||
со статусом `ERROR` → дубль step-id в отчёте (exit-код при этом честный — 1). Косметика отчёта.
|
||||
|
||||
## Документация
|
||||
|
||||
Обновлена полностью в том же PR (см. таблицу оси 4): паспорт, архитектурный README, CHANGELOG,
|
||||
оба ADR + индекс, новый runbook, обобщённый SETUP_WEBHOOKS, README kit. Несоответствий
|
||||
«код изменён — дока молчит» не найдено; обзорная витрина README не затронута задачей по
|
||||
построению (ограничение в ней не значилось).
|
||||
Обновлена полностью в том же PR (таблица оси 4). Несоответствий «код изменён — дока молчит» нет;
|
||||
дельта цикла (фикс тестов) получила собственную CHANGELOG-запись с диагнозом и обоснованием;
|
||||
обзорная витрина README задачей не затрагивается (проверено: открытые ограничения не про онбординг).
|
||||
|
||||
## Для следующей стадии (testing) — handoff
|
||||
|
||||
1. **AC-13 (операторский smoke)**: прогон по runbook §5.2 (staging 8501, sandbox `SMK`) должен
|
||||
быть выполнен и запротоколирован в «Журнале smoke-прогонов» `ONBOARDING.md`, ссылка — из
|
||||
`13-test-report.md` (требование D8). Это единственный непокрытый pytest'ом AC.
|
||||
2. Локальный полный регресс гонять с чистой средой (без `ORCH_AGENT_FALLBACK_MODEL`/
|
||||
`ORCH_AGENT_EFFORT_*` агент-раннера) — иначе 2 ложных средовых падения в
|
||||
`test_resolve_agent_model.py`/`test_resolve_agent_effort.py` (pre-existing, к PR отношения
|
||||
не имеют).
|
||||
1. **AC-13 (операторский smoke, ADR D8)** — единственный непокрытый pytest'ом AC: прогон по
|
||||
runbook §5.2 (staging 8501, sandbox `SMK`) должен быть выполнен и запротоколирован в «Журнале
|
||||
smoke-прогонов» `ONBOARDING.md`, ссылка — из `13-test-report.md`. Обязателен **до**
|
||||
`Confirm Deploy` (человеческий гейт — точка контроля сохраняется).
|
||||
2. Средовая мина merge-gate обезврежена фиксом `e903818`: полный регресс зелёный и в чистом env,
|
||||
и под прод-env (1713 passed, проверено в этом review) — спец-обвязка прогона больше не нужна.
|
||||
3. `13-test-report.md` в дереве — от прошлого цикла (до `e903818`): его строка «PR эти файлы не
|
||||
трогает» про `tests/test_resolve_agent_*` устарела. Перегенерировать отчёт штатно (артефакт
|
||||
чужой стадии — в этом review не правился).
|
||||
|
||||
@@ -1,169 +1,117 @@
|
||||
---
|
||||
result: PASS
|
||||
result: PASS # PASS | FAIL — машинный вердикт, UPPERCASE
|
||||
work_item: ORCH-009
|
||||
stage: testing
|
||||
author_agent: tester
|
||||
status: pass
|
||||
created_at: 2026-06-10
|
||||
model_used: claude-fable-5
|
||||
model_used: claude-opus-4-8
|
||||
type: test-report
|
||||
work_item_id: ORCH-009
|
||||
---
|
||||
|
||||
# Test Report — ORCH-009 — Turnkey-онбординг проектов (kit + CLI + runbook)
|
||||
|
||||
> Машинный вердикт — в frontmatter (`result: PASS`). Ниже — факты, на которых он основан.
|
||||
> Машинный вердикт читается ТОЛЬКО из frontmatter (`result:`). Перепрогон стадии testing на
|
||||
> opus после сброса session-limit (ре-ран по запросу). Review-вердикт цикла 2 — `APPROVED`
|
||||
> (`12-review.md`, P0/P1 нет). Дельта цикла (герметизация ORCH-41-тестов `e903818`) перепроверена
|
||||
> полным регрессом под фактическим окружением worktree.
|
||||
|
||||
## Окружение
|
||||
- Python: 3.12.13
|
||||
- pytest: 8.3.3
|
||||
- pytest: 8.3.3 (pytest-cov 5.0.0, asyncio 0.23.8)
|
||||
- Дата: 2026-06-10
|
||||
- Worktree: `/repos/_wt/orchestrator/feature_ORCH-009-turnkey-plane/` (ветка
|
||||
`feature/ORCH-009-turnkey-plane`) — прогон в worktree ветки задачи, НЕ в общем чекауте
|
||||
`/repos/orchestrator` (анти-гонка checkout).
|
||||
- Прод `http://localhost:8500/health` → `{"status":"ok","service":"orchestrator"}` ✅
|
||||
- Staging `http://localhost:8501/health` → `{"status":"ok","service":"orchestrator"}` ✅
|
||||
- Прод-контейнер не трогался (read-only smoke, NFR self-hosting).
|
||||
- Worktree: `feature/ORCH-009-turnkey-plane` (`/repos/_wt/orchestrator/feature_ORCH-009-turnkey-plane`, HEAD `b97ffae`)
|
||||
- Прод-контейнер `orchestrator` (8500) — НЕ трогался (smoke read-only).
|
||||
|
||||
## Smoke API (read-only)
|
||||
- `GET /health` → `{"status":"ok","service":"orchestrator"}` ✅
|
||||
- `GET /status` → отвечает; задача ORCH-009 (task 87) на стадии `testing` ✅
|
||||
- `GET /queue` → блок `serial_gate` **присутствует** (ORCH-088) ✅; блок `auto_labels` присутствует ✅
|
||||
(полный набор ключей: `auto_labels, bug_fast_track, build_cache_prune, counts, coverage,
|
||||
disk_monitor, fs_ownership, lessons, max_concurrency, merge_verify, poll_interval, post_deploy,
|
||||
reaper, recent, reconcile, resilience, serial_gate, stop, task_deps`).
|
||||
|
||||
## Результаты
|
||||
|
||||
### Полный регресс (`pytest tests/ -v --tb=short`)
|
||||
**1712 passed, 1 failed (средовое, pre-existing — не регресс PR), 1 warning, 69.90s.**
|
||||
### Полный регресс
|
||||
`pytest tests/ -q` → **1713 passed, 0 failed, 1 warning** за 65.40s (exit 0). Прод-контейнер не
|
||||
трогался. Средовая мина merge-gate цикла 1 обезврежена фиксом `e903818` — регресс зелёный.
|
||||
|
||||
Единственное падение — `tests/test_resolve_agent_effort.py::test_flags_present_when_configured`:
|
||||
```
|
||||
assert "--model claude-opus-4-8 " in flags
|
||||
E AssertionError: assert '--model claude-opus-4-8 ' in
|
||||
'--model claude-fable-5 --effort xhigh --fallback-model claude-sonnet-4-6 '
|
||||
```
|
||||
Диагноз (подтверждает handoff-пункт №2 reviewer'а в `12-review.md`): падение вызвано
|
||||
env-переменными агент-раннера (`ORCH_AGENT_MODEL_DEFAULT=claude-fable-5`,
|
||||
`ORCH_AGENT_FALLBACK_MODEL`, `ORCH_AGENT_EFFORT_*`), а не кодом ветки. Контрольный перепрогон
|
||||
с полностью очищенной средой (`env -u ORCH_AGENT_*`):
|
||||
```
|
||||
pytest tests/test_resolve_agent_effort.py tests/test_resolve_agent_model.py -q
|
||||
49 passed, 1 warning in 0.44s
|
||||
```
|
||||
Дополнительно проверено: `git diff --name-only origin/main...HEAD` НЕ содержит `src/**`,
|
||||
`.openclaw/**`, `tests/test_resolve_agent_*` — PR эти файлы не трогает (pre-existing средовой
|
||||
эффект; авторитетен CI с чистой средой). С учётом контрольного прогона **эффективный регресс —
|
||||
полностью зелёный**.
|
||||
### Профильные сюиты (онбординг)
|
||||
`pytest tests/test_onboarding_kit.py tests/test_onboarding_script.py tests/test_onboarding_invariants.py -v`
|
||||
→ **83 passed, 0 failed** за 0.55s (exit 0). Сетевых вызовов нет (Plane/Gitea — фейк-клиенты, NFR-5).
|
||||
|
||||
### Профильные сюиты задачи
|
||||
| Модуль | Результат |
|
||||
|--------|-----------|
|
||||
| `tests/test_onboarding_kit.py` | **60/60 PASSED** |
|
||||
| `tests/test_onboarding_script.py` | **18/18 PASSED** |
|
||||
| `tests/test_onboarding_invariants.py` | **5/5 PASSED** |
|
||||
| **Итого профильных** | **83/83 PASSED** |
|
||||
### Сопоставление с тест-планом (`04-test-plan.yaml`)
|
||||
|
||||
### Smoke API (read-only, прод 8500)
|
||||
| Проверка | Результат |
|
||||
|----------|-----------|
|
||||
| `GET /health` | ✅ `{"status":"ok"}` |
|
||||
| `GET /status` | ✅ активные задачи отдаются (ORCH-009 `stage=testing`, ORCH-101 `analysis`) |
|
||||
| `GET /queue` → блок `serial_gate` (ORCH-088) | ✅ **присутствует**: `enabled: true`, per-repo картина корректна (активная ORCH-009, ожидающая ORCH-101 — FIFO, заморозок нет) |
|
||||
| `GET /queue` → блок `auto_labels` (ORCH-089) | ✅ присутствует (`autoApprove`/`autoDeploy`) |
|
||||
| `GET /health` staging 8501 (контур smoke D8) | ✅ ok |
|
||||
| TC ID | Описание | Тест-функция | Рез. |
|
||||
|-------|----------|--------------|------|
|
||||
| TC-01 | Kit содержит все элементы FR-1 (6 промптов + доки) | `test_tc01_kit_contains_all_required_elements`, `test_tc01_kit_readme_and_placeholder_dictionary_exist` | PASS |
|
||||
| TC-02 | Материализация добавляет live-копии `_templates`/`_standards`; форк канона отсутствует | `test_tc02_materialise_live_copies_canon`, `test_kit_does_not_fork_the_canon` | PASS |
|
||||
| TC-03 | 5 XML-секций в нормативном порядке (6 ролей) | `test_tc03_five_xml_sections_in_normative_order[*]` | PASS |
|
||||
| TC-04 | `<escalation>` у dev/rev/tester; запреты «❌→✅» | `test_tc04_escalation_section_after_success_criteria[*]`, `test_tc04_bans_use_cross_check_format[*]` | PASS |
|
||||
| TC-05 | Директивы доки (читай паспорт/AGENTS/ADR; пиши в work-items; CHANGELOG) | `test_tc05_prompt_directs_agent_to_docs[*]`, `test_tc05_changelog_duty_present[*]`, `test_tc05_architect_carries_adr_rules` | PASS |
|
||||
| TC-06 | 6-польная схема 52c; verdict-ключи байт-в-байт; даты/модели — плейсхолдеры | `test_tc06_six_schema_fields_named[*]`, `test_tc06_schema_pins_role_author_and_stage[*]`, `test_tc06_machine_verdict_keys_byte_exact`, `test_tc06_dates_and_models_are_placeholders[*]` | PASS |
|
||||
| TC-07 | reviewer-gate: дока не обновлена → `REQUEST_CHANGES` | `test_tc07_reviewer_gate_docs_not_updated_means_request_changes` | PASS |
|
||||
| TC-08 | Языковая политика (5 ru + deployer en) | `test_tc08_ru_canon_for_five_roles[*]`, `test_tc08_deployer_is_english` | PASS |
|
||||
| TC-09 | Рендер подставляет все плейсхолдеры (нет неразрешённых) | `test_tc09_render_resolves_all_placeholders`, `test_render_is_a_pure_replace` | PASS |
|
||||
| TC-10 | Нет утечек орк-специфики (ORCH-/8500/8501/self-hosting) | `test_tc10_no_orchestrator_specific_leaks` | PASS |
|
||||
| TC-11 | Ссылочная целостность отрендеренных промптов/AGENTS | `test_tc11_referenced_paths_exist_in_materialised_tree` | PASS |
|
||||
| TC-12 | Registry round-trip через фактический `_parse_projects_json`; существующие записи целы | `test_tc12_registry_round_trip_through_actual_parser`, `test_tc12_merge_is_idempotent_no_duplicates` | PASS |
|
||||
| TC-13 | План Plane: все статусы `_PLANE_NAME_TO_KEY` (вкл. `Confirm Deploy`/`STOP`) + лейблы | `test_tc13_plan_covers_all_statuses_and_labels`, `test_state_groups_match_plane_name_to_key` | PASS |
|
||||
| TC-14 | Недоступный Plane-шаг → `manual-step` (не падение/не молча) | `test_tc14_plane_refusal_becomes_manual_step` | PASS |
|
||||
| TC-15 | План Gitea: репо + webhook (push/pr/status + HMAC) + initial push | `test_tc15_plan_contains_gitea_repo_webhook_and_push` | PASS |
|
||||
| TC-16 | dry-run (plan) — ноль мутаций | `test_tc16_plan_is_a_pure_dry_run`, `test_secret_never_leaks_into_report` | PASS |
|
||||
| TC-17 | Повторный apply: `skipped(exists)`, без дублей/удалений; отчёт created/skipped/manual | `test_tc17_second_apply_skips_everything_existing` | PASS |
|
||||
| TC-18 | Нет операций рестарта/правки прод-.env/push в существующие репо (NFR-2) | `test_tc18_fresh_apply_runs_git_only_inside_workdir`, `test_tc18_source_has_no_container_or_env_mutation_ops` | PASS |
|
||||
| TC-19 | INFRA.md шаблон: обязательные секции; INFRA орка не тронут | `test_tc19_infra_template_mandatory_sections`, `test_tc19_orchestrator_own_infra_untouched_sections` | PASS |
|
||||
| TC-20 | Runbook: слои предусловия→Plane→Gitea→kit→регистрация→верификация→откат | `test_tc20_runbook_exists_and_layer_order`, `test_tc20_runbook_manual_steps_and_selfhosting_warning`, `test_tc20_runbook_verification_and_smoke_journal` | PASS |
|
||||
| TC-21 | Снапшот `STAGE_TRANSITIONS`/`QG_CHECKS`; `src/**` не ссылается на онбординг; закрытый список импортов | `test_tc21_stage_transitions_snapshot`, `test_tc21_qg_checks_registry_snapshot`, `test_tc21_src_never_references_onboarding`, `test_tc21_cli_src_imports_stay_in_closed_list`, `test_tc21_kit_prompts_name_only_real_gates` | PASS |
|
||||
| TC-22 | Полный регресс `tests/` зелёный | весь прогон `pytest tests/` (1713 passed) | PASS |
|
||||
|
||||
Регресса смока нет.
|
||||
|
||||
### Сопоставление с тест-планом (`04-test-plan.yaml`) — каждый TC
|
||||
|
||||
| TC ID | Описание (кратко) | Тест-функция(и) | Результат |
|
||||
|-------|-------------------|------------------|-----------|
|
||||
| TC-01 | Состав kit: все элементы FR-1 | `test_tc01_kit_contains_all_required_elements`, `test_tc01_kit_readme_and_placeholder_dictionary_exist` | **PASS** |
|
||||
| TC-02 | Live-копии `_templates`/`_standards`, канон не форкается | `test_tc02_materialise_live_copies_canon`, `test_kit_does_not_fork_the_canon` | **PASS** |
|
||||
| TC-03 | 5 XML-секций в нормативном порядке (×6 промптов) | `test_tc03_five_xml_sections_in_normative_order` (6 параметров) | **PASS** |
|
||||
| TC-04 | `<escalation>` у dev/reviewer/tester; «❌ → ✅» | `test_tc04_escalation_section_after_success_criteria`, `test_tc04_bans_use_cross_check_format` | **PASS** |
|
||||
| TC-05 | Директивы доки: паспорт/AGENTS/ARCHITECTURE/ADR перед работой; артефакты в work-items; CHANGELOG | `test_tc05_prompt_directs_agent_to_docs`, `test_tc05_changelog_duty_present`, `test_tc05_architect_carries_adr_rules` | **PASS** |
|
||||
| TC-06 | 6-польная схема 52c; verdict-ключи байт-в-байт; даты/модели — плейсхолдеры | `test_tc06_six_schema_fields_named`, `test_tc06_machine_verdict_keys_byte_exact`, `test_tc06_schema_pins_role_author_and_stage`, `test_tc06_dates_and_models_are_placeholders` | **PASS** |
|
||||
| TC-07 | Reviewer-gate: дока не обновлена → REQUEST_CHANGES | `test_tc07_reviewer_gate_docs_not_updated_means_request_changes` | **PASS** |
|
||||
| TC-08 | Языковая политика: 5 ru + deployer en (ADR D9) | `test_tc08_ru_canon_for_five_roles`, `test_tc08_deployer_is_english` | **PASS** |
|
||||
| TC-09 | Рендер подставляет все плейсхолдеры, неразрешённых нет | `test_tc09_render_resolves_all_placeholders`, `test_render_is_a_pure_replace`, `test_placeholder_dictionary_bijection` | **PASS** |
|
||||
| TC-10 | Нет утечек орк-специфики (ORCH-, 8500/8501, self-hosting) | `test_tc10_no_orchestrator_specific_leaks` | **PASS** |
|
||||
| TC-11 | Ссылочная целостность отрендеренного каркаса | `test_tc11_referenced_paths_exist_in_materialised_tree` | **PASS** |
|
||||
| TC-12 | Registry round-trip через фактический `_parse_projects_json`; существующие записи сохранены | `test_tc12_registry_round_trip_through_actual_parser`, `test_tc12_merge_is_idempotent_no_duplicates` | **PASS** |
|
||||
| TC-13 | План Plane: все 22 статуса `_PLANE_NAME_TO_KEY` (incl. `Confirm Deploy`, `STOP`→`cancelled`) + лейблы | `test_tc13_plan_covers_all_statuses_and_labels`, `test_state_groups_match_plane_name_to_key` | **PASS** |
|
||||
| TC-14 | CE-отказ Plane → `manual-step` со ссылкой на runbook, не молча | `test_tc14_plane_refusal_becomes_manual_step` | **PASS** |
|
||||
| TC-15 | План Gitea: репо + webhook (push/pull_request/status, HMAC вне гита) + initial push | `test_tc15_plan_contains_gitea_repo_webhook_and_push`, `test_secret_never_leaks_into_report` | **PASS** |
|
||||
| TC-16 | `plan` — чистый dry-run: ноль мутаций | `test_tc16_plan_is_a_pure_dry_run` | **PASS** |
|
||||
| TC-17 | Повторный `apply` → `skipped(exists)`, без дублей/удалений | `test_tc17_second_apply_skips_everything_existing` | **PASS** |
|
||||
| TC-18 | Нет рестартов/правки `.env`/push в существующие репо | `test_tc18_source_has_no_container_or_env_mutation_ops`, `test_tc18_fresh_apply_runs_git_only_inside_workdir` | **PASS** |
|
||||
| TC-19 | INFRA.md шаблон: обязательные секции; INFRA орка не тронут | `test_tc19_infra_template_mandatory_sections`, `test_tc19_orchestrator_own_infra_untouched_sections` | **PASS** |
|
||||
| TC-20 | Runbook: все слои в порядке, ручные шаги помечены, журнал smoke | `test_tc20_runbook_exists_and_layer_order`, `test_tc20_runbook_manual_steps_and_selfhosting_warning`, `test_tc20_runbook_verification_and_smoke_journal` | **PASS** |
|
||||
| TC-21 | Снапшоты `STAGE_TRANSITIONS`/`QG_CHECKS`; `src/**` и `.openclaw/` не тронуты | `test_tc21_stage_transitions_snapshot`, `test_tc21_qg_checks_registry_snapshot`, `test_tc21_src_never_references_onboarding`, `test_tc21_cli_src_imports_stay_in_closed_list`, `test_tc21_kit_prompts_name_only_real_gates` | **PASS** |
|
||||
| TC-22 | Полный регресс `tests/` зелёный | весь прогон: 1712 passed (+1 средовой pre-existing, с чистой средой 49/49 — см. выше) | **PASS** |
|
||||
|
||||
**22/22 TC выполнены, все PASS.**
|
||||
**Итого тест-плана: 22/22 TC выполнены и PASS.**
|
||||
|
||||
### Сопоставление с критериями приёмки (`03-acceptance-criteria.md`)
|
||||
|
||||
| AC | Покрытие | Результат |
|
||||
|----|----------|-----------|
|
||||
| AC-1 (состав kit) | TC-01 | ✅ PASS |
|
||||
| AC-2 (канон 52d/92) | TC-03…TC-06 | ✅ PASS |
|
||||
| AC-3 (reviewer-gate доки) | TC-07 | ✅ PASS |
|
||||
| AC-4 (языковая политика, ADR D9) | TC-08 | ✅ PASS |
|
||||
| AC-5 (плейсхолдеры/утечки/целостность) | TC-09…TC-11 | ✅ PASS |
|
||||
| AC-6 (registry round-trip) | TC-12 | ✅ PASS |
|
||||
| AC-7 (план Plane: статусы/лейблы) | TC-13, TC-14 | ✅ PASS |
|
||||
| AC-8 (план Gitea + dry-run без мутаций) | TC-15, TC-16 | ✅ PASS |
|
||||
| AC-9 (идемпотентность/безопасность apply) | TC-17, TC-18 | ✅ PASS |
|
||||
| AC-10 (INFRA.md шаблон) | TC-19 | ✅ PASS |
|
||||
| AC-11 (runbook полон) | TC-20 | ✅ PASS |
|
||||
| AC-12 (инварианты `src/**`) | TC-21, TC-22 + diff-проверка (`origin/main...HEAD`: `src/**`, `.openclaw/**` — пусто) | ✅ PASS |
|
||||
| AC-13 (операторский smoke, ADR D8) | вне pytest-скоупа (по `04-test-plan.yaml`: «выполняется вручную и протоколируется») | ⚠️ **NOT RUN — открытый операторский шаг** (см. ниже) |
|
||||
| AC-1 Kit полон | TC-01 | PASS |
|
||||
| AC-2 Канон 52d/92 промптов | TC-03/04/05/06 | PASS |
|
||||
| AC-3 Reviewer-gate доки | TC-07 | PASS |
|
||||
| AC-4 Языковая политика | TC-08 | PASS |
|
||||
| AC-5 Материализация / нет утечек | TC-02/09/10/11 | PASS |
|
||||
| AC-6 Registry round-trip | TC-12 | PASS |
|
||||
| AC-7 План Plane (статусы/лейблы) | TC-13/14 | PASS |
|
||||
| AC-8 План Gitea + dry-run без мутаций | TC-15/16 | PASS |
|
||||
| AC-9 Идемпотентность/безопасность apply | TC-17/18 | PASS |
|
||||
| AC-10 INFRA.md шаблон | TC-19 | PASS |
|
||||
| AC-11 Runbook полон | TC-20 | PASS |
|
||||
| AC-12 `src/**` не тронут (снапшот + регресс) | TC-21/22 | PASS |
|
||||
| AC-13 Операторский smoke на песочнице | вне pytest (см. ниже) | DEFERRED (операторский гейт до `Confirm Deploy`) |
|
||||
|
||||
### ⚠️ AC-13 — открытый ОБЯЗАТЕЛЬНЫЙ операторский шаг (ADR-001 D8)
|
||||
|
||||
«Журнал smoke-прогонов» в `docs/operations/ONBOARDING.md` (§ строка 186) на момент отчёта
|
||||
содержит **плейсхолдер** — операторский smoke на песочнице (runbook §5.2: онбординг sandbox
|
||||
`onboarding-smoke`/`SMK` → регистрация в `.env.staging` → рестарт staging → тестовая задача →
|
||||
стадия analysis) **не выполнен и не запротоколирован**.
|
||||
|
||||
- Прогон по построению мутирующий (создание сущностей Plane/Gitea, правка `.env.staging`,
|
||||
рестарт staging-контейнера) и в `04-test-plan.yaml`/AC-13 явно классифицирован как **ручной
|
||||
операторский** — он вне полномочий tester-агента (read-only smoke) и не покрывается ни одним
|
||||
TC; дефекта кода нет, поэтому `FAIL`/откат на development не обоснован.
|
||||
- Контур smoke готов: staging 8501 жив (health ok), `verify`-режим CLI и runbook протестированы
|
||||
структурно (TC-13…TC-20).
|
||||
- **Эскалация оператору:** по D8 первый протокол в «Журнале smoke-прогонов» **обязателен для
|
||||
приёмки ORCH-009** — выполнить прогон по runbook §5.2 и заполнить журнал **ДО прод-деплоя**
|
||||
(гейт `Confirm Deploy` — человеческий, точка контроля сохраняется). Ссылка по требованию D8:
|
||||
`docs/operations/ONBOARDING.md` § «Журнал smoke-прогонов».
|
||||
|
||||
## Вывод pytest
|
||||
## AC-13 — операторский smoke (не блокирует ребро testing → deploy-staging)
|
||||
AC-13 по построению (ADR D8, scope-нота `04-test-plan.yaml`) — **документированный операторский
|
||||
прогон** на песочнице staging 8501 с реальными Plane/Gitea-вызовами. Это мутирующая операторская
|
||||
процедура → вне read-only smoke и автоматизированного скоупа тестера. «Журнал smoke-прогонов»
|
||||
в `docs/operations/ONBOARDING.md` сейчас — плейсхолдер (прогон не выполнен).
|
||||
- **Не блокирует данную стадию:** AC-13 обязателен **до `Confirm Deploy`** (человеческий гейт
|
||||
прод-деплоя, ORCH-059), который наступает ПОСЛЕ `deploy-staging`. Ребро `testing → deploy-staging`
|
||||
он не гейтит (это операторская страховка, а не Quality Gate; `QG_CHECKS` не содержит проверки AC-13).
|
||||
- **Handoff оператору:** выполнить runbook §5.2 (staging 8501, sandbox-префикс) и запротоколировать
|
||||
результат в «Журнале smoke-прогонов» `ONBOARDING.md` **перед** нажатием `Confirm Deploy`.
|
||||
|
||||
## Вывод pytest (итоги)
|
||||
```
|
||||
$ cd /repos/_wt/orchestrator/feature_ORCH-009-turnkey-plane && pytest tests/ -v --tb=short
|
||||
...
|
||||
tests/test_onboarding_invariants.py — 5 PASSED
|
||||
tests/test_onboarding_kit.py — 60 PASSED
|
||||
tests/test_onboarding_script.py — 18 PASSED
|
||||
...
|
||||
=================================== FAILURES ===================================
|
||||
______________________ test_flags_present_when_configured ______________________
|
||||
tests/test_resolve_agent_effort.py:190: in test_flags_present_when_configured
|
||||
assert "--model claude-opus-4-8 " in flags
|
||||
E AssertionError: assert '--model claude-opus-4-8 ' in
|
||||
'--model claude-fable-5 --effort xhigh --fallback-model claude-sonnet-4-6 '
|
||||
=========================== short test summary info ============================
|
||||
FAILED tests/test_resolve_agent_effort.py::test_flags_present_when_configured
|
||||
============= 1 failed, 1712 passed, 1 warning in 69.90s (0:01:09) =============
|
||||
# полный регресс
|
||||
1713 passed, 1 warning in 65.40s (exit 0)
|
||||
|
||||
# Контрольный перепрогон средового падения с чистой средой (handoff reviewer):
|
||||
$ env -u ORCH_AGENT_* pytest tests/test_resolve_agent_effort.py tests/test_resolve_agent_model.py -q
|
||||
49 passed, 1 warning in 0.44s
|
||||
# профильные сюиты онбординга
|
||||
83 passed, 1 warning in 0.55s (exit 0)
|
||||
```
|
||||
(Единственный warning — `PydanticDeprecatedSince20` в `src/config.py:8`, существующий, не связан с задачей.)
|
||||
|
||||
## Итог
|
||||
|
||||
**PASS.**
|
||||
- 22/22 TC тест-плана выполнены и зелёные; AC-1…AC-12 подтверждены.
|
||||
- Полный регресс эффективно зелёный (1712 passed; единственное падение — средовое pre-existing,
|
||||
с чистой средой проходит; PR `src/**`/`.openclaw/**`/файлы этих тестов не трогает).
|
||||
- Smoke API без регрессов: `/health`, `/status`, `/queue` (блоки `serial_gate` и `auto_labels`
|
||||
присутствуют); staging 8501 жив.
|
||||
- ⚠️ AC-13 (операторский smoke, D8) — **не закрыт**: обязателен к выполнению и протоколированию
|
||||
оператором до прод-деплоя (`Confirm Deploy`). Дефекта кода нет — вердикт стадии testing PASS.
|
||||
**PASS.** Полный регресс зелёный (1713 passed), все 22 TC тест-плана выполнены и PASS, все
|
||||
машинно-проверяемые AC (1–12) закрыты, read-only smoke API в норме (`serial_gate`/`auto_labels`
|
||||
в `/queue` присутствуют). AC-13 — операторский smoke, отложен к гейту `Confirm Deploy` (не блокирует
|
||||
переход на `deploy-staging`). Задача готова к стадии `deploy-staging`.
|
||||
|
||||
12
docs/work-items/ORCH-009/14-deploy-log.md
Normal file
12
docs/work-items/ORCH-009/14-deploy-log.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
deploy_status: SUCCESS
|
||||
work_item: ORCH-009
|
||||
hook_exit_code: 0
|
||||
deployed_by: deploy-finalizer
|
||||
---
|
||||
|
||||
# Deploy log — ORCH-036 executable self-deploy
|
||||
|
||||
Прод-деплой завершён хост-хуком с exit-code `0` -> `deploy_status: SUCCESS`.
|
||||
|
||||
Вердикт зафиксирован детерминированным finalizer'ом (Фаза C), не LLM.
|
||||
14
docs/work-items/ORCH-009/16-post-deploy-log.md
Normal file
14
docs/work-items/ORCH-009/16-post-deploy-log.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
post_deploy_status: HEALTHY
|
||||
action_taken: NONE
|
||||
work_item: ORCH-009
|
||||
window_s: 900
|
||||
checks_total: 30
|
||||
checks_failed: 0
|
||||
---
|
||||
|
||||
# Post-deploy log — ORCH-021 post-deploy monitor
|
||||
|
||||
Наблюдение прода завершено: `post_deploy_status: HEALTHY`, `action_taken: NONE`.
|
||||
|
||||
Окно наблюдения: 900s; опросов всего: 30, из них с провалом: 0.
|
||||
25
docs/work-items/ORCH-009/17-security-report.md
Normal file
25
docs/work-items/ORCH-009/17-security-report.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
security_status: PASS
|
||||
secrets_found: 0
|
||||
deps_blocking: 0
|
||||
deps_warning: 4
|
||||
deps_audit_degraded: false
|
||||
---
|
||||
# Security Report — ORCH-009
|
||||
|
||||
Детерминированный security-гейт (ORCH-022): secret-scanning (gitleaks, offline) + dependency audit (pip-audit). Машинный вердикт читается ТОЛЬКО из frontmatter выше.
|
||||
|
||||
## Verdict
|
||||
clean: 0 secrets, 0 blocking CVE(s)
|
||||
|
||||
## Secrets
|
||||
- None
|
||||
|
||||
## Dependencies (blocking)
|
||||
- None
|
||||
|
||||
## Dependencies (warning)
|
||||
- `pytest==8.3.3` — GHSA-6w46-j5rx-g56g severity=UNKNOWN fix=9.0.3
|
||||
- `starlette==0.38.6` — PYSEC-2026-161 severity=UNKNOWN fix=1.0.1
|
||||
- `starlette==0.38.6` — GHSA-f96h-pmfr-66vw severity=UNKNOWN fix=0.40.0
|
||||
- `starlette==0.38.6` — GHSA-2c2j-9gv5-cj73 severity=UNKNOWN fix=0.47.2
|
||||
@@ -42,6 +42,15 @@ def _clean_settings(monkeypatch):
|
||||
monkeypatch.setattr(settings, "agent_effort_default", "high")
|
||||
for a, e in CANON_EFFORT.items():
|
||||
monkeypatch.setattr(settings, f"agent_effort_{a}", e)
|
||||
# Hermeticity (mirrors test_resolve_agent_model's baseline): the flag-assembly
|
||||
# tests below also read the MODEL/fallback fields, and the host env (prod .env;
|
||||
# the merge-gate re-test runs under it) may legitimately set
|
||||
# ORCH_AGENT_MODEL_* / ORCH_AGENT_FALLBACK_MODEL. These tests assert
|
||||
# shipped-default behaviour, not the host config -> pin the shipped defaults.
|
||||
monkeypatch.setattr(settings, "agent_model_default", "claude-opus-4-8")
|
||||
for a in CANON_EFFORT:
|
||||
monkeypatch.setattr(settings, f"agent_model_{a}", "")
|
||||
monkeypatch.setattr(settings, "agent_fallback_model", "")
|
||||
monkeypatch.setattr(P.settings, "projects_json", "")
|
||||
reload_projects()
|
||||
yield
|
||||
|
||||
@@ -41,6 +41,9 @@ def _clean_settings(monkeypatch):
|
||||
monkeypatch.setattr(settings, "agent_model_default", "claude-opus-4-8")
|
||||
for a in ("analyst", "architect", "developer", "reviewer", "tester", "deployer"):
|
||||
monkeypatch.setattr(settings, f"agent_model_{a}", "")
|
||||
# Hermeticity: the host env (prod .env; merge-gate re-test runs under it)
|
||||
# may legitimately set ORCH_AGENT_FALLBACK_MODEL -> reset to shipped default.
|
||||
monkeypatch.setattr(settings, "agent_fallback_model", "")
|
||||
# default registry (no per-project overrides)
|
||||
monkeypatch.setattr(P.settings, "projects_json", "")
|
||||
reload_projects()
|
||||
@@ -233,8 +236,12 @@ def test_valid_per_project_override_unchanged(monkeypatch):
|
||||
|
||||
# ---- TC-09 / TC-11: G4 fallback is OFF (ADR-001 decision 3) ------------------
|
||||
def test_fallback_model_disabled_by_default():
|
||||
# G4 not enabled: agent_fallback_model stays "" -> no --fallback-model flag.
|
||||
assert settings.agent_fallback_model == ""
|
||||
# G4 not enabled (ORCH-074 ADR-001, Решение 3): the SHIPPED default of
|
||||
# agent_fallback_model is "" -> no --fallback-model flag out of the box.
|
||||
# Assert the CLASS field default, not the runtime singleton: the host env
|
||||
# may legitimately enable the fallback via ORCH_AGENT_FALLBACK_MODEL, and
|
||||
# this test must stay hermetic (the merge-gate re-test runs under that env).
|
||||
assert type(settings).model_fields["agent_fallback_model"].default == ""
|
||||
# never-break: the SAME predicate guards the inline fallback read in _spawn,
|
||||
# so a typo there would be rejected exactly like a model name.
|
||||
assert is_valid_model("claude-bad typo") is False
|
||||
|
||||
Reference in New Issue
Block a user