feat(plane): осмысленная статусная модель Plane (слой B — индикация)

Приводит статусы доски Plane к смыслу стадий конвейера, сохраняя
инвариант «статус — индикация, а не управление». Меняется только слой B
(отображение: src/plane_sync.py + точки выставления статуса в
stage_engine.py/webhooks/plane.py/reconciler.py); слой A — машина стадий
src/stages.py::STAGE_TRANSITIONS — остаётся байт-в-байт неизменным (AC-21).

- 6 новых логических ключей статуса (to_analyse, analysis, code_review,
  awaiting_deploy, deploying, monitoring) + сеттеры и диспетчер
  set_issue_stage_state.
- Project-relative alias-fallback (BR-12): новый ключ деградирует на
  базовый UUID того же проекта → нулевая регрессия для enduro-trails.
- Самодеплой (ORCH-036) индицирует фазы: Awaiting Deploy / Deploying;
  terminal-sync для self-hosting → Monitoring after Deploy, для прочих →
  терминальный Done.
- Post-deploy монитор (ORCH-021): HEALTHY → Done, DEGRADED → Blocked
  (только индикация; self-hosting ALERT_ONLY, прод не трогается, BR-5).
- Reconciler: триггер старта/резюма на To Analyse; Guard 2 учитывает
  новые активные ожидания без расширения skip-set на алиасах.
- never-raise контракт сеттеров и резолвера состояний сохранён.
- Раскатка — созданием статусов в Plane оператором, без kill-switch.

Инварианты не менялись: STAGE_TRANSITIONS, QG_CHECKS (12 чеков),
check_deploy_status, exit-код-контракт хука, merge-gate, схема БД.

ADR: docs/work-items/ORCH-066/06-adr/ADR-001-plane-status-model.md
Тесты: test_plane_status_model, test_plane_to_analyse_resume,
test_plane_status_failclosed + TC в существующих наборах. 774 passed.

Refs: ORCH-066

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 21:54:12 +00:00
committed by deployer
parent 22d3b77426
commit 0dfddf93f0
19 changed files with 999 additions and 40 deletions

View File

@@ -48,6 +48,9 @@ def silence_side_effects(monkeypatch):
"send_telegram", "plane_notify_stage", "plane_notify_qg", "plane_add_comment",
"set_issue_in_review", "set_issue_needs_input", "set_issue_in_progress",
"set_issue_blocked", "set_issue_done",
# ORCH-066 status setters.
"set_issue_analysis", "set_issue_awaiting_deploy", "set_issue_deploying",
"set_issue_monitoring",
):
monkeypatch.setattr(stage_engine, name, MagicMock())
@@ -127,6 +130,9 @@ def test_tc05_no_approve_does_not_call_prod_hook(monkeypatch):
assert _jobs() == []
# The restart-safe approve-requested marker was written.
assert self_deploy.has_marker("orchestrator", "ORCH-036", self_deploy.APPROVE_REQUESTED)
# ORCH-066 AC-6/AC-13: Phase A indicates `Awaiting Deploy`, NOT `In Review`.
stage_engine.set_issue_awaiting_deploy.assert_called_once_with("ORCH-036")
stage_engine.set_issue_in_review.assert_not_called()
# ---------------------------------------------------------------------------
@@ -151,6 +157,8 @@ def test_tc06_approved_calls_prod_hook_exactly_once(monkeypatch):
# The finalizer was enqueued.
assert any(j["agent"] == "deploy-finalizer" for j in _jobs())
assert self_deploy.has_marker("orchestrator", "ORCH-036", self_deploy.INITIATED)
# ORCH-066 AC-7: Phase B indicates `Deploying` on a successful initiate.
stage_engine.set_issue_deploying.assert_called_once_with("ORCH-036")
# 2nd (duplicate) Approved -> idempotent no-op, hook NOT called again.
res2 = advance_stage(