A DB stage=done task with 0 active jobs flapped in Plane between `Awaiting Deploy` and `Monitoring after Deploy` instead of holding `Done` (verified live on ORCH-061, task 47): the three deploy-phase setters were terminal-blind, so any stale/duplicate/unknown caller under the bot token re-stamped an intermediate status over the terminal Done, forever. - New leaf src/deploy_status_guard.py (pure, never-raise, config-gated): decide() -> ALLOW | CONVERGE_DONE | SUPPRESS on the entry of set_issue_awaiting_deploy / set_issue_deploying / set_issue_monitoring. A deploy-phase status is legitimate iff the task is non-terminal OR (done AND post-deploy window active); otherwise done converges to Done idempotently, cancelled is suppressed (FR-2, D1/D2). - D3: move post_deploy.arm_monitor ABOVE the terminal-sync block in advance_stage so window_active is True when the legitimate first Monitoring is set (the task is already DB-done by then); a re-drive after the window closes converges to Done. - D4: run_post_deploy_monitor no-ops without a status PATCH / re-queue when the task became cancelled mid-window (zombie-tick guard, FR-3). - D5: additive `reason` kwarg on the three setters + one structured log line per verdict (work_item/caller/target/db_stage/window_active/verdict); new read-only db.get_task_by_work_item_id; post_deploy.window_active helper. - Flags deploy_status_guard_enabled (kill-switch -> 1:1) / deploy_status_guard_repos (CSV; empty = self-hosting only). STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict keys / DB schema untouched (reads existing tasks.stage). Tests: TC-01..TC-12 across 5 new test modules + config flags; updated the reason-kwarg assertions in test_deploy_terminal_sync / test_deploy_approve. Full regress green (1413). Docs: CHANGELOG, CLAUDE.md, docs/architecture/README.md (status -> реализовано), .env.example. Refs: ORCH-094 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
405 lines
26 KiB
Plaintext
405 lines
26 KiB
Plaintext
ORCH_PLANE_API_URL=http://plane-app-api-1:8000
|
|
# External (browser) web URL of Plane for clickable issue links in notifications
|
|
# (ORCH-017). Falls back to ORCH_PLANE_API_URL; a loopback fallback is treated as
|
|
# "no web URL" and the Plane link is omitted. Example: https://plane.example.org
|
|
ORCH_PLANE_WEB_URL=
|
|
ORCH_PLANE_API_TOKEN=
|
|
ORCH_PLANE_WORKSPACE_SLUG=
|
|
ORCH_PLANE_WEBHOOK_SECRET=
|
|
ORCH_GITEA_URL=http://localhost:3000
|
|
ORCH_GITEA_TOKEN=
|
|
ORCH_GITEA_WEBHOOK_SECRET=
|
|
ORCH_CLAUDE_BIN=/usr/bin/claude
|
|
ORCH_REPOS_DIR=/home/slin/repos
|
|
ORCH_DB_PATH=/app/data/orchestrator.db
|
|
|
|
# ── 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/
|
|
# agent_efforts) > ORCH_AGENT_MODEL_<AGENT> / ORCH_AGENT_EFFORT_<AGENT> >
|
|
# ORCH_AGENT_MODEL_DEFAULT / ORCH_AGENT_EFFORT_DEFAULT > CLI default (no flag).
|
|
# The frontmatter `model:` in .openclaw/agents/*.md is DESCRIPTIVE only and is NOT
|
|
# read — config below is the single source of truth for the model (ORCH-74 G1).
|
|
#
|
|
# ORCH-74 (G2): a resolved MODEL name is validated (^claude-…$ format check) before
|
|
# it reaches --model. A structurally invalid name (typo, gpt-4, empty) is logged and
|
|
# the next valid level is used (in the limit: no --model flag). Forward-compatible:
|
|
# a future claude-* version passes without editing any allowlist. EFFORT is validated
|
|
# against low|medium|high|xhigh|max (ORCH-41); an invalid effort is dropped.
|
|
#
|
|
# All 6 agents resolve to claude-opus-4-8 (model-routing G3 NOT enabled). Leave the
|
|
# per-agent overrides empty to use the default. Do NOT hardcode the model version
|
|
# anywhere except ORCH_AGENT_MODEL_DEFAULT.
|
|
ORCH_AGENT_MODEL_DEFAULT=claude-opus-4-8
|
|
ORCH_AGENT_MODEL_ANALYST=
|
|
ORCH_AGENT_MODEL_ARCHITECT=
|
|
ORCH_AGENT_MODEL_DEVELOPER=
|
|
ORCH_AGENT_MODEL_REVIEWER=
|
|
ORCH_AGENT_MODEL_TESTER=
|
|
ORCH_AGENT_MODEL_DEPLOYER=
|
|
# Effort split (ORCH-081/ORCH-52h): thinking agents (analyst/architect/reviewer)
|
|
# -> high; developer -> xhigh (coding/agentic role, Opus 4.8 canon); mechanical
|
|
# agents (tester/deployer) -> medium. NB: an empty ORCH_AGENT_EFFORT_*= no longer
|
|
# zeroes the effort — the launcher falls back to a per-role floor (= the config.py
|
|
# class-default) so each role still runs at its canonical level (ORCH-081).
|
|
ORCH_AGENT_EFFORT_DEFAULT=high
|
|
ORCH_AGENT_EFFORT_ANALYST=high
|
|
ORCH_AGENT_EFFORT_ARCHITECT=high
|
|
ORCH_AGENT_EFFORT_DEVELOPER=xhigh
|
|
ORCH_AGENT_EFFORT_REVIEWER=high
|
|
ORCH_AGENT_EFFORT_TESTER=medium
|
|
ORCH_AGENT_EFFORT_DEPLOYER=medium
|
|
# Optional --fallback-model used when the primary is overloaded. Empty -> no flag
|
|
# (G4 NOT enabled, ADR-001 ORCH-74: determinism — all agents stay on opus-4-8). A
|
|
# non-empty value is validated by the SAME predicate as the model; a typo is dropped.
|
|
ORCH_AGENT_FALLBACK_MODEL=
|
|
# ORCH-042/ORCH-067: live-tracker mode. bump (DEFAULT since ORCH-067) -> on every
|
|
# update the old card is deleted and a fresh one is sent silently to the BOTTOM of
|
|
# the chat (deleteMessage + sendMessage + repoint), so the current status is always
|
|
# the last message in an active chat. edit -> the task card is edited in place
|
|
# (editMessageText). One card per task in both modes. Any value other than "bump"
|
|
# (incl. empty/garbage) -> edit.
|
|
ORCH_TRACKER_MODE=bump
|
|
# ORCH-067: best-effort live-overlay for the card status line. The offline core
|
|
# (stage -> Plane status, In Review from the brd-clock) always works without network;
|
|
# the overlay only fills in branches indistinguishable offline (Needs Input / Blocked /
|
|
# Rejected / Cancelled / Deploying / Monitoring after Deploy) by reading the LIVE Plane
|
|
# status with a short timeout + per-issue TTL cache. It NEVER blocks the pipeline and
|
|
# NEVER raises.
|
|
# LIVE_STATUS -> kill-switch (false -> offline core only).
|
|
# LIVE_STATUS_TTL_S -> TTL (seconds) of the per-issue live-uuid cache (hot-path guard).
|
|
# LIVE_STATUS_TIMEOUT_S -> timeout (seconds) of a single live-GET on the render path.
|
|
ORCH_TRACKER_LIVE_STATUS=true
|
|
ORCH_TRACKER_LIVE_STATUS_TTL_S=60
|
|
ORCH_TRACKER_LIVE_STATUS_TIMEOUT_S=3
|
|
# ORCH-043: merge-gate (auto-rebase onto current origin/main + re-test + merge-lock)
|
|
# on the deploy-staging -> deploy edge. Deterministic sub-gate (no LLM) that catches
|
|
# the branch up to the CURRENT origin/main, re-tests it, and serialises merges so two
|
|
# green parallel branches can't break main.
|
|
# ENABLED -> global kill-switch (false -> whole gate is a no-op pass).
|
|
# REPOS -> CSV of repos where the gate is REAL; empty -> only the self-hosting
|
|
# repo (orchestrator); other repos -> conditional no-op (mirrors ORCH-35).
|
|
# RETEST_TIMEOUT_S -> wall-clock budget for the post-rebase re-test.
|
|
# RETEST_TARGET -> pytest target for the re-test.
|
|
# LOCK_TIMEOUT_S -> max merge-lease age before a stale lease is reclaimed.
|
|
# DEFER_DELAY_S -> delay before re-running the gate when the lock is busy.
|
|
# DEFER_MAX_ATTEMPTS -> defer retries before escalation (avoids livelock).
|
|
ORCH_MERGE_GATE_ENABLED=true
|
|
ORCH_MERGE_GATE_REPOS=
|
|
ORCH_MERGE_RETEST_TIMEOUT_S=600
|
|
ORCH_MERGE_RETEST_TARGET=tests/
|
|
ORCH_MERGE_LOCK_TIMEOUT_S=300
|
|
ORCH_MERGE_DEFER_DELAY_S=60
|
|
ORCH_MERGE_DEFER_MAX_ATTEMPTS=5
|
|
# ORCH-026 Level A: unconditional pre-merge rebase. With the flag ON (default),
|
|
# check_branch_mergeable ALWAYS rebases the branch onto origin/main under the held
|
|
# merge-lease (not only when behind) — a deterministic structural anti-phantom on
|
|
# the scheduler edge. No-op on an up-to-date branch (rebase keeps HEAD, force-with-
|
|
# lease -> "Everything up-to-date", CI not triggered). Scope = ORCH_MERGE_GATE_REPOS.
|
|
# PREMERGE_REBASE_ALWAYS=false -> strictly pre-ORCH-026 (rebase only when behind).
|
|
ORCH_PREMERGE_REBASE_ALWAYS=true
|
|
# ORCH-026 Level B: declarative task dependencies ("B waits for A"). claim_next_job
|
|
# gates jobs whose depends-on tasks are not yet 'done' (additive job_deps table,
|
|
# NOT EXISTS) WITHOUT occupying a max_concurrency slot. Inert on an empty job_deps.
|
|
# TASK_DEPS_ENABLED=false -> claim query is 1:1 the ORCH-1 query (no gate).
|
|
# TASK_DEPS_SOURCE=db|plane|hybrid -> declaration source; db (default) never calls
|
|
# Plane on the hot path; plane/hybrid ingest Plane `blocked-by` relations and
|
|
# cache them into job_deps (the scheduler then reads only the DB).
|
|
ORCH_TASK_DEPS_ENABLED=true
|
|
ORCH_TASK_DEPS_SOURCE=db
|
|
# ORCH-088 (Stage 1, serial e2e): per-repo serial gate. A NEW task's analyst-job does
|
|
# NOT enter analysis (no branch cut, no analyst) while the same repo has an EARLIER
|
|
# unfinished task (FIFO, tasks.id < the job's task) OR the repo is frozen. The branch
|
|
# cut is DEFERRED from start_pipeline to the analyst-job claim so its base is a fresh
|
|
# origin/main already containing the predecessor (anti-stale-base). Gate lives in
|
|
# claim_next_job (offline hot-path, fail-OPEN on error); freeze (FR-5) is a durable
|
|
# repo_freeze row set on post-deploy DEGRADED, cleared manually via
|
|
# POST /serial-gate/unfreeze?repo=<repo>. Leaf src/serial_gate.py (never-raise).
|
|
# SERIAL_GATE_ENABLED=false -> claim AND start_pipeline are 1:1 as before ORCH-088.
|
|
# SERIAL_GATE_REPOS (CSV) -> scope; EMPTY = ALL repos (not self-hosting-only).
|
|
# SERIAL_GATE_FREEZE_ENABLED=false -> the rollback-freeze layer is off (not set/read).
|
|
ORCH_SERIAL_GATE_ENABLED=true
|
|
ORCH_SERIAL_GATE_REPOS=
|
|
ORCH_SERIAL_GATE_FREEZE_ENABLED=true
|
|
# ORCH-090: STOP-status task cancellation (stop active agent + full progress reset)
|
|
# and the relaunch-hole close. A dedicated Plane "STOP" status (logical key `stop`,
|
|
# fail-closed: absent from _DEFAULT_STATES, so a board without the status -> no-op)
|
|
# routes to a cancel handler that drives the task to the system-terminal state
|
|
# `cancelled` (stop agent via the graceful SIGTERM cascade, cancel all jobs, remove
|
|
# worktree + delete the remote feature branch [never main / never force-push],
|
|
# tombstone the natural keys for a clean re-create via "To Analyse"; docs preserved).
|
|
# STOP during a critical merge/deploy window is DEFERRED until the irreversible step
|
|
# finishes honestly. The relaunch-hole gate restricts the "To Analyse" agent relaunch
|
|
# to the `analysis` stage (the sole Needs-Input owner). Additive, never-raise.
|
|
# Infra precondition: create a "STOP" status with the `cancelled` group on the ORCH
|
|
# board (07-infra-requirements.md). Leaf src/cancel.py.
|
|
# STOP_STATUS_ENABLED=false -> STOP handling AND the relaunch-hole gate are inert
|
|
# (behaviour strictly as before ORCH-090).
|
|
# STOP_STATUS_REPOS (CSV) -> scope; EMPTY = ALL repos (cancellation is meaningful
|
|
# for enduro too).
|
|
ORCH_STOP_STATUS_ENABLED=true
|
|
ORCH_STOP_STATUS_REPOS=
|
|
# ORCH-094: terminal-window-aware guard for the three deploy-phase Plane status
|
|
# setters (set_issue_awaiting_deploy / set_issue_deploying / set_issue_monitoring).
|
|
# A DB stage=done task converges to Done idempotently instead of flapping
|
|
# Awaiting <-> Monitoring, EXCEPT the legitimate post-deploy Monitoring while the
|
|
# window is active (ARMED & not DONE). Leaf src/deploy_status_guard.py, never-raise;
|
|
# STAGE_TRANSITIONS / QG_CHECKS / machine-verdict keys untouched (no DB migration).
|
|
# DEPLOY_STATUS_GUARD_ENABLED=false -> setters are terminal-blind (1:1 pre-ORCH-094).
|
|
# DEPLOY_STATUS_GUARD_REPOS (CSV) -> scope; EMPTY = self-hosting only (orchestrator),
|
|
# the only repo where deploy-phase statuses are set.
|
|
ORCH_DEPLOY_STATUS_GUARD_ENABLED=true
|
|
ORCH_DEPLOY_STATUS_GUARD_REPOS=
|
|
# ORCH-071/073: merge-verify under-gate on the `deploy -> done` edge (врезка in
|
|
# advance_stage, NOT a new STAGE_TRANSITIONS edge / registered QG). A deterministic
|
|
# merge-actor merges the feature code-PR via the Gitea PR-merge API (never push/
|
|
# force-push to main), then `done` is allowed ONLY when the deployed SHA is proven an
|
|
# ancestor of origin/main (ORCH-073 FR-1: SHA-in-main is the single criterion; a
|
|
# merged PR alone no longer confirms). A secondary regression guard then checks a
|
|
# declarative marker set (MAIN_REGRESSION_MARKERS) is still in origin/main; a missing
|
|
# marker -> alert + HOLD (NOT done), a git error of the grep itself -> fail-open.
|
|
# MERGE_VERIFY_ENABLED -> global kill-switch (false -> strictly pre-ORCH-071).
|
|
# MERGE_VERIFY_REPOS -> CSV of repos where the under-gate is REAL; empty ->
|
|
# only the self-hosting repo (orchestrator); non-self -> no-op.
|
|
# MERGE_PR_TIMEOUT_S -> per Gitea list/merge HTTP call timeout.
|
|
# MERGE_VERIFY_TIMEOUT_S -> git fetch/merge-base timeout for the ancestor + marker checks.
|
|
# REGRESSION_GUARD_ENABLED -> kill-switch for the ORCH-073 main-integrity regression
|
|
# guard (false -> SHA-in-main alone gates done); reuses the
|
|
# merge-verify scope, so non-self repos are a no-op.
|
|
# MERGE_VERIFY_AUTOCREATE_PR_ENABLED -> ORCH-082: guarantee an open code-PR
|
|
# (head==branch, base==main) via merge_gate.ensure_open_pr
|
|
# BEFORE the deterministic merge_pr (fixes the false HOLD
|
|
# "no open PR"). false -> exactly pre-ORCH-082 behaviour.
|
|
# Reuses the merge-verify scope; non-self repos -> no-op.
|
|
ORCH_MERGE_VERIFY_ENABLED=true
|
|
ORCH_MERGE_VERIFY_REPOS=
|
|
ORCH_MERGE_PR_TIMEOUT_S=60
|
|
ORCH_MERGE_VERIFY_TIMEOUT_S=60
|
|
ORCH_REGRESSION_GUARD_ENABLED=true
|
|
ORCH_MERGE_VERIFY_AUTOCREATE_PR_ENABLED=true
|
|
# ORCH-093: deterministic merge-actor retry of TRANSIENT Gitea merge errors. merge_pr
|
|
# wraps ONLY the mutating POST /pulls/{n}/merge in a bounded exponential-backoff
|
|
# retry-loop on transient outcomes (405 "try again later" / 408 / 5xx / network /
|
|
# timeout, and 409|422 while the PR is still mergeable); terminal outcomes
|
|
# (403/404/real conflict) -> fast honest False (the ORCH-071/081 HOLD backstop is
|
|
# unchanged). Fixes the ORCH-063 false HOLD + manual re-merge. The already-in-main
|
|
# guard (no commits beyond origin/main -> no garbage PR) is always-on under
|
|
# MERGE_VERIFY_AUTOCREATE_PR_ENABLED (no separate flag).
|
|
# MERGE_RETRY_ENABLED -> kill-switch; false -> exactly one POST (one-shot, prior behaviour).
|
|
# MERGE_RETRY_MAX_ATTEMPTS -> max POST attempts on a transient outcome.
|
|
# MERGE_RETRY_BACKOFF_BASE_S -> exponential backoff base seconds (sleep = base*2^(i-1)).
|
|
# MERGE_RETRY_BACKOFF_MAX_S -> per-sleep backoff ceiling seconds (bounds total wait).
|
|
ORCH_MERGE_RETRY_ENABLED=true
|
|
ORCH_MERGE_RETRY_MAX_ATTEMPTS=3
|
|
ORCH_MERGE_RETRY_BACKOFF_BASE_S=2
|
|
ORCH_MERGE_RETRY_BACKOFF_MAX_S=5
|
|
# ORCH-036: executable self-deploy of the `deploy` stage. For the self-hosting repo
|
|
# (orchestrator) the stage REALLY restarts prod (8500) via a detached host hook;
|
|
# deploy_status: SUCCESS means proven health-ok, not an LLM declaration. Three
|
|
# deterministic phases (A: request approve, B: human Approved -> detached deploy,
|
|
# C: finalizer maps hook exit-code -> deploy_status). Non-self repos: unchanged
|
|
# synchronous ssh deploy. SECRETS / host paths live ONLY on the host — do NOT commit.
|
|
# SELF_DEPLOY_ENABLED -> global kill-switch (false -> legacy synchronous deploy for all).
|
|
# SELF_DEPLOY_REPOS -> CSV of repos where Phase A/B/C is REAL; empty -> only the
|
|
# self-hosting repo (orchestrator); others -> no-op (mirrors ORCH-35).
|
|
# DEPLOY_REQUIRE_MANUAL_APPROVE -> require a human Plane "Approved" before the prod
|
|
# deploy (true on rollout; full auto is ORCH-54).
|
|
# DEPLOY_FINALIZE_DELAY_S -> delay before the first/each finalize poll (>= hook+health).
|
|
# DEPLOY_FINALIZE_MAX_ATTEMPTS -> bounded finalize-defer budget (anti-livelock).
|
|
# DEPLOY_SSH_USER / DEPLOY_SSH_HOST -> ssh target for the host hook (DEPLOY_SSH_HOST
|
|
# empty -> detached deploy will NOT launch; set on the host).
|
|
# DEPLOY_HOOK_SCRIPT -> path to the hook ON THE HOST (relative to the repo).
|
|
# DEPLOY_HOST_REPO_PATH -> orchestrator clone path on the host.
|
|
# DEPLOY_PROD_SOURCE_IMAGE -> staging-validated image, retagged build-once (no rebuild).
|
|
# DEPLOY_PROD_TARGET_SERVICE / _PORT / _IMAGE / _COMPOSE_PROFILE -> prod compose profile.
|
|
# DEPLOY_PROD_PREV_IMAGE_FILE -> prod prev-image snapshot (separate from staging's).
|
|
ORCH_SELF_DEPLOY_ENABLED=true
|
|
ORCH_SELF_DEPLOY_REPOS=
|
|
ORCH_DEPLOY_REQUIRE_MANUAL_APPROVE=true
|
|
ORCH_DEPLOY_FINALIZE_DELAY_S=90
|
|
ORCH_DEPLOY_FINALIZE_MAX_ATTEMPTS=10
|
|
ORCH_DEPLOY_SSH_USER=slin
|
|
ORCH_DEPLOY_SSH_HOST=
|
|
ORCH_DEPLOY_HOOK_SCRIPT=scripts/orchestrator-deploy-hook.sh
|
|
ORCH_DEPLOY_HOST_REPO_PATH=/home/slin/repos/orchestrator
|
|
ORCH_DEPLOY_PROD_SOURCE_IMAGE=orchestrator-orchestrator-staging
|
|
ORCH_DEPLOY_PROD_TARGET_SERVICE=orchestrator
|
|
ORCH_DEPLOY_PROD_TARGET_PORT=8500
|
|
ORCH_DEPLOY_PROD_TARGET_IMAGE=orchestrator-orchestrator
|
|
ORCH_DEPLOY_PROD_COMPOSE_PROFILE=
|
|
ORCH_DEPLOY_PROD_PREV_IMAGE_FILE=.deploy-prev-image-prod
|
|
|
|
# ORCH-058: staging-image provenance before the BUILD-ONCE prod retag (INV-FRESH).
|
|
# Guarantees the staging image promoted to prod is the EXACT artefact rebuilt from the
|
|
# validated commit — two layers, self-hosting only:
|
|
# A (liveness): QG sub-check `check_staging_image_fresh` on the deploy-staging->deploy
|
|
# edge rebuilds orchestrator-orchestrator-staging from the validated commit + recreates
|
|
# 8501; FAIL -> rollback to development. (builds/recreate STAGING only, never prod.)
|
|
# B (safety): the Dockerfile stamps `org.opencontainers.image.revision`; the prod hook
|
|
# fail-closes (exit 1) before `docker tag` if SOURCE_IMAGE's label != EXPECTED_REVISION.
|
|
# ENABLED -> single kill-switch for A+B as a WHOLE (never "B without A"); false -> legacy.
|
|
# REPOS -> CSV of repos where the gate is REAL; empty -> only self-hosting (orchestrator).
|
|
ORCH_IMAGE_FRESHNESS_ENABLED=true
|
|
ORCH_IMAGE_FRESHNESS_REPOS=
|
|
|
|
# ORCH-061: staging-verdict tolerance to sandbox-infra-only FAILs. The self-hosting
|
|
# orchestrator looped on deploy-staging because staging_check.py exited 1 on ANY FAIL,
|
|
# so two infra-only checks (C9a sandbox branch / C9b analyst-job — caused by SANDBOX
|
|
# bot accounts not being members of the sandbox Plane project, NOT a pipeline regress)
|
|
# forced staging_status: FAILED -> rollback -> loop. With this ON, C9a/C9b are WAIVED
|
|
# to SUCCESS when every REAL check is green; any REAL failure still fails closed.
|
|
# true (default) -> tolerant; false -> legacy strict (1:1 pre-ORCH-061, any FAIL rolls back).
|
|
# Lives in .env.staging (the staging instance). CLI --strict overrides this per-run.
|
|
ORCH_STAGING_INFRA_TOLERANCE_ENABLED=true
|
|
|
|
# ORCH-053: stuck-task reconciler (sweeper for lost webhooks). A background daemon
|
|
# replays a missed stage transition through the SAME gates/handlers a webhook would,
|
|
# fixing tasks that got stuck on a dropped event (502 on rebuild, no Plane/Gitea
|
|
# retries, unresolved sha->branch).
|
|
# ENABLED -> global kill-switch (self-hosting safety / staged rollout).
|
|
# PLANE_ENABLED -> separate flag for the F-2 Plane-API poll (mute only F-2).
|
|
# INTERVAL_S -> background sweep period (seconds).
|
|
# GRACE_DEFAULT_S -> default "stuck" threshold on tasks.updated_at (seconds).
|
|
# GRACE_OVERRIDES_JSON -> per-stage thresholds, e.g. {"development":300}; bad JSON -> default.
|
|
# NOTIFY_UNBLOCK -> send a Telegram message when a stuck task is unblocked.
|
|
# SKIP_BLOCKED_ENABLED -> ORCH-060 F-1 Guard 2: skip reconciling issues a human moved
|
|
# to Blocked / Needs Input (per-candidate Plane state lookup).
|
|
# false mutes ONLY the networked Guard 2; Guard 1 (escalated by
|
|
# developer retries, local+deterministic) is always active.
|
|
ORCH_RECONCILE_ENABLED=true
|
|
ORCH_RECONCILE_PLANE_ENABLED=true
|
|
ORCH_RECONCILE_INTERVAL_S=120
|
|
ORCH_RECONCILE_GRACE_DEFAULT_S=600
|
|
ORCH_RECONCILE_GRACE_OVERRIDES_JSON=
|
|
ORCH_RECONCILE_NOTIFY_UNBLOCK=true
|
|
ORCH_RECONCILE_SKIP_BLOCKED_ENABLED=true
|
|
|
|
# ORCH-068: TTL (seconds) for the per-project Plane states cache (plane_sync
|
|
# _STATES_CACHE). Historically the cache lived for the whole process lifetime,
|
|
# so a status added to Plane after start was invisible until a restart
|
|
# ("stale set -> no pipeline action"). With a TTL the entry self-heals by
|
|
# re-fetching /states/ once it expires (reuses reload_project_states()).
|
|
# >0 -> re-fetch after this many seconds (default 300 = 5 min);
|
|
# 0 -> disable TTL -> strictly the previous lifetime cache (back-compat).
|
|
ORCH_PLANE_STATES_TTL_S=300
|
|
|
|
# ORCH-065: job-reaper + proactive merge-lease reclaim. A background daemon thread
|
|
# (src/job_reaper.py, started LAST in main.lifespan after requeue_running_jobs) reaps
|
|
# zombie 'running' jobs whose monitor/process died before writing the terminal status
|
|
# (one zombie at max_concurrency=1 blocks the whole shared queue) and periodically
|
|
# reclaims dead/stale merge-leases. Liveness is three-tier: Tier-1 dead jobs.pid
|
|
# (os.kill(pid,0)) after REAPER_DEAD_TICKS consecutive dead ticks (anti-false-positive
|
|
# for a live agent); Tier-2 agent_runs.exit_code recorded but job still 'running'
|
|
# (only after a REAPER_FINALIZE_GRACE_S finalization grace, so a live monitor still
|
|
# doing git push / PR / Plane comments is never reaped); Tier-3 backstop after
|
|
# REAPER_MAX_RUNNING_S. The terminal flip carries an atomic status='running' guard and
|
|
# precedes any advance/enqueue (claim-before-act) so it never double-processes/-advances
|
|
# a row racing a late monitor or requeue_running_jobs.
|
|
# REAPER_ENABLED -> global kill-switch (false -> strictly prior behaviour).
|
|
# REAPER_INTERVAL_S -> background scan period (seconds).
|
|
# REAPER_DEAD_TICKS -> consecutive dead-pid ticks before reaping (Tier-1, >=2).
|
|
# REAPER_MAX_RUNNING_S -> Tier-3 backstop ceiling; must exceed max agent_timeout+grace.
|
|
# REAPER_FINALIZE_GRACE_S -> Tier-2 grace: how long agent_runs.exit_code must have been
|
|
# recorded before a still-'running' job is reaped; MUST exceed
|
|
# the max finalization window (git push + PR + Plane comments).
|
|
# LEASE_RECLAIM_ENABLED -> kill-switch for the proactive stale/dead lease reclaim
|
|
# (false -> only the legacy lazy TTL reclaim in acquire_merge_lease).
|
|
# (reuse) ORCH_MERGE_LOCK_TIMEOUT_S -> lease TTL; ORCH_MERGE_GATE_REPOS -> reclaim scope.
|
|
ORCH_REAPER_ENABLED=true
|
|
ORCH_REAPER_INTERVAL_S=60
|
|
ORCH_REAPER_DEAD_TICKS=2
|
|
ORCH_REAPER_MAX_RUNNING_S=3600
|
|
ORCH_REAPER_FINALIZE_GRACE_S=300
|
|
ORCH_LEASE_RECLAIM_ENABLED=true
|
|
|
|
# ORCH-063: disk-watchdog — background heartbeat that measures HOST-FS fill via the
|
|
# mounted bind-paths (/repos, /app/data) with shutil.disk_usage (NOT the container
|
|
# overlay /) and Telegram-alerts the operator at >= threshold. On 07.06.2026 the
|
|
# mva154 host disk silently hit 100% and stalled the WHOLE self-hosting pipeline;
|
|
# this is the missing proactive signal. Daemon thread modelled on reconciler/reaper
|
|
# (start/stop in main.lifespan, /queue snapshot, never-raise). Anti-spam state is
|
|
# in-memory (no DB migration); the watchdog only READS fill and SENDS Telegram — it
|
|
# never touches the disk/container or restarts prod (self-hosting safety).
|
|
# DISK_MONITOR_ENABLED -> kill-switch; false -> the daemon does not start (1:1 as before).
|
|
# DISK_MONITOR_INTERVAL_S -> heartbeat measurement period, seconds (order of minutes).
|
|
# DISK_MONITOR_THRESHOLD_PCT -> fill % that triggers the alert (Owner-fixed 85; valid 1..100).
|
|
# DISK_MONITOR_REALERT_S -> cooldown between repeat alerts while above threshold (~6h).
|
|
# DISK_MONITOR_PATHS -> CSV of monitored HOST bind-paths; empty -> /repos,/app/data.
|
|
ORCH_DISK_MONITOR_ENABLED=true
|
|
ORCH_DISK_MONITOR_INTERVAL_S=300
|
|
ORCH_DISK_MONITOR_THRESHOLD_PCT=85
|
|
ORCH_DISK_MONITOR_REALERT_S=21600
|
|
ORCH_DISK_MONITOR_PATHS=/repos,/app/data
|
|
|
|
# ORCH-062: build-cache-pruner — the "second half" of the disk-watchdog
|
|
# (watchdog SIGNALS, pruner CLEANS). A daemon thread modelled on disk_watchdog
|
|
# that periodically runs STRICTLY `docker builder prune -f --filter until=<until>`
|
|
# on the HOST over ssh (BuildKit GC). Touches ONLY the build cache: never
|
|
# images/containers of running services, never restarts the docker daemon or the
|
|
# prod container (self-hosting safety). State is in-memory (no DB migration). No
|
|
# ssh host configured -> the tick is a no-op. See docs/operations/INFRA.md.
|
|
# BUILD_CACHE_PRUNE_ENABLED -> kill-switch; false -> the daemon does not start (1:1 as before).
|
|
# BUILD_CACHE_PRUNE_INTERVAL_S -> tick period, seconds (order of hours; default ~6h). >0, else default.
|
|
# BUILD_CACHE_PRUNE_UNTIL -> retention age for the warm cache (`--filter until=`); ^\d+[smhdw]?$, else 24h.
|
|
# BUILD_CACHE_PRUNE_ALL -> add `-a` (ALWAYS paired with until); default false.
|
|
# BUILD_CACHE_PRUNE_TIMEOUT_S -> bound on the ssh command, seconds. >0, else default.
|
|
# BUILD_CACHE_PRUNE_NOTIFY_MIN_GB -> Telegram when reclaimed >= N GB; 0 -> silent.
|
|
ORCH_BUILD_CACHE_PRUNE_ENABLED=true
|
|
ORCH_BUILD_CACHE_PRUNE_INTERVAL_S=21600
|
|
ORCH_BUILD_CACHE_PRUNE_UNTIL=24h
|
|
ORCH_BUILD_CACHE_PRUNE_ALL=false
|
|
ORCH_BUILD_CACHE_PRUNE_TIMEOUT_S=120
|
|
ORCH_BUILD_CACHE_PRUNE_NOTIFY_MIN_GB=0
|
|
|
|
# ORCH-022: security-gate (secret-scanning + dependency audit) on the
|
|
# deploy-staging -> deploy edge, run FIRST among the edge sub-gates. Deterministic
|
|
# (no LLM): gitleaks (offline secret-scan, pinned Go binary in the image) + pip-audit
|
|
# (OSV/PyPI CVE audit). Verdict in the versioned 17-security-report.md frontmatter;
|
|
# FAIL -> rollback to development + developer-retry (cap 3). See ADR-001.
|
|
# GATE_ENABLED -> global kill-switch; false -> pipeline 1:1 as before ORCH-022.
|
|
# GATE_REPOS -> CSV of repos where the gate is REAL; empty -> only self-hosting.
|
|
# DEP_BLOCK_SEVERITY -> CVE severity that BLOCKS (CRITICAL>HIGH>MEDIUM>LOW); below /
|
|
# UNKNOWN -> warning only (anti-loop).
|
|
# SCAN_TIMEOUT_S -> per external scanner call timeout.
|
|
# DEP_AUDIT_FAIL_CLOSED -> strict mode: unreachable CVE feed -> FAIL instead of the
|
|
# default fail-open + warning (anti-loop). Default false.
|
|
# SECRETS_BLOCK -> a found secret blocks (always true by default; the offline
|
|
# secrets guarantee is unconditional).
|
|
ORCH_SECURITY_GATE_ENABLED=true
|
|
ORCH_SECURITY_GATE_REPOS=
|
|
ORCH_SECURITY_DEP_BLOCK_SEVERITY=HIGH
|
|
ORCH_SECURITY_SCAN_TIMEOUT_S=300
|
|
ORCH_SECURITY_DEP_AUDIT_FAIL_CLOSED=false
|
|
ORCH_SECURITY_SECRETS_BLOCK=true
|
|
|
|
# ORCH-021: post-deploy production monitoring + degradation reaction. After the
|
|
# terminal deploy->done transition for an applicable repo, a reserved-agent job
|
|
# `post-deploy-monitor` (no LLM, modelled on deploy-finalizer) probes prod over a
|
|
# window and reacts to a degradation the restart-time health-check missed (class
|
|
# "green deploy, red prod", precedent ET-8). State is in sentinel files
|
|
# (.post-deploy-state-<repo>/<wi>/), no DB migration.
|
|
# MONITOR_ENABLED -> global kill-switch; false -> pipeline is 1:1 as before ORCH-021.
|
|
# REPOS -> CSV of repos where monitoring is REAL; empty -> only self-hosting.
|
|
# WINDOW_S -> observation window length (~15 min).
|
|
# INTERVAL_S -> seconds between probe ticks.
|
|
# FAIL_THRESHOLD -> N CONSECUTIVE health failures -> DEGRADED.
|
|
# 5XX_THRESHOLD -> window 5xx ratio above this -> DEGRADED.
|
|
# AUTO_ROLLBACK -> allow auto-rollback; acts ONLY for non-self repos. Self-hosting
|
|
# is ALWAYS ALERT_ONLY (a tick NEVER restarts the prod container).
|
|
# BASE_URL -> base URL of the observed prod instance.
|
|
ORCH_POST_DEPLOY_MONITOR_ENABLED=true
|
|
ORCH_POST_DEPLOY_REPOS=
|
|
ORCH_POST_DEPLOY_WINDOW_S=900
|
|
ORCH_POST_DEPLOY_INTERVAL_S=30
|
|
ORCH_POST_DEPLOY_FAIL_THRESHOLD=3
|
|
ORCH_POST_DEPLOY_5XX_THRESHOLD=0.5
|
|
ORCH_POST_DEPLOY_AUTO_ROLLBACK=false
|
|
ORCH_POST_DEPLOY_BASE_URL=http://localhost:8500
|
|
|
|
# ── QG-0 entry validation (ORCH-069) ──────────────────────────────────────────
|
|
# Upper title-length limit for the QG-0 entry gate (_qg0_errors). The old 80-char
|
|
# cap was a hygiene limit, not structural (slug is cut to [:30] independently, the
|
|
# DB title TEXT is unbounded). Default 200. An invalid/empty value gracefully
|
|
# degrades to 200 (the process never crashes on startup).
|
|
ORCH_QG0_TITLE_MAX=200
|