Files
claude-bot 69aa6eacde
All checks were successful
CI / test (push) Successful in 1m9s
analyst(ET): auto-commit from analyst run_id=602
2026-06-10 20:02:14 +03:00

24 KiB
Raw Permalink Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-101 analysis analyst ready-for-review 2026-06-10 claude-opus-4-8

02 — ТЗ (TRZ): ORCH-101 — ORCH-10-common: расхардкод + секреты + smoke (фундамент тиража)

Work Item: ORCH-101 · Repo: orchestrator · Стадия: analysis

ТЗ описывает что должно измениться и где (модули/контракты/артефакты), выведено из BRD и фактического кода (аудит выполнен по репо на ветке задачи). Как (точные имена новых конфиг-ключей, механика генератора секретов, форма smoke-скрипта) — решает архитектор в 06-adr/.


1. Сводка изменения

Три слоя одного фундамента тиража (эпик ORCH-10, оба типа A/B):

  1. Расхардкод: все хост-специфичные значения, которые сегодня обходят конфиг (зашиты в код src/**, в docker-compose.yml, Dockerfile, scripts/orchestrator-deploy-hook.sh), переводятся на чтение из Settings (env ORCH_*) / compose-интерполяцию ${VAR:-default} / ARG / env-override — с дефолтами, равными текущим боевым значениям (zero-regression). Нормативный реестр находок — §3.1 (результат аудита всего репо).
  2. Секреты: механизм выпуска нового комплекта секретов на новом хосте (генерация webhook-секретов + чек-лист внешних токенов) + полнота .env.example.
  3. Smoke: документированная воспроизводимая процедура «тестовый проект + задача → конвейер доехал» с PASS/FAIL-критериями (+ анти-регресс grep-тест против возврата хардкодов).

Конвейер (STAGE_TRANSITIONS/QG_CHECKS/check_*/verdict-ключи/схема БД) — не меняется.


2. Задействованные модули / пути

