feat(replication): расхардкод хоста + секреты нового хоста + smoke-runbook
All checks were successful
CI / test (push) Successful in 57s
CI / test (pull_request) Successful in 55s

Фундамент тиража 10-common (эпик ORCH-10): платформа разворачивается на
новой инфре без правки кода — только env/конфиг. Каждый дефолт = боевому
значению (пустой .env => поведение 1:1, kill-switch-природа, NFR-2);
STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict/схема БД не тронуты.

- config: agent_home_dir / agent_git_name / git_email_domain / staging_port
  (ADR-001 D2/D4); код-блокеры A1-A4 закрыты: plane_sync ссылки из
  gitea_public_url+gitea_owner, launcher - единый agent_git_env() (x2 места),
  self_deploy/post_deploy - HOME+домен из Settings (имена системных акторов -
  платформенные литералы)
- image_freshness: staging_port из конфига + fail-closed guard
  staging_port == прод-порт -> отказ ДО ssh/build (инвариант ORCH-058 AC-9
  стал исполняемым); REPO= передаётся хуку явно обоими инвокерами (D7)
- SELF_HOSTING_REPO - нормативная платформенная константа (D3, пин-тест)
- compose: полная ${VAR:-default}-интерполяция (реестр B, карта D6); группа
  ORCH-040 uid/gid/HOME/маунты двигается согласованно (build.args APP_*);
  group_add "МИНА 1" сохранён x3; оба app-сервиса с явным command:
- Dockerfile: ARG APP_UID/APP_GID/APP_USER/APP_HOME (CMD exec-form 8500
  сознательно не тронут - D5); deploy-hook: REPO="${REPO:-...}" (D1 реестра)
- секреты: stdlib scripts/gen_secrets.py (token_hex(32); печать по умолчанию;
  --write никогда не перезаписывает существующий .env молча, exit=2;
  перезапись только --force); .env.example дополнен до полноты ключей старта
- доки: новый docs/operations/REPLICATION.md (карта env, чек-лист секретов,
  smoke-процедура с PASS/FAIL, границы 10-common/Lite/Bundled), INFRA.md,
  README, CLAUDE.md, CHANGELOG
- анти-регресс: tests/test_no_host_hardcodes.py (tokenize-сканер запрещённых
  литералов, config-модули - структурное исключение, allowlist пуст,
  негативная самопроверка) + test_host_config_keys / test_infra_parametrization
  / test_secrets_gen / test_replication_smoke; согласованные структурные
  правки test_orch040_compose (судит резолв дефолтов) и
  test_deploy_hook_rollback_sim (REPO через env-override = контракт D7)

Полный регресс: 1764 passed.

Refs: ORCH-101

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 20:50:43 +03:00
parent 26bdd783d6
commit f1635ddb39
26 changed files with 1583 additions and 86 deletions

View File

@@ -5,14 +5,68 @@ ORCH_PLANE_API_URL=http://plane-app-api-1:8000
ORCH_PLANE_WEB_URL=
ORCH_PLANE_API_TOKEN=
ORCH_PLANE_WORKSPACE_SLUG=
# Webhook secrets are GENERATED PER HOST: python3 scripts/gen_secrets.py
# (ORCH-101 / AC-5: production secrets are NEVER copied to a new host).
ORCH_PLANE_WEBHOOK_SECRET=
ORCH_GITEA_URL=http://localhost:3000
# External (browser) URL of Gitea for clickable Branch/PR links in comments;
# empty -> falls back to ORCH_GITEA_URL.
ORCH_GITEA_PUBLIC_URL=
ORCH_GITEA_TOKEN=
ORCH_GITEA_WEBHOOK_SECRET=
ORCH_GITEA_OWNER=admin
# Per-agent Plane bot tokens (optional): when set, comments are posted under
# the matching bot so Plane shows the real author; empty -> ORCH_PLANE_API_TOKEN.
ORCH_PLANE_BOT_ANALYST=
ORCH_PLANE_BOT_ARCHITECT=
ORCH_PLANE_BOT_DEVELOPER=
ORCH_PLANE_BOT_REVIEWER=
ORCH_PLANE_BOT_TESTER=
ORCH_PLANE_BOT_DEPLOYER=
ORCH_PLANE_BOT_STREAM=
# Telegram live-tracker / alerts (empty -> notifications are logged, not sent).
ORCH_TELEGRAM_BOT_TOKEN=
ORCH_TELEGRAM_CHAT_ID=
# ORCH-6: project registry — JSON array of {plane_project_id, repo,
# work_item_prefix, name}. Empty -> built-in default registry (src/projects.py)
# whose Plane UUIDs belong to the ORIGINAL host. On a NEW host this key is
# MANDATORY (ORCH-101 replication checklist, docs/operations/REPLICATION.md).
ORCH_PROJECTS_JSON=
ORCH_CLAUDE_BIN=/usr/bin/claude
ORCH_REPOS_DIR=/home/slin/repos
ORCH_DB_PATH=/app/data/orchestrator.db
# ── ORCH-101: host parametrization (replication foundation, ADR-001 D1D7) ───
# Every host-specific value lives HERE (defaults = the current production host;
# an empty/absent value keeps behaviour 1:1). The same names are read by BOTH
# pydantic Settings (env_file) and docker-compose ${VAR:-default} interpolation
# (compose reads .env/shell, NOT a service's env_file). Full variable map and
# the new-host procedure: docs/operations/REPLICATION.md.
# AGENT_HOME_DIR -> HOME of all actor subprocesses (agents/finalizer/monitor)
# AND the target of the .claude/.claude.json/.ssh mounts AND
# Dockerfile ARG APP_HOME (ORCH-040 group moves together).
# AGENT_GIT_NAME / GIT_EMAIL_DOMAIN -> git identity of agent commits; system
# actors keep platform names deploy-finalizer/post-deploy-
# monitor under the same domain.
# STAGING_PORT -> staging instance port; image_freshness fail-closes when it
# equals the prod port (ORCH-058 AC-9 guard).
# HOST_* -> host-side sources of the bind mounts (repos, ~/.claude,
# ~/.claude.json, ssh keydir, claude-code dist, node binary).
# RUN_UID/RUN_GID/DOCKER_GID -> container uid:gid + host docker group for
# docker.sock access (group_add «МИНА 1», ORCH-040).
ORCH_AGENT_HOME_DIR=/home/slin
ORCH_AGENT_GIT_NAME=claude-bot
ORCH_GIT_EMAIL_DOMAIN=mva154.local
ORCH_STAGING_PORT=8501
ORCH_HOST_REPOS_DIR=/home/slin/repos
ORCH_HOST_CLAUDE_DIR=/home/slin/.claude
ORCH_HOST_CLAUDE_JSON=/home/slin/.claude.json
ORCH_HOST_SSH_DIR=/home/slin/.orchestrator-ssh
ORCH_HOST_CLAUDE_CODE_DIR=/usr/lib/node_modules/@anthropic-ai/claude-code
ORCH_HOST_NODE_BIN=/usr/bin/node
ORCH_RUN_UID=1000
ORCH_RUN_GID=1000
ORCH_DOCKER_GID=999
# ── Agent model / effort / fallback (ORCH-41, validation ORCH-74) ─────────────
# Per-agent LLM model + reasoning effort, resolved by launcher.resolve_agent_*.
# Resolution priority (per agent): project-override (projects_json agent_models/