"""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) "check_staging_image_fresh", # ORCH-058 image-freshness sub-gate (same edge) "check_security_gate", # ORCH-022 security sub-gate (same edge, run FIRST) "check_coverage_gate", # ORCH-027 coverage sub-gate (same edge, after merge-gate) } 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}, # ORCH-090 (adr-0026): terminal SINK for a STOP-cancelled task (parallel to # `done`; not a new edge — no exit-gate changed). "cancelled": {"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." )