Путь Действие
src/config.py изменить: новые Settings-ключи (HOME агент-процессов, git-идентичность акторов, внешний Gitea-URL уже есть — gitea_public_url; прочее по §3.1), дефолты = текущим значениям
src/plane_sync.py изменить: notify_stage_change — убрать литерал gitea_base = "http://git.mva154.duckdns.org" и owner admin из путей ссылок → settings.gitea_public_url (fallback gitea_url) + settings.gitea_owner
src/agents/launcher.py изменить: env агент-процесса (×2 места) — HOME и git author/committer (claude-bot@mva154.local) из конфига
src/self_deploy.py изменить: env detached-процесса — HOME, deploy-finalizer@mva154.local из конфига
src/post_deploy.py изменить: env монитора — HOME, post-deploy-monitor@mva154.local из конфига
src/image_freshness.py изменить ИЛИ обосновать: _STAGING_PORT = 8501 (см. §3.4 А-1, инвариант ORCH-058)
src/qg/checks.py изменить ИЛИ обосновать: SELF_HOSTING_REPO = "orchestrator" (см. §3.4 А-2)
src/fs_normalize.py изменить: fallback-подсказку sudo chown -R 1000:1000 /repos/_wt строить из settings (uid/worktrees_dir)
src/projects.py не менять логику; _DEFAULT_PROJECTS остаётся документированным fallback (UUID'ы чужого Plane безвредны); фиксация в чек-листе «на новом хосте обязателен ORCH_PROJECTS_JSON»
watchdog/config.py проверить/синхронизировать: уже env-driven (WATCHDOG_*); дефолт http://127.0.0.1:8500/metrics согласовать с параметризацией прод-порта
docker-compose.yml изменить: интерполяция ${VAR:-default} для всех хост-значений (§3.1 B)
Dockerfile изменить: ARG для uid/gid/username/home; CMD-порт — по решению архитектора (§3.4 А-3)
scripts/orchestrator-deploy-hook.sh изменить: REPO=/home/slin/repos/orchestrator → env-override REPO="${REPO:-…}"
scripts/ (новый) создать: генератор секретов и/или smoke-обвязка — форма за архитектором (§3.2, §3.3)
.env.example изменить: новые ключи, секции секретов с плейсхолдерами, чек-лист «что заполнить»
.env.staging.example изменить: согласовать с новыми ключами (при необходимости)
tests/test_no_host_hardcodes.py (новый) создать: структурный анти-регресс grep-тест (FR-8)
tests/ (новые) создать: тесты параметризации/секретов/smoke по 04-test-plan.yaml
docs/operations/ создать deployment-раздел тиража (предложение: REPLICATION.md; имя финализирует архитектор) + обновить карту env в INFRA.md
CHANGELOG.md, CLAUDE.md, README.md изменить: запись изменения; паспорт/обзорные доки — по фактическому объёму (правило агентов №2/№6)

3. Функциональные требования

FR-1 — Нормативный реестр хардкодов (результат аудита) и их устранение

Привязка: BR-1, BR-2. Аудит выполнен по всему репо (grep по классам: IP/hostname, пути, порты, gid/uid, URL, идентичности). Реестр ниже нормативен: developer закрывает каждую строку; расхождение «нашёл ещё» — дополняет реестр в PR, а не игнорирует.

3.1. Реестр

A. src/** / watchdog/** — код, обходящий конфиг (блокеры AC-1):

# Файл:строка Хардкод Требование
A1 src/plane_sync.py:1064 gitea_base = "http://git.mva154.duckdns.org" в notify_stage_change + owner admin в построении ссылок Branch/PR читать settings.gitea_public_url (fallback settings.gitea_url; loopback-семантика — как в notifications._build_plane_issue_link) + settings.gitea_owner; никаких новых ключей не требуется
A2 src/agents/launcher.py:594, 825 "HOME": "/home/slin"; GIT_AUTHOR/COMMITTER_EMAIL: claude-bot@mva154.local (×2 места) HOME и git-идентичность из конфига (новые ключи, дефолты = текущим значениям)
A3 src/self_deploy.py:332336 "HOME": "/home/slin"; deploy-finalizer@mva154.local то же (единый источник с A2; имя актора может остаться per-actor)
A4 src/post_deploy.py:575579 "HOME": "/home/slin"; post-deploy-monitor@mva154.local то же
A5 src/image_freshness.py:61 _STAGING_PORT = 8501 (модульная константа; намеренный анти-prod-инвариант ORCH-058 AC-9) конфигуризовать с дефолтом 8501 с сохранением инварианта (guard «staging-порт ≠ прод-порт») ИЛИ оставить константой с явным обоснованием в ADR — решение архитектора (§3.4 А-1)
A6 src/qg/checks.py:517 SELF_HOSTING_REPO = "orchestrator" (узел: на него опираются все *_repos-leaf'ы «empty CSV → self-hosting only») конфиг-ключ с дефолтом orchestrator ИЛИ нормативная платформенная константа («репо платформы в тираже обязан называться orchestrator», фиксируется в deployment-доке) — решение архитектора (§3.4 А-2)
A7 src/fs_normalize.py:539 fallback-строка подсказки sudo chown -R 1000:1000 /repos/_wt строить из settings.fs_target_uid / settings.worktrees_dir (соседние строки 533535 уже так делают)
A8 src/disk_watchdog.py:95 fallback ["/repos", "/app/data"] — зеркало дефолта settings.disk_monitor_paths допустимое config-backed зеркало; не блокер. Требование: значения остаются синхронными с конфиг-дефолтом (одна точка истины желательна — на усмотрение архитектора)
A9 src/projects.py:4255 _DEFAULT_PROJECTS: UUID'ы Plane текущего хоста НЕ блокер (документированный fallback «out of the box», чужие UUID не сматчатся). Требование: чек-лист тиража обязывает задать ORCH_PROJECTS_JSON
A10 watchdog/config.py:100,144 дефолт http://127.0.0.1:8500/metrics уже env-driven (WATCHDOG_METRICS_URL); дефолт легитимен; согласовать с А-3 (прод-порт), если тот станет параметром

Паттерн getattr(settings, "x", <литерал>) (A7/A8 и др.) — config-backed fallback, не хардкод-блокер: значение управляется конфигом. Блокер — только код, который конфиг обходит.

B. docker-compose.yml — хост-специфика (блокеры AC-6):

# Место Хардкод
B1 volumes ×3 сервисов /home/slin/repos:/repos(+:ro), /home/slin/.claude, /home/slin/.claude.json, /home/slin/.orchestrator-ssh:/home/slin/.ssh
B2 volumes ×2 сервисов /usr/lib/node_modules/@anthropic-ai/claude-code:/opt/claude-code:ro, /usr/bin/node:/usr/bin/node:ro
B3 group_add ×3 "999" (gid docker-группы хоста)
B4 user: ×2 "1000:1000" (uid:gid хоста)
B5 environment ORCH_HOST_REPOS_DIR=/home/slin/repos ×2, ORCH_DEPLOY_HOST_REPO_PATH=/home/slin/repos/orchestrator, DEPLOY_SSH_USER=slin ×2, ORCH_DEPLOY_SSH_USER=slin, DEPLOY_HOOK_SCRIPT=/home/slin/bin/enduro-deploy-hook.sh ×2 (legacy enduro)
B6 orchestrator-staging.command порт 8501

Требование: compose-интерполяция ${VAR:-default} (источник — .env, штатный механизм Compose); docker compose config без заданных переменных резолвится в текущие значения 1:1. Целевая семантика «HOME внутри контейнера согласован с маунтами .claude/.ssh и useradd Dockerfile» (инвариант ORCH-040) сохраняется как согласованная группа переменных.

C. Dockerfile:

# Место Хардкод
C1 useradd -u 1000 -g 1000 -m -d /home/slin -s /bin/bash slin uid/gid/home/username
C2 CMD … --port 8500 прод-порт

Требование: C1 → ARG с дефолтами (1000/1000//home/slin/slin). C2 — по решению архитектора (§3.4 А-3): порт уже переопределяем через compose command:; допустимо оставить CMD-дефолт 8500.

D. scripts/:

# Место Хардкод
D1 orchestrator-deploy-hook.sh:38 REPO=/home/slin/repos/orchestrator (единственный непараметризованный путь скрипта; остальное уже env-override)

Требование: REPO="${REPO:-/home/slin/repos/orchestrator}" (паттерн остальных переменных скрипта). scripts/staging_check.py — уже env-driven (ORCH_PLANE_API_URL/ORCH_GITEA_URL/--base-url), изменений не требует.

E. Уже параметризовано (НЕ трогать, фиксация аудита): дефолты src/config.py (plane_api_url:8091, gitea_url:3000, repos_dir, host_repos_dir, worktrees_dir, runs_dir, db_path, deploy_ssh_user, deploy_host_repo_path, deploy_prod_target_port:8500, post_deploy_base_url, disk_monitor_paths, claude_bin) — легитимные значения по умолчанию, управляемые env; менять их значения запрещено (BR-5).

FR-2 — Чтение хост-значений из конфига в src/**

Привязка: BR-1. Каждый блокер класса A (A1A4, A7; A5/A6 — по исходу §3.4) читает значение через settings (существующие либо новые ключи Settings с env ORCH_*). Требования к новым ключам:

  • дефолт = текущее боевое значение (BR-5): /home/slin, claude-bot, *@mva154.local-адреса;
  • описательный комментарий в config.py по образцу существующих блоков (назначение + env-имя);
  • ключи отражены в .env.example и карте env INFRA.md;
  • точные имена ключей и группировка (например, единый agent_home_dir + per-actor идентичности или общий email-домен) — решение архитектора; ТЗ фиксирует контракт: ни один из пяти акторов (агенты CLI ×2 места, self-deploy finalizer, post-deploy monitor, fs-подсказка) не содержит литералов /home/slin / mva154 после изменения.

FR-3 — Параметризация инфра-файлов

Привязка: BR-2. Реестр B/C/D закрыт; механика — compose-интерполяция / ARG / shell-default. Инварианты: ORCH-040 (uid 1000 + group_add docker-gid + HOME-согласованность маунтов — группа переменных меняется согласованно, «МИНА 1» group_add не удаляется), ORCH-058 (staging-сервис никогда не резолвится в прод-таргет). Поведение docker compose config при пустом окружении — эквивалент текущего файла (проверяется тестом TC-06).

FR-4 — Механизм секретов нового хоста

Привязка: BR-3. Состав:

  1. Инвентаризация (фиксация аудита): генерируемые локально — ORCH_PLANE_WEBHOOK_SECRET, ORCH_GITEA_WEBHOOK_SECRET; выпускаемые внешними системами — ORCH_PLANE_API_TOKEN, ORCH_PLANE_BOT_* (7 шт., опциональны: fallback на ORCH_PLANE_API_TOKEN), ORCH_GITEA_TOKEN, ORCH_TELEGRAM_BOT_TOKEN (+ несекретный ORCH_TELEGRAM_CHAT_ID).
  2. Генерация: webhook-секреты создаются криптослучайно (стандарт: secrets-модуль Python / эквивалент, длина ≥ 32 байт энтропии) на целевом хосте; форма (отдельный scripts/gen_secrets.py, режим onboarding-CLI или документированная команда) — решение архитектора. Требования к поведению: повторный запуск даёт другие значения; существующий .env никогда не перезаписывается молча (NFR-3); вывод согласован с ключами .env.example.
  3. Чек-лист в deployment-доке: для каждого секрета — где выпустить (Plane UI/API, Gitea UI, BotFather), куда вписать, как проверить. Явное правило: «боевые секреты текущего хоста не копируются».
  4. Полнота .env.example: каждый обязательный для старта ключ присутствует (с плейсхолдером или дефолтом), включая новые ключи FR-2/FR-3; секретные значения — только плейсхолдеры.

FR-5 — Smoke-верификация тиража

Привязка: BR-4. Документированная процедура (deployment-раздел) + опциональная скрипт-обвязка (форма — архитектор; кандидаты-кирпичи уже в репо: scripts/onboard_project.py plan/apply/verify, scripts/staging_check.py, GET /health / /queue / /metrics):

  1. Шаги: поднять инстанс (env+секреты заполнены) → GET /health ок → завести тестовый проект (onboarding-CLI verify или sandbox-проект) → создать тестовую задачу → убедиться, что конвейер «доехал» (задача продвигается по стадиям; минимальный обязательный сигнал — стадия analysis отработала и артефакты 0104 созданы; полный прогон до done — расширенный режим).
  2. Критерии: каждый шаг имеет явный PASS/FAIL; итог процедуры — однозначный вердикт.
  3. Воспроизводимость: процедура прогоняется на текущей инфре (staging-песочница 8501 + sandbox-проект) без нового железа — это и приёмочная проверка AC-3.
  4. Stateless: процедура нигде не предполагает перенос данных/БД с боевого хоста.

FR-6 — Анти-регресс структурный тест

Привязка: BR-6. Новый tests/test_no_host_hardcodes.py (образец — tests/test_agent_prompts_canon.py):

  • сканирует src/**/*.py и watchdog/**/*.py на запрещённые литералы: 82.22.50.71, /home/slin, mva154, duckdns (список централизован в тесте; расширяем);
  • судит код: строки-комментарии и докстринги исключаются (NFR-5; механика исключения — прагматичная построчная/AST — за developer);
  • tests/**, docs/**, .env.example — вне зоны сканирования;
  • допускает точечный явный allowlist (файл:литерал) с комментарием-обоснованием — пустой на момент сдачи задачи (все блокеры A закрыты).

FR-7 — Документация

Привязка: BR-7. Deployment-раздел в docs/operations/ (предложение: REPLICATION.md): карта новых env-переменных (включая compose-интерполяцию), процедура секретов (FR-4), smoke-процедура (FR-5), границы «10-common vs Lite vs Bundled». Обновления: INFRA.md (карта env), CHANGELOG.md; CLAUDE.md/README.md — по фактическому объёму изменений (правило №2/№6).


3.4. Вопросы архитектору (решаются в 06-adr/, не в ТЗ)

  • А-1: _STAGING_PORT = 8501 (image_freshness) — конфиг-ключ с guard'ом «≠ прод-порт» или нормативная константа? Инвариант ORCH-058 AC-9 («никогда не прод 8500») обязан сохраниться.
  • А-2: SELF_HOSTING_REPO = "orchestrator" — конфиг-ключ или платформенная конвенция тиража (репо платформы всегда orchestrator)? На него завязаны все *_repos-leaf'ы.
  • А-3: CMD-порт Dockerfile (8500) — ARG или остаётся (порт уже переопределяем compose command:)?
  • А-4: форма генератора секретов (отдельный скрипт / режим onboard_project.py / документированная команда) и форма smoke (чистый runbook / runbook + скрипт).
  • А-5: группировка новых конфиг-ключей FR-2 (единый HOME-ключ + per-actor email'ы vs общий email-домен).

4. Изменения API

Нет. Существующие эндпоинты (/health, /queue, /metrics) переиспользуются smoke-процедурой read-only; новые эндпоинты не вводятся.

5. Изменения схемы БД

Нет.

6. Требования к новым/изменённым QG checks

Нет. QG_CHECKS/check_*/machine-verdict ключи не меняются. Новый структурный тест (FR-6) — обычный pytest: попадает в существующие гейты check_ci_green/check_tests_passed/merge-gate re-test автоматически, без регистрации нового QG.

7. Совместимость / регресс

  • Zero-regression (BR-5): все новые параметры — с дефолтами, равными сегодняшним боевым значениям; пустой/неизменённый .env ⇒ байт-в-байт текущее поведение; docker compose config без переменных ⇒ эквивалент текущего файла. Полный pytest tests/ -q зелёный.
  • Обратимость (NFR-2): откат = не задавать новые переменные. Функциональный kill-switch не требуется (дефолты и есть прежнее поведение); вводить новый флаг ради флага запрещено.
  • Область раската: изменения инертны для enduro-trails (значения те же); compose/Dockerfile вступают в силу только при следующем штатном деплое через конвейер (staging 8501 → ручной Confirm Deploy) — прод-контейнер в рамках задачи не рестартуется (NFR-1).
  • Инварианты соседних маркеров (правило №9): ORCH-040 (uid/gid/HOME/«МИНА 1»), ORCH-058 (freshness → только staging), ORCH-036/059 (self-deploy фазы, Confirm Deploy), INV-4 (мерж только через PR-merge API) — сохраняются; соответствующие ADR прочитаны при правке помеченных блоков.