Runbook: Release Orchestrator - Promotion Not Progressing
For platform and release on-call. Use this when a Stella Ops release promotion appears “stuck” — the dashboard timeline stops advancing, the UI spins, or a downstream environment never receives the promoted artifact. It walks you from symptom to the actual lifecycle state the promotion is wedged in (almost always AwaitingApprovalor Deploying) and the correct way to unblock it.
Sprint: SPRINT_20260117_029_DOCS_runbook_coverage Task: RUN-004 - Release Orchestrator Runbooks
Reconciliation note (verify against
src/ReleaseOrchestrator): This runbook was reconciled against the implemented Release Orchestrator. A promotion is theStellaOps.ReleaseOrchestrator.Promotionaggregate, whose lifecycle is thePromotionStatusenum (Pending → AwaitingApproval → Approved → Deploying → Deployed, plus terminalRejected/Cancelled/Failed/RolledBack) enforced byPromotionStateMachine. There is noin_progressstate — a promotion that “looks stuck” is almost always sitting inAwaitingApproval(no approval recorded) orDeploying(deployment running). Earlier drafts referenced astella promotion status/trace/gatescommand group,stella orch status/connectivity/locks/credentials, astella service restartcommand, acheck.orchestrator.job-healthDoctor check, and anOrchestratorPromotionStuckalert — none of those exist in source. They have been corrected to the implemented surface or flagged NOT IMPLEMENTED below.
Metadata
| Field | Value |
|---|---|
| Component | Release Orchestrator |
| Severity | High |
| On-call scope | Platform team, Release team |
| Last updated | 2026-05-30 |
| Doctor check | check.release.active (Release Pipeline plugin; default severity Warn) |
The Doctor check above is the real “stuck release” diagnostic —
ActiveReleaseHealthCheck(src/Doctor/__Plugins/StellaOps.Doctor.Plugin.Release/Checks/ActiveReleaseHealthCheck.cs). It queries the real Release Orchestrator deployment and approval read models and flagspending/running/paused/rolling_backdeployments or stale pending approvals. There is nocheck.orchestrator.job-healthcheck in source. The full Release Pipeline plugin also shipscheck.release.promotion.gates,check.release.rollback.readiness,check.release.environment.readiness, andcheck.release.environment.matches-approved.
Symptoms
- [ ] Promotion stuck in
AwaitingApprovalwith noApprovalRecordposted (most common) - [ ] Promotion stuck in
Deploying— deployment step running longer than expected - [ ] No progress in the release dashboard timeline
- [ ] UI shows promotion spinner indefinitely
- [ ] Downstream environment not receiving the promoted artifact
- [ ]
stella doctor --check check.release.activereports a Warn/Fail (stuck/approval-pending counts > 0)
NOT IMPLEMENTED: there is no
OrchestratorPromotionStuckalert in source. The release health signal is the Doctorcheck.release.activeresult, whose thresholds are encoded inActiveReleaseHealthCheck: anexecuting/pendingrelease warns at 1h and fails at 4h; apending_approvalrelease warns at 4h and fails at 24h. Verify any dashboard alert is wired to those Doctor results, not to a nonexistent metric.
Impact
| Impact Type | Description |
|---|---|
| User-facing | Release blocked; cannot promote to the target environment |
| Data integrity | Artifact is safe; the promotion is re-evaluable. A Failed promotion may transition to RolledBack or back to AwaitingApproval (the only non-terminal exits the state machine allows from Failed) |
| SLA impact | Release SLO violated if not resolved promptly |
Diagnosis
Quick checks
Run the Doctor active-release check:
stella doctor --check check.release.activeThe evidence block reports
stuck_release_count,failed_release_count,pending_approval_count, and per-release summaries (name:state:Nmin). Aconnection_error_type=timeoutorCannot reach Release OrchestratorWarn points at an unreachable/slow orchestrator rather than a stuck promotion.List deployments and find the stuck one:
stella deployment list --environment <environment> stella deployment list --status running,failed --jsonCorrection, 2026-07-28. This step previously read
stella promote list --env …and asserted thatstella promote {start|status|approve|reject|list}“is the implemented promotion CLI”, citingsrc/Cli/StellaOps.Cli/Commands/PromoteCommandHandler.cs. All of that was wrong:stella promoteexits 2 (Unrecognized command or argument 'promote'), and noPromoteCommandHandler.csexists undersrc/Cli/— the only file of that name in the repository is an archived audit note underdocs-archive/.The shipped split is:
stella release …acts,stella deployment …observes. There is no CLI command that lists promotions as objects; use the Console orGET /api/v1/release-orchestrator/…. The surrounding claim thatstella promotionbuilds promotion attestations rather than lifecycle operations is correct — its verbs areassemble,attest,verify.Inspect the deployment’s current status and detail:
stella deployment show <deployment-id> stella deployment watch <deployment-id> --interval 5 # poll to a terminal state stella deployment events <deployment-id>These print status, per-step deployment progress, and the event stream, and tell you whether the release is waiting on an approval or mid-deployment.
Deep diagnosis
Determine which lifecycle state the promotion is in. Map the symptom to the state machine (
PromotionStateMachine.ValidTransitions):AwaitingApproval→ waiting for the requiredApprovalRecord(s). Valid exits:Approved,Rejected,Cancelled. This is the dominant “stuck” case.Approved→ ready to deploy. Valid exits:Deploying,Cancelled.Deploying→ deployment running. Valid exits:Deployed,Failed.Failed→ valid exits:RolledBack,AwaitingApproval(re-submit). A promotion inPendingwas created but never submitted for approval.
Check the promotion gates. Gate evaluation results live on the promotion (
Promotion.GateResults, each aGateResultwithGateName,GateType,Passed,Blocking). The seven built-in gates registered byGateRegistry.RegisterBuiltInareapproval-gate,security-gate,policy-gate,dependency-gate,schedule-gate,freeze-window-gate, andmanual-gate. Six of the provider classes live under…/Promotion/Gate/BuiltIn/(ApprovalGate,PolicyGate,DependencyGate,ScheduleGate,FreezeWindowGate,ManualGate);SecurityGatelives under…/Promotion/Gate/Security/. For gate health (missing policies, unreachable attestor, missing approvers) run:stella doctor --check check.release.promotion.gatesSee
orchestrator-gate-timeout.mdfor the CVE-aware policy-gate timeout path (HttpPolicyEngineGateClient, fail-closeddeny).Inspect gate status for the underlying release.
REMOVED (2026-07-03): the
stella release gates {list|status|approve|reject}group emitted hardcoded sample output and was deleted from the CLI (stella releasenow carries only the real approve/reject/promote/deploy/rollback verbs). The authoritative gate state for a promotion isPromotion.GateResults(see step 2), the Doctorcheck.release.promotion.gatesresult, and the UI Gate policies / promotion decision cockpit (/releases/promotions/<id>).Read the orchestrator logs. Look for the deployment-step and gate-evaluation lines. Gate metrics emitted by
ReleaseOrchestratorPluginMonitorarestellaops_gate_evaluations_totalandstellaops_gate_evaluation_duration_ms(verify the exposed names on your build).
NOT IMPLEMENTED: there is no
stella orch connectivity --target <env>, nostella orch locks list, and no lock-contention surface for promotions in source. Thestella orchcommand group operates the Source & Job Orchestrator (sources/backfill/quotas) and is unrelated to release promotions. Environment connectivity is exercised via the deployment agents (SSH/WinRM/Docker/Compose/etc.), not a promotion-level lock manager.
Resolution
Immediate mitigation
If the promotion is waiting in
AwaitingApproval, record the approval (or rejection). These operations require therelease:publishscope (policyReleaseOrchestratorPolicies.ReleaseApprove):stella release approve <approval-id> --comment "Approved by <approver>" # or stella release reject <approval-id> --reason "<why>"Note the argument is the approval request id, not the promotion id. Both verbs also accept
--sign(plus--key/--key-id/--fingerprint/--algorithm) to submit the decision as an operator-decision DSSE envelope.REST equivalents:
POST /api/v1/release-orchestrator/promotions/{id}/approveand…/reject(ReleaseDashboardEndpoints, also mirrored under/api/release-orchestrator). The v2 decision surface isPOST /api/v1/approvals/{id}/decision(ReleaseControlV2Endpoints, actionsapprove|reject|defer|escalate).If a blocking gate is wedged, drive the decision through the promotion approval surface (CLI
stella release approve|reject <approval-id>, REST…/promotions/{id}/approveor v2…/approvals/{id}/decision) rather than a “skip” command.The dedicated scope to bypass release policy gates is
release:bypass(StellaOpsScopes.ReleaseBypass); confirm which surface honors it before relying on a skip flow. There is nostella promotion gate skipcommand in source, and the former stubbedstella release gates approve/stella release promote --forcesample-output commands were removed from the CLI on 2026-07-03 — drive overrides through the promotion approval surface above, never a CLI “skip”.If the promotion is
Deployingand the deployment is wedged, triage the deployment itself (agent reachability, target health). A failed deployment moves the promotion toFailed; from there the state machine allowsRolledBackor re-submission viaAwaitingApproval. Rollback of a run goes throughPOST /api/v1/runs/{id}/rollback(ReleaseControlV2Endpoints), which is only accepted when the run status isfailed/warning/degraded. Seeorchestrator-rollback-failed.md.If the orchestrator service is unresponsive, restart/redeploy the release-orchestrator container through your normal deployment tooling (compose/agent). There is no
stella service restart orchestratorcommand in source.
Root cause fix
Approval bottleneck (promotion sits in AwaitingApproval):
- Confirm approvers are configured for the gate — Doctor reports
no_approverswhen an approval gate has none (check.release.promotion.gates). - Ensure the approving principal holds
release:publish; without it the approve/reject endpoints return 403. - Separation-of-duties is enforced (
SeparationOfDutiesEnforcer) — the requester may be blocked from self-approving. Use a different approver.
Policy gate denying or timing out:
- Triage with
orchestrator-gate-timeout.md(CVE-aware policy gate;ReleaseOrchestrator:Policy:TimeoutSeconds, default 10s, fail-closed on timeout). - Verify the environment policy binding exists (
release_orchestrator.environment_policy_bindings).
Target environment unreachable (promotion sits in Deploying):
- Verify the deployment agent for the target environment is healthy and reachable.
- Check target credentials/secrets resolution (the orchestrator resolves tenant secrets at deploy time; a resolution failure surfaces as a failed deployment step).
NOT IMPLEMENTED in source (forward-looking / roadmap only — do not run): per-gate timeout retry config (
stella orch config set gates.<name>.retries), optimistic locking modes (stella orch config set locks.mode optimistic), lock timeout config (stella orch config set locks.timeout), andstella orch credentials verify. The CVE gate timeout is configured viaReleaseOrchestrator:Policy:TimeoutSeconds(see the gate runbook), not agates.<name>.timeoutkey.
Verification
# Re-check active releases (stuck/approval-pending counts should clear)
stella doctor --check check.release.active
# Confirm the deployment advanced
stella deployment show <deployment-id>
# Confirm gate health
stella doctor --check check.release.promotion.gates
After approval (or restoring the target / policy engine), confirm the promotion transitions through Approved → Deploying → Deployed. stella deployment watch <deployment-id> polls to a terminal state (--interval seconds, default 2). Watching is a separate verb here, not a --watch flag — stella deployment show --watch is not valid. (stella doctor does have a --watch flag; the two are unrelated.)
Prevention
- [ ] Approvers: Configure approvers on every approval gate so promotions don’t stall in
AwaitingApproval; Doctorcheck.release.promotion.gatesflagsno_approvers. - [ ] Approval SLAs: Watch the Doctor
check.release.activepending_approval_count— it warns at 4h and fails at 24h. - [ ] Deployment timeouts: Watch the same check’s
stuck_release_countforexecutingreleases (warn 1h / fail 4h). - [ ] Monitoring: Alert on Doctor
check.release.activeresults (and, for gates,stellaops_gate_evaluation_duration_ms). There is no dedicatedpromotion_stuckmetric to alert on.
Related Resources
- Doctor article:
docs/doctor/articles/release/active.md - Architecture / module dossier:
docs/modules/release-orchestrator/(e.g.modules/promotion-manager.md,workflow/promotion.md,gates/). The previously citeddocs/modules/release-jobengine/architecture.mddoes not exist — the dossier lives underrelease-orchestrator. - Related runbooks:
orchestrator-gate-timeout.md,orchestrator-rollback-failed.md,orchestrator-evidence-missing.md,orchestrator-quota-exceeded.md,policy-evaluation-slow.md - Dashboard: Grafana > Stella Ops > Release Orchestrator (verify panel/metric names against the emitted
stellaops_gate_*series and Doctorcheck.release.*results).
