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>
35 lines
1.5 KiB
Python
35 lines
1.5 KiB
Python
"""ORCH-089 — TC-26: invariant registries are NOT touched by the auto-label work.
|
|
|
|
The auto-mode reuses existing transitions/gates and only removes the wait for a
|
|
human signal; it must not add a stage, a transition, or a QG check (TRZ §10 / AC-10).
|
|
"""
|
|
import os
|
|
import tempfile
|
|
|
|
os.environ.setdefault("ORCH_DB_PATH", os.path.join(tempfile.gettempdir(), "test_auto_inv.db"))
|
|
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
|
|
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
|
|
|
|
|
|
def test_tc26_stage_transitions_unchanged():
|
|
from src.stages import STAGE_TRANSITIONS
|
|
# ORCH-090 (adr-0026): `cancelled` terminal sink added (parallel to `done`).
|
|
assert set(STAGE_TRANSITIONS) == {
|
|
"created", "analysis", "architecture", "development", "review",
|
|
"testing", "deploy-staging", "deploy", "done", "cancelled",
|
|
}
|
|
# The two human gates still use their existing QG names (unchanged).
|
|
assert STAGE_TRANSITIONS["analysis"]["qg"] == "check_analysis_approved"
|
|
|
|
|
|
def test_tc26_no_new_qg_check():
|
|
from src.qg.checks import QG_CHECKS
|
|
# No auto-label / auto-approve / auto-deploy QG check was introduced — the
|
|
# auto-mode is a decision врезка, not a registered quality gate.
|
|
assert not any(
|
|
tok in k for k in QG_CHECKS for tok in ("auto_label", "autoapprove", "autodeploy")
|
|
), "ORCH-089 must not register a new QG check"
|
|
# The gates it reuses are present and untouched.
|
|
for k in ("check_analysis_approved", "check_deploy_status", "check_staging_status"):
|
|
assert k in QG_CHECKS
|