feat(deploy): dedicated "Confirm Deploy" status triggers prod deploy

Split the overloaded `Approved` Plane status: it served BOTH as the human BRD
gate on `analysis` AND as the silent Phase B prod-deploy trigger on `deploy`
(ORCH-036), so a routine approve could launch a self-hosting prod restart.

ORCH-059 introduces a dedicated logical status `confirm_deploy` ("Confirm
Deploy") that triggers ONLY Phase B on `deploy`; `Approved` stays purely a
pipeline gate.

- plane_sync: map "Confirm Deploy" -> "confirm_deploy" in _PLANE_NAME_TO_KEY;
  intentionally absent from _DEFAULT_STATES => fail-closed (no UUID -> .get
  yields None, no KeyError, no blind deploy).
- webhooks/plane: handle_issue_updated routes "Confirm Deploy" (fail-closed
  .get) to new handle_confirm_deploy (guarded to stage=="deploy") ->
  _try_advance_stage(confirm_deploy=True).
- stage_engine: advance_stage gains keyword-only confirm_deploy=False; Phase B
  block returns early for deploy+finished_agent is None but only initiates the
  deploy when confirm_deploy=True; a plain Approved is a deterministic no-op
  (returns before check_deploy_status -> no false БАГ-8 rollback).
- Phase A CTA now asks the operator for "Confirm Deploy", not "Approved".

Contracts unchanged: STAGE_TRANSITIONS, QG_CHECKS, check_deploy_status, hook
exit codes, Phases A/C, merge-gate, DB schema. Conditional like ORCH-35/36
(self-hosting only). Docs updated (CLAUDE.md, architecture/README.md, CHANGELOG).

Refs: ORCH-059

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 19:12:41 +00:00
committed by deployer
parent dd07b58165
commit 86fe8dd509
12 changed files with 813 additions and 35 deletions

View File

@@ -139,12 +139,14 @@ def test_tc06_approved_calls_prod_hook_exactly_once(monkeypatch):
ssh_run = MagicMock(return_value=MagicMock(returncode=0, stdout="", stderr=""))
monkeypatch.setattr(self_deploy.subprocess, "run", ssh_run)
task_id = _make_task("deploy") # already on deploy, awaiting Approved
task_id = _make_task("deploy") # already on deploy, awaiting Confirm Deploy
# 1st human Approved -> Phase B initiates the detached deploy.
# ORCH-059: Phase B is now triggered by the dedicated "Confirm Deploy" status
# (confirm_deploy=True), NOT by a plain Approved. 1st Confirm Deploy ->
# Phase B initiates the detached deploy.
res1 = advance_stage(
task_id, "deploy", "orchestrator", "ORCH-036",
"feature/ORCH-036-x", finished_agent=None,
"feature/ORCH-036-x", finished_agent=None, confirm_deploy=True,
)
assert res1.note == "self-deploy-initiated"
assert ssh_run.call_count == 1
@@ -152,10 +154,10 @@ def test_tc06_approved_calls_prod_hook_exactly_once(monkeypatch):
assert any(j["agent"] == "deploy-finalizer" for j in _jobs())
assert self_deploy.has_marker("orchestrator", "ORCH-036", self_deploy.INITIATED)
# 2nd (duplicate) Approved -> idempotent no-op, hook NOT called again.
# 2nd (duplicate) Confirm Deploy -> idempotent no-op, hook NOT called again.
res2 = advance_stage(
task_id, "deploy", "orchestrator", "ORCH-036",
"feature/ORCH-036-x", finished_agent=None,
"feature/ORCH-036-x", finished_agent=None, confirm_deploy=True,
)
assert res2.note == "self-deploy-already-initiated"
assert ssh_run.call_count == 1 # still exactly one prod deploy