Files
orchestrator/tests/test_qg_registry_snapshot.py
claude-bot ebbf2e7a2d feat(cancel): STOP-status task cancellation + relaunch-hole close (ORCH-090)
Introduce the dedicated Plane STOP status as a single declarative task-cancel
mechanism: stop the active agent (graceful SIGTERM cascade), cancel all jobs
(terminal `cancelled`, never requeued), remove the worktree + delete the remote
feature branch (never main, never force-push), drive the task to the new
system-terminal state `cancelled` and tombstone the natural keys so a later
"To Analyse" re-creates it from scratch (docs artefacts preserved). STOP during a
critical merge/deploy window is deferred until the irreversible step finishes
honestly. Also closes the relaunch hole: handle_status_start relaunch is gated to
the `analysis` stage; the only pipeline-start entry point remains "To Analyse".

Cross-cutting (adr-0026): the "task terminal" predicate is widened {done} ->
{done, cancelled} in serial_gate / task_deps / stages sink + reaper/worker
requeue guards. STAGE_TRANSITIONS exit-gates / QG_CHECKS / check_* are unchanged
(`cancelled` is a sink, not a new edge). Additive, never-raise, restart-safe,
under kill-switch ORCH_STOP_STATUS_ENABLED (off -> zero regression).

New: src/cancel.py (leaf), src/gitea.py (delete_remote_branch), tasks columns
cancelled_at/cancel_requested_at, jobs status `cancelled`, GET /queue `stop` block.
Tests: tests/test_stop_status.py (TC-01..TC-14 + D7); full suite green (1345).
Docs updated in-PR (architecture README, CLAUDE.md, README.md, .env.example,
CHANGELOG). ADR-001 D4 refinement: plane_issue_id is tombstoned too (the lookup
ORs on it) — original UUID recoverable from the parseable suffix.

Refs: ORCH-090

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 21:31:56 +03:00

71 lines
3.1 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)
"check_staging_image_fresh", # ORCH-058 image-freshness sub-gate (same edge)
"check_security_gate", # ORCH-022 security sub-gate (same edge, run FIRST)
}
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."
)