Assemble predictive ML artifacts into validated training, registry promotion, canary monitoring, and rollback.
Four products now need the same operational discipline: a late-delivery warning model, a product ranker, a warehouse demand forecast, and a damaged-package photo classifier. Each uses different metrics, but each relies on immutable data evidence, validated candidates, controlled promotion, monitoring, and rollback.
This capstone assembles that discipline into one ML platform workflow. It isn't tied to a particular orchestrator or cloud vendor. A reviewer must be able to trace any live decision back to data, feature, model, policy, and promotion evidence. That requires stored receipts, not a mutable Boolean that happens to say passed.
Models differ, but their release manifest can share a schema:
| Field | ETA example | Ranking example | Forecast example | Vision example |
|---|---|---|---|---|
| data snapshot | carrier events through cutoff | catalog and judged queries | daily counts through cutoff | return photos grouped by shipment |
| feature or preprocessing version | eta-features-v1 | ranking-features-v1 | demand-lags-v1 | parcel-rgb-224-center-crop-v1 |
| model artifact | delay-model-v1 | market-ranker-v1 | warehouse-demand-v1 | damage-cnn-v1 |
| action policy | warning threshold | eligibility and slate rule | alert threshold | quality check and review threshold |
| promotion policy | slice recall and cost limits | blocked-listing and NDCG limits | peak underforecast-cost limit | usable-image and source-slice limits |
| monitor | delayed labels and freshness | impressions and returns | residuals and alert review | photo quality and reviewer labels |
| previous release | delivery-risk-v0 | market-ranker-v0 | warehouse-demand-v0 | damage-cnn-v0 |
The release tuple prevents an incident review from asking which threshold, feature transform, or gate policy happened to be active. Sculley et al. warn that ML systems accumulate debt through data dependencies, configuration, and feedback loops unless those boundaries are managed explicitly.[1]
Submit a small but inspectable platform surface:
1production-ml-platform/
2 contracts/
3 release_manifest.schema.json
4 promotion_policy.json
5 pipelines/
6 validate_snapshot.py
7 train_candidate.py
8 evaluate_candidate.py
9 promote_alias.py
10 registry/
11 releases.jsonl
12 monitoring/
13 live_windows.py
14 rollback_policy.py
15 receipts/
16 offline_gate_report.json
17 canary_monitor_report.json
18 alias_audit.jsonl
19 projects/
20 eta/
21 ranking/
22 forecast/
23 vision/
24 tests/
25 test_failed_gate_never_promotes.py
26 test_empty_monitor_window_holds.py
27 test_unregistered_receipt_never_promotes.py
28 test_alias_race_blocks_promotion.py
29 test_delayed_labels_block_promotion.py
30 test_rollback_restores_manifest.pyGoogle Cloud's MLOps architecture separates automated data/model validation, metadata, serving, monitoring, and continuous-training triggers around promotion.[2] Your repository needn't copy that platform, but it should prove each boundary through a deterministic local fixture and test.
Training completion is evidence, not permission to change live behavior. Start by freezing the whole release tuple. The candidate records both its previous release and its promotion policy, so a reviewer can replay the rollback target and gate thresholds before any traffic moves.
1from dataclasses import asdict, dataclass
2import json
3
4@dataclass(frozen=True)
5class PromotionPolicy:
6 policy_id: str
7 required_offline_gates: tuple[str, ...]
8 min_canary_windows: int
9 max_error_rate: float
10 max_p95_latency_ms: int
11 max_late_warning_cost_delta: float
12
13@dataclass(frozen=True)
14class Release:
15 release_id: str
16 data_snapshot: str
17 feature_version: str
18 model_artifact: str
19 action_policy_version: str
20 promotion_policy_version: str
21 previous_release: str | None
22
23@dataclass(frozen=True)
24class OfflineReceipt:
25 receipt_id: str
26 candidate: str
27 production_before: str
28 promotion_policy_version: str | None
29 failed_gates: tuple[str, ...]
30 decision: str
31
32POLICIES = {
33 "eta-promotion-v1": PromotionPolicy(
34 policy_id="eta-promotion-v1",
35 required_offline_gates=(
36 "schema_valid",
37 "no_leakage",
38 "critical_slice_pass",
39 "cost_improves",
40 ),
41 min_canary_windows=2,
42 max_error_rate=0.01,
43 max_p95_latency_ms=250,
44 max_late_warning_cost_delta=0.0,
45 )
46}
47
48registry = {
49 "delivery-risk-v0": Release(
50 "delivery-risk-v0",
51 "carrier-events-through-2026-04-30",
52 "eta-features-v1",
53 "delay-model-v0",
54 "eta-threshold-v1",
55 "eta-promotion-v1",
56 None,
57 ),
58 "delivery-risk-v1": Release(
59 "delivery-risk-v1",
60 "carrier-events-through-2026-05-31",
61 "eta-features-v1",
62 "delay-model-v1",
63 "eta-threshold-v1",
64 "eta-promotion-v1",
65 "delivery-risk-v0",
66 ),
67}
68aliases = {"production": "delivery-risk-v0"}
69offline_receipts: dict[str, OfflineReceipt] = {}
70
71print("registry:", list(registry))
72print("production:", aliases["production"])1registry: ['delivery-risk-v0', 'delivery-risk-v1']
2production: delivery-risk-v01def open_canary(candidate_id: str, gates: dict[str, bool]) -> OfflineReceipt:
2 candidate = registry.get(candidate_id)
3 production_before = aliases["production"]
4 failed = []
5 policy = POLICIES.get(candidate.promotion_policy_version) if candidate else None
6 if candidate is None:
7 failed.append("candidate_not_registered")
8 elif policy is None:
9 failed.append("promotion_policy_not_registered")
10 else:
11 failed.extend(
12 gate for gate in policy.required_offline_gates if not gates.get(gate, False)
13 )
14 if candidate is not None and candidate.previous_release != production_before:
15 failed.append("previous_release_mismatch")
16 if aliases.get("canary") not in (None, candidate_id):
17 failed.append("another_canary_is_active")
18
19 receipt = OfflineReceipt(
20 receipt_id=f"offline-receipt-{len(offline_receipts) + 1}",
21 candidate=candidate_id,
22 production_before=production_before,
23 promotion_policy_version=(
24 candidate.promotion_policy_version if candidate is not None else None
25 ),
26 failed_gates=tuple(sorted(failed)),
27 decision="hold_offline" if failed else "open_canary",
28 )
29 offline_receipts[receipt.receipt_id] = receipt
30 if receipt.decision == "open_canary":
31 aliases["canary"] = candidate_id
32 return receipt
33
34bad_gates = {
35 "schema_valid": True,
36 "no_leakage": True,
37 "critical_slice_pass": False,
38 "cost_improves": True,
39}
40good_gates = {**bad_gates, "critical_slice_pass": True}
41
42bad_offline_receipt = open_canary("delivery-risk-v1", bad_gates)
43accepted_offline_receipt = open_canary("delivery-risk-v1", good_gates)
44print(json.dumps(asdict(bad_offline_receipt), indent=2))
45print(json.dumps(asdict(accepted_offline_receipt), indent=2))
46print("aliases:", json.dumps(aliases, sort_keys=True))1{
2 "receipt_id": "offline-receipt-1",
3 "candidate": "delivery-risk-v1",
4 "production_before": "delivery-risk-v0",
5 "promotion_policy_version": "eta-promotion-v1",
6 "failed_gates": [
7 "critical_slice_pass"
8 ],
9 "decision": "hold_offline"
10}
11{
12 "receipt_id": "offline-receipt-2",
13 "candidate": "delivery-risk-v1",
14 "production_before": "delivery-risk-v0",
15 "promotion_policy_version": "eta-promotion-v1",
16 "failed_gates": [],
17 "decision": "open_canary"
18}
19aliases: {"canary": "delivery-risk-v1", "production": "delivery-risk-v0"}A failed offline slice leaves production unchanged. Passing evaluation gates opens only the canary alias. Each attempt appends an immutable receipt with candidate, production base, policy version, verdict, and reasons. Nothing in this cell can overwrite production.
Fast checks catch broken schemas, errors, and latency spikes. They can't prove prediction quality when labels arrive later. A canary rollout needs both kinds of evidence. The controller below stores each observation window inside an immutable receipt, refuses to promote after the first hour because late-delivery outcomes aren't ready yet, and handles an empty window list as a hold rather than crashing.
1@dataclass(frozen=True)
2class CanaryWindow:
3 window_id: str
4 release_id: str
5 observed_day: int
6 requests: int
7 error_rate: float
8 p95_latency_ms: int
9 delayed_labels_ready: bool
10 late_warning_cost_delta: float | None
11
12@dataclass(frozen=True)
13class CanaryReceipt:
14 receipt_id: str
15 candidate: str
16 production_before: str
17 offline_receipt_id: str
18 promotion_policy_version: str | None
19 windows: tuple[CanaryWindow, ...]
20 failed_gates: tuple[str, ...]
21 decision: str
22
23canary_receipts: dict[str, CanaryReceipt] = {}
24
25print("canary policy windows:", POLICIES["eta-promotion-v1"].min_canary_windows)1def evaluate_canary(
2 candidate_id: str,
3 offline_receipt_id: str,
4 windows: list[CanaryWindow],
5) -> CanaryReceipt:
6 candidate = registry.get(candidate_id)
7 policy = POLICIES.get(candidate.promotion_policy_version) if candidate else None
8 offline_receipt = offline_receipts.get(offline_receipt_id)
9 failed = []
10 abort_reasons = []
11 if candidate is None:
12 failed.append("candidate_not_registered")
13 abort_reasons.append("candidate_not_registered")
14 elif policy is None:
15 failed.append("promotion_policy_not_registered")
16 abort_reasons.append("promotion_policy_not_registered")
17 if aliases.get("canary") != candidate_id:
18 failed.append("canary_alias_missing")
19 if (
20 offline_receipt is None
21 or offline_receipt.candidate != candidate_id
22 or offline_receipt.decision != "open_canary"
23 ):
24 failed.append("accepted_offline_receipt_missing")
25 abort_reasons.append("accepted_offline_receipt_missing")
26 if candidate is not None and candidate.previous_release != aliases["production"]:
27 failed.append("production_changed_during_canary")
28 abort_reasons.append("production_changed_during_canary")
29 if policy is not None and len(windows) < policy.min_canary_windows:
30 failed.append("observation_window_incomplete")
31 if len({window.window_id for window in windows}) != len(windows):
32 failed.append("duplicate_window_id")
33 abort_reasons.append("duplicate_window_id")
34 observed_days = [window.observed_day for window in windows]
35 if observed_days != sorted(set(observed_days)):
36 failed.append("window_order_invalid")
37 abort_reasons.append("window_order_invalid")
38 if any(window.release_id != candidate_id for window in windows):
39 failed.append("mixed_release_windows")
40 abort_reasons.append("mixed_release_windows")
41 if any(window.requests <= 0 for window in windows):
42 failed.append("request_count_missing")
43 abort_reasons.append("request_count_missing")
44 if policy is not None and any(window.error_rate > policy.max_error_rate for window in windows):
45 failed.append("error_rate_regression")
46 abort_reasons.append("error_rate_regression")
47 if policy is not None and any(
48 window.p95_latency_ms > policy.max_p95_latency_ms for window in windows
49 ):
50 failed.append("latency_regression")
51 abort_reasons.append("latency_regression")
52
53 latest = windows[-1] if windows else None
54 if latest is None or not latest.delayed_labels_ready:
55 failed.append("delayed_quality_not_ready")
56 elif (
57 policy is not None
58 and (
59 latest.late_warning_cost_delta is None
60 or latest.late_warning_cost_delta > policy.max_late_warning_cost_delta
61 )
62 ):
63 failed.append("late_warning_cost_regression")
64 abort_reasons.append("late_warning_cost_regression")
65
66 decision = (
67 "abort_canary"
68 if abort_reasons
69 else "hold_canary"
70 if failed
71 else "ready_for_promotion"
72 )
73 receipt = CanaryReceipt(
74 receipt_id=f"canary-receipt-{len(canary_receipts) + 1}",
75 candidate=candidate_id,
76 production_before=aliases["production"],
77 offline_receipt_id=offline_receipt_id,
78 promotion_policy_version=(
79 candidate.promotion_policy_version if candidate is not None else None
80 ),
81 windows=tuple(windows),
82 failed_gates=tuple(sorted(failed)),
83 decision=decision,
84 )
85 canary_receipts[receipt.receipt_id] = receipt
86 if decision == "abort_canary":
87 aliases.pop("canary", None)
88 return receipt
89
90first_hour = CanaryWindow("first-hour", "delivery-risk-v1", 0, 500, 0.002, 118, False, None)
91day_seven = CanaryWindow("day-seven", "delivery-risk-v1", 7, 4200, 0.003, 124, True, -0.08)
92
93empty_receipt = evaluate_canary("delivery-risk-v1", accepted_offline_receipt.receipt_id, [])
94early_receipt = evaluate_canary(
95 "delivery-risk-v1", accepted_offline_receipt.receipt_id, [first_hour]
96)
97ready_receipt = evaluate_canary(
98 "delivery-risk-v1", accepted_offline_receipt.receipt_id, [first_hour, day_seven]
99)
100print("empty decision:", empty_receipt.decision)
101print("early decision:", early_receipt.decision)
102print("ready decision:", ready_receipt.decision)1print("empty:", json.dumps(asdict(empty_receipt), indent=2))
2print("early:", json.dumps(asdict(early_receipt), indent=2))
3print("ready:", json.dumps(asdict(ready_receipt), indent=2))1empty: {
2 "receipt_id": "canary-receipt-1",
3 "candidate": "delivery-risk-v1",
4 "production_before": "delivery-risk-v0",
5 "offline_receipt_id": "offline-receipt-2",
6 "promotion_policy_version": "eta-promotion-v1",
7 "windows": [],
8 "failed_gates": [
9 "delayed_quality_not_ready",
10 "observation_window_incomplete"
11 ],
12 "decision": "hold_canary"
13}
14early: {
15 "receipt_id": "canary-receipt-2",
16 "candidate": "delivery-risk-v1",
17 "production_before": "delivery-risk-v0",
18 "offline_receipt_id": "offline-receipt-2",
19 "promotion_policy_version": "eta-promotion-v1",
20 "windows": [
21 {
22 "window_id": "first-hour",
23 "release_id": "delivery-risk-v1",
24 "observed_day": 0,
25 "requests": 500,
26 "error_rate": 0.002,
27 "p95_latency_ms": 118,
28 "delayed_labels_ready": false,
29 "late_warning_cost_delta": null
30 }
31 ],
32 "failed_gates": [
33 "delayed_quality_not_ready",
34 "observation_window_incomplete"
35 ],
36 "decision": "hold_canary"
37}
38ready: {
39 "receipt_id": "canary-receipt-3",
40 "candidate": "delivery-risk-v1",
41 "production_before": "delivery-risk-v0",
42 "offline_receipt_id": "offline-receipt-2",
43 "promotion_policy_version": "eta-promotion-v1",
44 "windows": [
45 {
46 "window_id": "first-hour",
47 "release_id": "delivery-risk-v1",
48 "observed_day": 0,
49 "requests": 500,
50 "error_rate": 0.002,
51 "p95_latency_ms": 118,
52 "delayed_labels_ready": false,
53 "late_warning_cost_delta": null
54 },
55 {
56 "window_id": "day-seven",
57 "release_id": "delivery-risk-v1",
58 "observed_day": 7,
59 "requests": 4200,
60 "error_rate": 0.003,
61 "p95_latency_ms": 124,
62 "delayed_labels_ready": true,
63 "late_warning_cost_delta": -0.08
64 }
65 ],
66 "failed_gates": [],
67 "decision": "ready_for_promotion"
68}late_warning_cost_delta=-0.08 means the candidate reduced late-warning cost by eight percent relative to the previous release on this local fixture. It's a teaching threshold, not a universal production policy. Real teams choose windows and limits from product risk, traffic volume, and label delay.
An incomplete window returns hold_canary: gather more evidence without widening exposure. A measured latency, error-rate, or delayed-quality regression returns abort_canary and removes the canary alias. Corrupted or mismatched telemetry aborts too because the controller can't prove safe exposure. Missing evidence and negative evidence aren't the same operational state.
The final cell makes alias movement explicit. Promotion fetches a stored canary receipt by ID rather than trusting a caller-supplied decision dictionary. Rollback follows the same rule: production metrics become a stored receipt before they can restore the previous release. Both paths recheck the live production alias immediately before movement.
1@dataclass(frozen=True)
2class AliasEvent:
3 action: str
4 from_release: str
5 to_release: str
6 evidence_receipt_id: str
7 reasons: tuple[str, ...]
8
9@dataclass(frozen=True)
10class ProductionReceipt:
11 receipt_id: str
12 window: CanaryWindow
13 failed_gates: tuple[str, ...]
14 decision: str
15
16audit_events: list[AliasEvent] = []
17production_receipts: dict[str, ProductionReceipt] = {}
18
19def promote(candidate_id: str, canary_receipt_id: str) -> dict[str, object]:
20 candidate = registry.get(candidate_id)
21 canary_receipt = canary_receipts.get(canary_receipt_id)
22 failed = []
23 if candidate is None:
24 failed.append("candidate_not_registered")
25 if aliases.get("canary") != candidate_id:
26 failed.append("canary_alias_missing")
27 if canary_receipt is None:
28 failed.append("canary_receipt_not_registered")
29 elif canary_receipt.candidate != candidate_id:
30 failed.append("canary_receipt_candidate_mismatch")
31 elif canary_receipt.decision != "ready_for_promotion":
32 failed.append("canary_receipt_not_ready")
33 elif (
34 candidate is not None
35 and canary_receipt.promotion_policy_version != candidate.promotion_policy_version
36 ):
37 failed.append("canary_receipt_policy_mismatch")
38 elif (
39 offline_receipts.get(canary_receipt.offline_receipt_id) is None
40 or offline_receipts[canary_receipt.offline_receipt_id].decision != "open_canary"
41 ):
42 failed.append("offline_receipt_not_registered")
43 if candidate is not None and candidate.previous_release != aliases["production"]:
44 failed.append("production_changed_since_canary_open")
45 if canary_receipt is not None and canary_receipt.production_before != aliases["production"]:
46 failed.append("production_changed_since_canary_receipt")
47 if failed:
48 return {"action": "hold_promotion", "reasons": sorted(failed)}
49
50 previous = aliases["production"]
51 aliases["previous_production"] = previous
52 aliases["production"] = candidate_id
53 aliases.pop("canary")
54 event = AliasEvent("promote", previous, candidate_id, canary_receipt_id, ())
55 audit_events.append(event)
56 return asdict(event)
57
58print("promote helper ready")1def rollback_reasons(window: CanaryWindow) -> list[str]:
2 release = registry.get(window.release_id)
3 if release is None:
4 return ["production_window_release_not_registered"]
5 policy = POLICIES.get(release.promotion_policy_version)
6 if policy is None:
7 return ["production_policy_not_registered"]
8 failed = []
9 if window.error_rate > policy.max_error_rate:
10 failed.append("error_rate_regression")
11 if window.p95_latency_ms > policy.max_p95_latency_ms:
12 failed.append("latency_regression")
13 if not window.delayed_labels_ready:
14 failed.append("delayed_quality_not_ready")
15 elif (
16 window.late_warning_cost_delta is None
17 or window.late_warning_cost_delta > policy.max_late_warning_cost_delta
18 ):
19 failed.append("late_warning_cost_regression")
20 return failed
21
22def evaluate_production(window: CanaryWindow) -> ProductionReceipt:
23 failed = rollback_reasons(window)
24 release_mismatch = window.release_id != aliases["production"]
25 if release_mismatch:
26 failed.append("production_window_release_mismatch")
27 decision = (
28 "hold_rollback"
29 if release_mismatch
30 else "rollback_required"
31 if failed
32 else "keep_production"
33 )
34 receipt = ProductionReceipt(
35 receipt_id=f"production-receipt-{len(production_receipts) + 1}",
36 window=window,
37 failed_gates=tuple(sorted(failed)),
38 decision=decision,
39 )
40 production_receipts[receipt.receipt_id] = receipt
41 return receipt
42
43def rollback_if_needed(production_receipt_id: str) -> dict[str, object]:
44 receipt = production_receipts.get(production_receipt_id)
45 if receipt is None:
46 return {"action": "hold_rollback", "reason": "production_receipt_not_registered"}
47 if receipt.decision == "hold_rollback":
48 return {"action": "hold_rollback", "reasons": list(receipt.failed_gates)}
49 if receipt.window.release_id != aliases["production"]:
50 return {"action": "hold_rollback", "reason": "production_changed_since_monitor_receipt"}
51 if receipt.decision == "keep_production":
52 return {"action": "keep_production", "release": aliases["production"]}
53 if receipt.decision != "rollback_required":
54 return {"action": "hold_rollback", "reason": "production_receipt_decision_invalid"}
55
56 previous = aliases.get("previous_production")
57 if previous is None or previous not in registry:
58 return {"action": "hold_rollback", "reason": "previous_production_not_registered"}
59 failed_release = aliases["production"]
60 aliases["production"] = previous
61 aliases["rollback_from"] = failed_release
62 event = AliasEvent(
63 "rollback",
64 failed_release,
65 previous,
66 receipt.receipt_id,
67 receipt.failed_gates,
68 )
69 audit_events.append(event)
70 return asdict(event)
71
72print("rollback helper ready")1print("fabricated promotion:", promote("delivery-risk-v1", "canary-receipt-missing"))
2print("early promotion:", promote("delivery-risk-v1", early_receipt.receipt_id))
3print("approved promotion:", promote("delivery-risk-v1", ready_receipt.receipt_id))1degraded = CanaryWindow("production-day-eight", "delivery-risk-v1", 8, 900, 0.004, 130, True, 0.14)
2degraded_receipt = evaluate_production(degraded)
3rollback_result = rollback_if_needed(degraded_receipt.receipt_id)
4print("production decision:", degraded_receipt.decision)
5print("rollback action:", rollback_result["action"])1print("production receipt:", json.dumps(asdict(degraded_receipt), indent=2))
2print("rollback:", rollback_result)
3print("aliases:", json.dumps(aliases, sort_keys=True))
4print("audit:", json.dumps([asdict(event) for event in audit_events], indent=2))1production receipt: {
2 "receipt_id": "production-receipt-1",
3 "window": {
4 "window_id": "production-day-eight",
5 "release_id": "delivery-risk-v1",
6 "observed_day": 8,
7 "requests": 900,
8 "error_rate": 0.004,
9 "p95_latency_ms": 130,
10 "delayed_labels_ready": true,
11 "late_warning_cost_delta": 0.14
12 },
13 "failed_gates": [
14 "late_warning_cost_regression"
15 ],
16 "decision": "rollback_required"
17}
18rollback: {'action': 'rollback', 'from_release': 'delivery-risk-v1', 'to_release': 'delivery-risk-v0', 'evidence_receipt_id': 'production-receipt-1', 'reasons': ('late_warning_cost_regression',)}
19aliases: {"previous_production": "delivery-risk-v0", "production": "delivery-risk-v0", "rollback_from": "delivery-risk-v1"}
20audit: [
21 {
22 "action": "promote",
23 "from_release": "delivery-risk-v0",
24 "to_release": "delivery-risk-v1",
25 "evidence_receipt_id": "canary-receipt-3",
26 "reasons": []
27 },
28 {
29 "action": "rollback",
30 "from_release": "delivery-risk-v1",
31 "to_release": "delivery-risk-v0",
32 "evidence_receipt_id": "production-receipt-1",
33 "reasons": [
34 "late_warning_cost_regression"
35 ]
36 }
37]Rollback restores delivery-risk-v0, not its weights alone. That distinction matters because preprocessing, features, thresholds, and policy can all change serving behavior. Each audit event names the stored receipt that authorized its alias movement, so a later reviewer can reconstruct both promotion and rollback.
Live checks differ by product, but the promotion controller handles the same categories:
| Gate type | ETA | Ranking | Forecast | Vision |
|---|---|---|---|---|
| immediate data health | scan freshness | eligible candidate supply | latest counts loaded | photo quality |
| immediate service health | latency/errors | scoring latency | forecast API availability | image scoring latency |
| delayed quality | late warning cost | purchase/return experiment | MAE and peak residual | reviewer-confirmed damage |
| rollback event | stale warning spike | blocked listing exposure | broken alert flood | unsupported escalations |
For scoring systems with delayed labels, canary monitoring should pause wider promotion until enough outcomes arrive. A model that hasn't failed yet isn't the same as a model that has passed.
Continuous training is appropriate when a schedule or monitored condition creates a candidate run. It should never skip data validation, offline comparisons, or a promotion record. The pipeline's value isn't automation alone; it's refusing untraceable changes.
Run the runnable examples again after each mutation. Predict which receipt or alias changes before reading output.
delivery-risk-v1 constructor's previous_release from "delivery-risk-v0" to "delivery-risk-v-missing".good_gates["critical_slice_pass"] back to False.[day_seven, first_hour] to one evaluate_canary call. Confirm that the receipt records window_order_invalid.day_seven constructor's delayed_labels_ready argument from True to False.day_seven constructor's late_warning_cost_delta argument from -0.08 to 0.05.ready_receipt.receipt_id with "canary-receipt-missing" in the approved promotion call.aliases["production"] = "delivery-risk-v1" to simulate an out-of-band alias move. Confirm that promotion holds, then reset it to "delivery-risk-v0".degraded constructor's late_warning_cost_delta argument from 0.14 to -0.01.degraded constructor's release ID from "delivery-risk-v1" to "delivery-risk-v0". Confirm that monitoring evidence for a different release can't move the current alias.degraded_receipt.receipt_id with "production-receipt-missing" in the rollback call. Which authorization check holds the alias?| Artifact | Acceptance condition |
|---|---|
| release schema | identifies data, features, model, action policy, promotion policy, and previous release |
| registry | contains immutable stable and candidate releases |
| append-only receipts | record why each candidate passed, held, aborted, promoted, or rolled back |
| alias promotion code | fetches stored receipt and rechecks production before moving alias |
| monitor policy | defines canary pause, promote, abort, rollback |
| tests | execute empty-window, failed-gate, fabricated-receipt, alias-race, and rollback paths |
This completes the conventional production ML portfolio. The next capstone returns to LLM products: document QA must apply the same lineage and release discipline to retrieved evidence and generated answers.
| Artifact | Strong submission demonstrates |
|---|---|
| reproducible run | versioned data, features, model artifact, threshold policy, and evaluation evidence |
| controlled promotion | candidate alias, automated gates, canary criteria, and explicit production move |
| recovery | monitoring tied to actions, rollback trigger, and deployable prior release |
| Symptom | Cause | Fix |
|---|---|---|
| Retrain job changes behavior with no review | training and promotion merged | separate candidate registry from aliases |
| Rollback restores weights but not threshold | policy omitted from release bundle | version complete release tuple |
| Empty canary window crashes controller | latest observation indexed before evidence exists | return a stored hold receipt for missing evidence |
| Canary promotes before outcomes exist | only latency checked | require delayed quality window |
Fabricated ready dictionary promotes candidate | alias mover trusts caller-owned state | fetch immutable receipt by stored ID |
| Production changed after canary opened | rollback base checked only once | recheck current alias before promotion |
| Old monitoring window rolls back current release | rollback trusts reasons without release identity | verify monitor window matches production alias |
| Caller fabricates rollback reasons | alias mover accepts an unregistered reason list | evaluate monitoring once, store receipt, and fetch it by ID |
| Rollback points at an unknown state | candidate omits previous release | verify rollback target before opening canary traffic |
| Production changes but incident review has no history | alias moves aren't audited | append promotion and rollback events |
Answer every question, then check your score. Score above 75% to mark this lesson complete.
9 questions remaining.
Questions and insights from fellow learners.