ML model promotion is the rare deploy where "the pipeline is green" means almost nothing. Your eval suite passed on staging data; the question is whether the model is good for real traffic. That judgment call is exactly what a human gate is for — automated gates should check the metrics, and a human should own the promotion.
ML model promotion approval workflow
A promotion pipeline that:
- Trains and registers model v2 in the registry (automated)
- Runs eval against staging, collects offline + canary metrics (automated)
- Creates a promotion decision with the metrics attached (the gate)
- Promotes to production only on explicit human approval
- Auto-rolls back if the canary breaches guardrails
Step 1: the promotion gate
import os
from datetime import datetime, timedelta, timezone
from actionbox import Actionbox
box = Actionbox(api_key=os.environ["ACTIONBOX_TOKEN"])
decision = box.ask(
title="Promote model v2 to production?",
description="Evaluation passed; canary stable for 72h. Sign-off required before full rollout.",
context=[{
"type": "key_value",
"items": {
"model": "checkout_reranker",
"version": "v2 (2026-08-09)",
"offline_ndcg": "+4.2% vs v1",
"canary_latency_p95": "38ms (target <50ms)",
"canary_error_rate": "0.02%",
"cost_delta": "+$31/day",
"rolled_back_v1": "untouched",
},
}}],
interaction={"type": "single_choice", "label": "Promotion decision", "options": [
{"id": "promote", "label": "Promote to production", "style": "primary"},
{"id": "extend_canary", "label": "Extend canary 24h"},
{"id": "reject", "label": "Reject — keep v1", "style": "destructive"},
]},
expires_at=(datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
on_expire={"type": "resolve", "response": {"type": "single_choice", "value": "extend_canary"}, "reason": "no response, extended canary"},
)The approver sees the metrics next to the buttons — no dashboards to cross-reference.
Step 2: branch the pipeline on the decision
if decision == "promote":
registry.promote(model_version="v2", env="production")
elif decision == "extend_canary":
canary.extend(hours=24)
else:
notify("#ml-team", "v2 rejected; staying on v1")
raise SystemExit(1)A rejection is a decision with a reason, recorded for audit — it's not a silent skip.
Step 3: the canary keeps its own guardrails
The human gate covers promotion; the machine covers execution. After promotion, let the canary auto-rollback on guardrail breach — the same approval API can run the rollback decision automatically via its callback:
# Guardrail breach -> auto-rollback (pre-approved by runbook policy)
EXPIRES_AT=$(date -u -d '+15 minutes' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u -v+15M '+%Y-%m-%dT%H:%M:%SZ')
actionbox send "Canary error rate 3.1% — auto-rollback to v1" \
--option rollback="Roll back" \
--expires "$EXPIRES_AT" \
--on-expire-json '{"type":"resolve","response":{"type":"single_choice","value":"rollback"},"reason":"auto-rollback policy"}' \
--callback-url https://ml.acme.com/actionbox/rollbackFail-closed: if the callback can't be reached, the rollback doesn't happen and the incident page fires.
Where the gate fits your stack
| Stack | Gate point | Notes |
|---|---|---|
| MLflow / Sagemaker registry | stage → prod transition | register + ask, then promote |
| Argo / KServe rollout | canary → full | approve before ramping to 100% |
| LLM prompt/registry | new prompt version | attach eval deltas (see below) |
| Batch re-training cron | nightly retrain | gate on data-quality metrics |
LLM-specific: prompt changes need gates too
Prompts regress like code, but silently. Gate a prompt version the same way:
decision = box.ask(
title="Ship prompt v14 to production?",
context=[{"type": "key_value", "items": {
"task": "support-triage",
"offline_win_rate": "0.62 (v13: 0.58)",
"refusal_rate": "1.1% (target <2%)",
"hallucination_hallmark": "none in eval",
}}],
interaction={"type": "boolean", "label": "Ship prompt v14?"},
)Try it
pip install actionboxin your promotion job- Create a Source →
ACTIONBOX_TOKEN - Replace your "manual promotion ticket" with the
askgate above
Create a free Source · Python SDK reference · Approval gates for data pipelines