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>
13 KiB
13 KiB
Changelog
Формат: Keep a Changelog. Записи — на смысловой PR/задачу.
[Unreleased]
Added
- Read-only эндпоинт
GET /projects+ staging-чек B6 читает реестр по HTTP (ORCH-048): B6 вscripts/staging_check.py(«Registry: sandbox present, prod ET/ORCH absent») раньше строил реестр локальным импортомsrc.projects(host-path хакsys.path.insert(0,"/repos/orchestrator")+importlib.reload), оцениваяORCH_PROJECTS_JSONиз process-env того процесса, что запускает скрипт. На каноническом host-запуске деплоера (python3 scripts/staging_check.py --base-url http://localhost:8501) переменная не задана → встроенный_DEFAULT_PROJECTS(ET+ORCH) → ложный FAIL при фактически исправной изоляции; B6 проверял реестр НЕ того окружения, что реально обслуживает webhooks. Теперь добавлен read-only additive-эндпоинтGET /projects(src/main.py), отдающийknown_plane_project_ids+ список{plane_project_id, repo, work_item_prefix, name}(без секретов) именно того процесса, что слушает вебхуки; источник — существующаяsrc.projects(новой логики реестра нет). B6 переписан наGET {base}/projectsтем же stdlib-хелпером_get, что и A/B4/B5/C; host-path хак иimportlib.reloadудалены (инвариантен к способу запуска: хост /docker exec). Логика вердикта вынесена в чистую_evaluate_b6(known) -> (passed, detail)(контракт неизменен: PASS ⟺SANDBOX ∈ known ∧ PROD_ET ∉ known ∧ PROD_ORCH ∉ known), форматResults.addсохранён; недоступность эндпоинта (не-200 / нет ключа / битый ответ / сетевой сбой) → детерминированный FAIL (TR-4), без ложного PASS и необработанного исключения.src/projects.pyи.env*не тронуты; прод-поведение существующих роутов неизменно. ADRdocs/work-items/ORCH-048/06-adr/ADR-001-b6-registry-via-http-endpoint.md. Тесты:tests/test_staging_check_b6.py,tests/test_projects_endpoint.py. - Дословный текст findings reviewer/tester встраивается в
task_descзаворота (ORCH-046): при откате наdevelopmentстрокаtask_desc(попадает в.task-dev.mddeveloper-агента) теперь несёт суть претензий, а не только ссылку на файл — устраняет «испорченный телефон», из-за которого агент шёл «читать файл», терял ключевые P0/P1 / причину FAIL и заворачивался снова, выжигаяMAX_DEVELOPER_RETRIESи токены. Новый defensive-модульsrc/review_parse.py(контракт «never raise», какsrc/frontmatter.py):extract_review_findings(path)— дословные пункты P0/P1 из секции## Findingsфайла12-review.md;extract_test_failures(path)— релевантный фрагмент тела13-test-report.md(приоритет## Вывод pytest→ FAIL-строки## Результаты→## Итог). Обе функции усекают результат доMAX_FINDINGS_CHARS/MAX_FAILURES_CHARS(≈2000) с маркером…(truncated). Две rollback-веткиsrc/stage_engine.py(reviewer REQUEST_CHANGES, testercheck_tests_passedFAIL) встраивают извлечённый текст и сохраняют ссылку на полный файл («Полный контекст»); при пустом/битом артефакте — graceful-фоллбэк на прежнюю ссылку-строку (никаких исключений вadvance_stage). Tester-ветка дополнительно всегда включаетreasonгейта. Последовательность отката,_developer_retry_count, поляAdvanceResultи реестрQG_CHECKSне менялись. ADRdocs/work-items/ORCH-046/06-adr/ADR-001-embed-findings-in-task-desc.md. Тесты:tests/test_review_parse.py,tests/test_stage_engine.py::TestRollbackTaskDescEmbedding. - Поллинг с ретраем в quality-gate
check_ci_green(ORCH-045): гейт CI превращён из single-shot в polling, чтобы устранить race condition — раньше один опрос combined commit-status сразу после пуша developer-а ловил транзиентныйpending(типично 1-3с, реальный кейс ORCH-017: опрос 17:58:54 → pending, CI дозеленел 17:58:55) и задача застревала насмерть без повторного опроса. Теперь:success→ пропуск сразу;failure/error→ провал сразу (терминально, ретрай бессмыслен);pending/unknown →time.sleepи повторный опрос доci_poll_max_attemptsраз; истечение попыток → явный(False, "CI still pending after <T>s")(тупик больше не молчаливый); 404 → как раньше; транзиентнаяhttpx.HTTPErrorна попытке логируется и ретраится в рамках бюджета. Параметры — новые настройкиORCH_CI_POLL_MAX_ATTEMPTS(12) иORCH_CI_POLL_INTERVAL_S(10) вsrc/config.py(~2 мин ожидания pending). Сигнатураcheck_ci_green(repo, branch)и реестрQG_CHECKSне менялись;check_tests_passedне затронут. ADRdocs/architecture/adr/adr-0004-ci-poll-retry.md. Тесты:tests/test_qg.py::TestCheckCIGreen. - Прямые ссылки на BRD и Plane-таску в Telegram-уведомлении об апруве (ORCH-017): пингующее сообщение
notify_approve_requestedтеперь встраивает две HTML-<a>-ссылки — наdocs/work-items/<WI>/01-brd.md(Gitea branch-view:gitea_public_url→gitea_url) и на issue в Plane ({web_base}/{workspace}/projects/{project_id}/issues/{plane_issue_id}/). Новая настройкаORCH_PLANE_WEB_URL(внешний браузерный web-URL Plane; фолбэк наplane_api_url). Loopback-guard: если итоговый Plane web-base указывает на localhost/127.0.0.1/0.0.0.0/::1 или пуст — Plane-ссылка опускается (не выпускаем битый localhost-URL). Graceful degradation: каждая ссылка строится независимо и опускается при нехватке данных, сообщение и призыв «Переведите задачу в статус Approved …» сохраняются всегда; ровно одно пингующее сообщение, разделяемаяsend_telegramне тронута. Динамические подписи экранируютсяhtml.escape,parse_mode=HTMLсохранён. ADRdocs/work-items/ORCH-017/06-adr/ADR-001-telegram-approve-links.md. Тесты:test_notify_approve_links.py,test_analysis_approve_flow_links.py. - Конфигурируемые модель LLM и режим работы (
--effort) агентов (ORCH-41): модель/effort каждого агента вынесены из хардкодаlauncher.pyв конфиг — глобально per-agent (ORCH_AGENT_MODEL_<AGENT>/ORCH_AGENT_EFFORT_<AGENT>, дефолтыORCH_AGENT_MODEL_DEFAULT=claude-opus-4-8,ORCH_AGENT_EFFORT_DEFAULT=high) и per-project (agent_models/agent_effortsвORCH_PROJECTS_JSON). Резолверыresolve_agent_model/resolve_agent_effort(приоритет project > per-agent env > default > пусто), валидация effort{low,medium,high,xhigh,max}, опц.ORCH_AGENT_FALLBACK_MODEL(--fallback-model). Хардкод"model":"opus"(architect/reviewer) удалён. Тесты:test_resolve_agent_model.py,test_resolve_agent_effort.py. - Единый status-коммент агентов в Plane (ORCH-016):
usage.build_status_comment(...)— один хелпер для ВСЕХ ролей (analyst..deployer). HTML-формат: header{icon} {Role} — {описание}, опциональная строкаVerdict/Status: …из YAML-frontmatter артефакта, строкаДлительность: 4m 12s(явныйduration_sот launcher, fallback изagent_runsдля аналитика),<b>Документы:</b><ul><li><a>…</a></li></ul>, тех-хвост<sub>tokens · cost</sub>. Утилитки:usage.fmt_duration,usage.get_agent_duration, новый модульsrc/frontmatter.py(defensive YAML reader). ADRdocs/work-items/ORCH-016/06-adr/ADR-001-unified-status-comment.md. - Документация по канону (ORCH-9):
CLAUDE.md(паспорт проекта), структураdocs/(architecture/+adr/,operations/,work-items/,history/),docs/operations/INFRA.md(RUNBOOK с инфра-изоляцией и self-hosting рисками). - ADR: adr-0001 (multi-repo registry), adr-0002 (job queue), adr-0003 (условный staging-гейт).
- Стадия
deploy-staging(ORCH-35): промежуточный гейт междуtestingиdeploy. QGcheck_staging_status(условный, только для self-hosting repo). PR #31. - Деплой-хук (ORCH-34):
scripts/orchestrator-deploy-hook.shс health-check и авто-rollback. PR #30. - Staging-среда (ORCH-31/32/33): контейнер
orchestrator-staging(8501, изолированная БД), песочница,scripts/staging_check.py. PR #28/#29. - Очередь задач (ORCH-1): таблица
jobs,queue_worker.py, atomic claim, max_concurrency, ретраи, restart-safe, эндпоинт/queue. - Реестр проектов (ORCH-6):
src/projects.py, фильтрация вебхуков по проекту.
Changed
- Status-коммент агентов теперь HTML и единообразен (ORCH-016):
src/usage.usage_comment(...)помечен deprecated и стал тонкой обёрткой надbuild_status_comment;src/usage.artifact_links(...)теперь возвращает<li><a>…</a></li>HTML-фрагменты (раньше — markdown[label](url));stage_engine._build_analyst_ready_comment(...)— тонкая обёртка, аналитик идёт через ту же веткуbuild_status_comment(agent="analyst", ...). РеестрQG_CHECKSиSTAGE_TRANSITIONSНЕ изменялись. - Цепочка стадий:
... testing → deploy-staging → deploy → done(была безdeploy-staging).
Fixed
- Testing-гейт
check_tests_passedчитаетresult:наравне сverdict:/status:(ORCH-047): парсер_parse_tests_verdict(src/qg/checks.py) теперь принимает три равноправных машиночитаемых поля frontmatter13-test-report.md—result:(канон промпта тестера.openclaw/agents/tester.md,result: PASS|FAIL), плюс легасиverdict:иstatus:(enduro-trails ET-001..ET-014); достаточно любого одного непустого. Устраняет рассинхрон контракта: тестер честно эмитилresult: PASSбезverdict:/status:, парсер попадал в ветку «нет машинного вердикта» → откатtesting → developmentв петлю до исчерпанияMAX_DEVELOPER_RETRIES(наблюдалось на ORCH-17; ORCH-016 прошёл лишь из-за избыточного дублирования полей). Семантика приоритетов сохранена и распространена на все три поля через объединённую строку: negative-токен в любом поле авторитетен (перебивает positive), наборы токенов заморожены (обратная совместимость). Сигнатура гейта, имя и реестрQG_CHECKSне менялись. ADRdocs/work-items/ORCH-047/06-adr/ADR-001-result-field-in-tests-gate.md. Тесты:tests/test_qg.py::TestCheckTestsPassed. - БАГ-8: провал deploy/deploy-staging → корректный откат на
development. - Изоляция тестов от живого Plane API (PR #27): autouse-фикстура сброса settings.
Историю до введения канона см. в docs/history/ (BUGFIXES_, LESSONS_, INCIDENT_).*