feat(ORCH-026): task dependencies (B waits for A) + single-repo merge serialization

Level A — merge/deploy serialization within one repo: reuse the existing
ORCH-043/065 merge-lease (no new mechanism); the only new logic is an
unconditional pre-merge rebase in check_branch_mergeable — under the held
lease, auto_rebase_onto_main is ALWAYS called when premerge_rebase_always
(default True), not just when the branch is behind. No-op on an up-to-date
branch (rebase keeps HEAD, force-with-lease -> "Everything up-to-date", CI
not triggered). Kill-switch off -> ORCH-043 behaviour 1:1.

Level B — declarative task dependencies: additive job_deps table
(CREATE ... IF NOT EXISTS, no live-DB migration); claim_next_job gate
(NOT EXISTS) defers a job whose depends-on tasks are not yet 'done' without
occupying a max_concurrency slot; inert on empty job_deps -> zero regression.
New leaf src/task_deps.py (never-raise): is_task_ready (fail-open), DFS cycle
detection + Blocked/alert, declare/ingest_plane_relations (db source never
hits the network on the hot path), snapshot. Telegram waiting-line, /queue
observability, reconciler skip + cycle backstop, reaper untouched.

Invariants unchanged: STAGE_TRANSITIONS, QG_CHECKS registry (dep gate is a
claim_next_job врезка, not a registered QG), DB schema of existing tables,
HTTP endpoints; non-self repos remain a no-op on empty deps/scope.

Flags: ORCH_PREMERGE_REBASE_ALWAYS, ORCH_TASK_DEPS_ENABLED, ORCH_TASK_DEPS_SOURCE.
Docs: docs/architecture/README.md, CLAUDE.md, .env.example, CHANGELOG.md,
adr-0015. Tests: tests/test_orch026_*.py (64 tests); full suite 991 green.

Refs: ORCH-026

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 19:06:22 +03:00
committed by stream
parent 9019e12d98
commit a74379f657
24 changed files with 1686 additions and 2 deletions

View File

@@ -396,6 +396,37 @@ class Settings(BaseSettings):
merge_pr_timeout_s: int = 60
merge_verify_timeout_s: int = 60
# ORCH-026: intra-repo merge serialisation (Level A) + declarative task
# dependencies (Level B). Level A reuses the ORCH-043/065 merge-lease window
# (no new mechanism) — the merge-lease already serialises "merge -> main-updated"
# per repo; the ONLY new behaviour is an unconditional pre-merge rebase. Level B
# adds a new ADDITIVE job_deps table + a NOT EXISTS gate in claim_next_job. Both
# features are inert without data (no applicable repo / no declared deps) ->
# zero regression for enduro-trails.
# premerge_rebase_always -> Level A (A-2): when True, check_branch_mergeable
# ALWAYS rebases the task branch onto the CURRENT
# origin/main UNDER the merge-lease (not only when
# branch_is_behind_main) — a deterministic anti-phantom
# that does not depend on the ancestor check's precision.
# auto_rebase_onto_main is a cheap no-op on an already
# up-to-date branch (rc 0, push up-to-date, CI not
# retriggered). Scope = merge_gate_repos (empty ->
# self-hosting). Kill-switch (False -> exactly the
# ORCH-043 behaviour: rebase only when behind). Env
# ORCH_PREMERGE_REBASE_ALWAYS.
# task_deps_enabled -> Level B (B-2): global kill-switch for the scheduler
# dependency gate. False -> claim_next_job is 1:1 as
# ORCH-1 (the NOT EXISTS clause is omitted). Inert when
# job_deps is empty. Env ORCH_TASK_DEPS_ENABLED.
# task_deps_source -> declaration source: db|plane|hybrid (default db).
# The scheduler ALWAYS reads the DB cache (offline-safe
# hot path); plane/hybrid additionally ingest Plane
# `blocked-by` relations into job_deps at task creation.
# Env ORCH_TASK_DEPS_SOURCE.
premerge_rebase_always: bool = True
task_deps_enabled: bool = True
task_deps_source: str = "db"
# ORCH-073 (ADR-001 Р-4): main-integrity regression guard. After the merge-verify
# under-gate confirms the deployed SHA is an ancestor of origin/main (FR-1), a
# secondary deterministic (no-LLM) guard checks that a declarative set of markers