Both compose services (orchestrator, orchestrator-staging) now declare user: "1000:1000" so pipeline artifacts (git worktree, docs/work-items commits) are created as slin:slin on the host — git pull/reset under slin no longer fail with permission errors. docker.sock access preserved via group_add: ["999"]. SSH mount target aligned with the launcher-forced HOME=/home/slin (/root/.ssh -> /home/slin/.ssh). launcher.py and Dockerfile unchanged. INFRA.md and CHANGELOG.md updated; host-prerequisites (P-1..P-4) documented. Refs: ORCH-040 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
19 KiB
19 KiB
Changelog
Формат: Keep a Changelog. Записи — на смысловой PR/задачу.
[Unreleased]
Added
- Режим
bumplive-трекера Telegram (ORCH-042): новыйORCH_TRACKER_MODE(Settings.tracker_mode, дефолтedit) выбирает поведение карточки задачи.edit(как было) — карточка редактируется на месте (editMessageText).bump— на каждом обновлении старое сообщение удаляется и карточка отправляется заново вниз чата (best-effortdelete_telegram(старый_id)→send_telegram(text, disable_notification=True)→set_tracker_message_id(new_id)), чтобы актуальный статус всегда был последним в чате при активной переписке. Инвариант «одна карточка на задачу» сохранён в обоих режимах: за один вызовupdate_task_trackerшлётся ≤1 нового сообщения;set_tracker_message_idвызывается ТОЛЬКО при успешном send (транзиентныйNoneне затирает указатель); результат delete НЕ блокирует отправку новой карточки (delete-fail у сообщения >48ч → всё равно шлём новое). Резолюция режима вnotifications(case-insensitive, trim): всё, что ≠"bump"(включая пустое/мусор) →edit→ нулевая регрессия и оркестратор не падает на любом значении флага. Новый low-level helperdelete_telegram(message_id) -> bool(контракт «never raises», маркеры_DELETE_GONE_MARKERS):ok:trueили «уже нет / нельзя удалить» →True; неизвестныйok:false/5xx/исключение →False; нет кредов →Falseбез HTTP. Сигнатурыsend_telegram/edit_telegram/update_task_trackerи схема БД (tasks.tracker_message_id) не менялись. ADRdocs/work-items/ORCH-042/06-adr/ADR-001-tracker-bump-mode.md. Тесты:tests/test_tracker_bump.py,tests/test_config.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
- Русификация и косметика карточки live-трекера Telegram (ORCH-042, оба режима): метка
Подтверждение BRDвместо «Ревью БРД» (_BRD_LABEL); после прохождения approve-gate строка подтверждения BRD начинается с ✅ вместо ⏸️ (ветка ожидания человека сохраняет ⏸️/⏳); русские display-labels стадий в_TRACKER_STAGES(Анализ / Архитектура / Разработка / Код ревью / Тестирование / Внедрение) — применяются и в «✅ …», и в «🔄 … идёт»; финальная строка готовой задачи📦 Внедреновместоdeployed(_done_link). Меняются только отображаемые строки — ключи стадий и имена агентов не трогаются. Существующие ассертыtests/test_telegram_tracker.pyобновлены под русские метки. - 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
- Контейнер и агенты бегут под uid хоста (1000:1000), не root (ORCH-040): оба сервиса в
docker-compose.yml(orchestrator,orchestrator-staging) получилиuser: "1000:1000"(slin) — устраняет корень проблемы, при которой Claude-CLI агенты, запускаемые черезsubprocess.Popenвнутри root-контейнера, создавали все артефакты конвейера (git worktree/repos/_wt/..., коммиты вdocs/work-items/...) с владельцемroot:rootна хосте, из-за чегоgit pull/git resetпод slin падали сinsufficient permission for adding an objectи каждый деплой требовал ручногоchown. Теперь файлы сразуslin:slin. Доступ к docker.sock сохранён черезgroup_add: ["999"](МИНА 1 — НЕ удалена). SSH-маунт приведён к единому HOME агента: target/root/.ssh→/home/slin/.ssh(/home/slin/.orchestrator-ssh:/home/slin/.ssh:ro), синхронно сHOME=/home/slin, который launcher форсит в env Popen и git_env — устранён скрытый рассинхрон SSH-маунта с форсимым HOME.src/agents/launcher.pyиDockerfileНЕ менялись (numeric uid работает без записи в/etc/passwd;safe.directory '*'уже покрывает git над bind-mount). Требует host-prerequisites Owner (P-1…P-4, вне кода): блокер P-1 —chown -R 1000:1000 /home/slin/.claudeдля доступа uid 1000 к claude creds (иначе preflight заворачивает конвейер); прод-рестарт self — только в окно тишины (общий инстанс с enduro-trails), страховка — staging-гейт (adr-0003). ADRdocs/work-items/ORCH-040/06-adr/ADR-001-run-agents-as-host-uid.md, глобальныйdocs/architecture/adr/adr-0005-container-runs-as-host-uid.md; INFRA.md обновлён (рантайм-uid, volumes/SSH target, host-prerequisites). Тесты:tests/test_orch040_compose.py. - Staging-чек B6 читает реестр из окружения работающего staging-инстанса (ORCH-048): блок B6 «Registry: sandbox present, prod ET/ORCH absent» в
scripts/staging_check.pyдавал ложный FAIL (prod-ET=YES(BAD!),prod-ORCH=YES(BAD!)) при фактически исправной изоляции — единственный чек suite, который не ходил к инстансу по HTTP, а импортировалsrc.projectsлокально через host-path хакsys.path.insert(0, "/repos/orchestrator")+importlib.reload, строя реестр изORCH_PROJECTS_JSONprocess-env запускающего процесса. При фактическом запуске деплоером с хоста переменная не задана → дефолт_DEFAULT_PROJECTS(ET+ORCH) → ложный FAIL → лишний откатdeploy-staging → development. Решение (вариант «в», ADR-001): host-path хак удалён; suite канонически запускается ВНУТРИ контейнераorchestrator-stagingчерезdocker exec … python3 /repos/orchestrator/scripts/staging_check.py(scripts/доступен только через bind-mount,import src.projectsрезолвится черезPYTHONPATH=/appиз кода контейнера, env —.env.staging) → B6 читает реестр именно работающего инстанса, без HTTP-bootstrap и «курицы-яйца». Логика вердикта вынесена в чистую_evaluate_b6(known) -> (passed, detail)(инвариантpassed ⟺ SANDBOX ∈ known ∧ PROD_ET ∉ known ∧ PROD_ORCH ∉ known, формат detail сохранён) +_known_project_ids_from_registry()/_run_b6()с детерминированным FAIL при недоступности источника (не ложный PASS, не необработанное исключение). Синхронно обновлены.openclaw/agents/deployer.md(команда стадии черезdocker exec) иdocs/operations/STAGING_CHECK.md.src/projects.py,.env*и прочие чеки A/B4/B5/C не тронуты; реестрQG_CHECKSиcheck_staging_status(ADR-0003) не менялись. ADRdocs/work-items/ORCH-048/06-adr/ADR-001-b6-registry-via-in-container-run.md. Тесты:tests/test_staging_check_b6.py. - 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_).*