Files
orchestrator/tests/test_qg_registry_snapshot.py
claude-bot 00d69d9e27
All checks were successful
CI / test (push) Successful in 15s
CI / test (pull_request) Successful in 17s
feat(merge-gate): auto-rebase onto current main + re-test + serialise merges
Deterministic (no-LLM) sub-gate on the deploy-staging -> deploy edge that
catches a feature branch up to the CURRENT origin/main, re-tests the combined
tree, and serialises merges with a per-repo file lease — so two green parallel
branches can no longer break main (self-hosting safety for the orchestrator repo).

- src/merge_gate.py: branch_is_behind_main, auto_rebase_onto_main (push
  --force-with-lease ONLY the task branch, NEVER main), retest_branch, and a
  file merge-lease (atomic O_CREAT|O_EXCL, holder-aware release, stale reclaim).
  Strict never-raise contract; all git ops in the per-branch worktree.
- src/qg/checks.py: check_branch_mergeable composes the primitives under the
  lease; registered in QG_CHECKS. Conditional rollout (merge_gate_enabled /
  merge_gate_repos, default self-hosting only).
- src/stage_engine.py: sub-gate hook on deploy-staging (not a new stage). PASS ->
  advance; "merge-lock busy" -> DEFER (re-queue with available_at, anti-deadlock
  at max_concurrency=1, capped); conflict/red re-test -> rollback to development
  + developer retry (capped by MAX_DEVELOPER_RETRIES). Lease released on
  deploy->done / rollback / PR-merged webhook.
- src/db.py: enqueue_job(available_at_delay_s=...) for the defer (no schema change).
- src/webhooks/gitea.py: holder-aware lease release on PR-merged.
- src/config.py + .env.example: ORCH_MERGE_* settings.

Docs: README + adr-0006 (architect) already cover the design; CHANGELOG updated.
Tests: test_merge_gate.py, test_qg_merge_gate.py, test_merge_gate_race.py,
test_stage_engine.py::TestMergeGate, test_config.py, QG-registry snapshot.
Full suite: 535 passed.

Refs: ORCH-043

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 17:32:50 +00:00

66 lines
2.7 KiB
Python

"""ORCH-016 / TC-20 + AC-11: Quality Gates + stage machine are unchanged.
Smoke / change-detector test: the ORCH-016 PR touches comment formatting only.
The QG registry (src/qg/checks.QG_CHECKS) and the stage-machine table
(src/stages.STAGE_TRANSITIONS) MUST remain bit-identical to the contracts the
pipeline depends on. If a future change moves the comment hot path into these
files by accident, this guard breaks first.
"""
import os
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
from src.qg.checks import QG_CHECKS # noqa: E402
from src.stages import STAGE_TRANSITIONS # noqa: E402
# The set of QG names the pipeline DEPLOYS on. Order doesn't matter, identity does.
_EXPECTED_QGS = {
"check_analysis_approved",
"check_analysis_complete",
"check_architecture_done",
"check_ci_green",
"check_review_approved",
"check_tests_passed",
"check_reviewer_verdict",
"check_tests_local",
"check_deploy_status",
"check_staging_status",
"check_branch_mergeable", # ORCH-043 merge-gate (deploy-staging -> deploy edge)
}
def test_tc20_qg_registry_unchanged():
assert set(QG_CHECKS.keys()) == _EXPECTED_QGS
def test_tc20_qg_callables_unchanged():
# All entries must be callable — no stub / lambda / None.
for name, fn in QG_CHECKS.items():
assert callable(fn), f"QG {name} is not callable"
# Reference snapshot of STAGE_TRANSITIONS (mirrors what's in docs/architecture
# and src/stages.py — duplicated here on purpose as a regression yardstick).
_EXPECTED_TRANSITIONS = {
"created": {"next": "analysis", "agent": "analyst", "qg": None},
"analysis": {"next": "architecture", "agent": "architect", "qg": "check_analysis_approved"},
"architecture": {"next": "development", "agent": "developer", "qg": "check_architecture_done"},
"development": {"next": "review", "agent": "reviewer", "qg": "check_ci_green"},
"review": {"next": "testing", "agent": "tester", "qg": "check_reviewer_verdict"},
"testing": {"next": "deploy-staging", "agent": "deployer", "qg": "check_tests_passed"},
"deploy-staging": {"next": "deploy", "agent": "deployer", "qg": "check_staging_status"},
"deploy": {"next": "done", "agent": None, "qg": "check_deploy_status"},
"done": {"next": None, "agent": None, "qg": None},
}
def test_tc20_stage_transitions_unchanged():
assert STAGE_TRANSITIONS == _EXPECTED_TRANSITIONS, (
"STAGE_TRANSITIONS drift detected — ORCH-016 must not change the "
"stage machine. Touched stage_engine or stages.py? Update the snapshot "
"in a separate, intentional PR."
)