Skip to content
Skip to lesson
Back to Topics
LearnPortfolio CapstonesCapstone: Production ML Pipeline
⚙️HardMLOps & Deployment

Capstone: Production ML Pipeline

Build a point-in-time training and reload path, then test offline gates, simulated canary decisions, promotion, and rollback.

46 min read
Learning path
Step 90 of 197 in the full curriculum

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

A shipping service wants to replace delivery-risk-v0. One candidate makes every test row cheaper, yet misses one of two late deliveries in the critical slice. A single passed flag could promote the cheaper model while hiding the exact cases the service must catch.

A successful HTTP response tells you that a request completed, not that its prediction was useful. Ordinary software can also produce plausible wrong answers without throwing an exception; an ML service additionally needs checks on its data, learned transformations, and labeled outcomes. The first candidate below fails one of those checks even while reducing the assumed cost.

Follow six handoffs in this local exercise:

  1. Validate record schemas, identities, clocks, and finite values.
  2. Select features observed and available by prediction time.
  3. Fit preprocessing and a small model on admitted training rows; save and reload both together.
  4. Derive slice recall and assumed cost from evaluation rows.
  5. Bind the results to retained artifact, release, and policy fingerprints.
  6. Use injected monitoring windows to exercise local canary, promotion, and rollback decisions.

These are the lesson's boundaries, not a mandatory six-stage architecture for every ML system. The dictionaries and model files remain editable. Fingerprints support integrity checks against trusted expected values; they don't make storage immutable, authenticate the evaluator, or implement distributed transactions.

The ETA, ranking, forecast, and vision capstones already produce pieces of this release. Reuse their train/validation split, preprocessing, metric, and persistence ideas here. The running example is delivery warnings; the other products help you identify what varies. All data and telemetry are synthetic. The lab performs real local fitting, serialization, reload, and controller transitions, but it doesn't deploy a service or measure a live canary.

Define the shared release tuple

Start with a release that can be restored as one unit. Models differ, but their manifest can share a schema. Read the fields before looking at implementation details:

FieldETA exampleRanking exampleForecast exampleVision example
data snapshotcarrier events through cutoffcatalog and judged queriesdaily counts through cutoffreturn photos grouped by shipment
feature or preprocessing versioneta-features-v1ranking-features-v1calendar-regression-v1parcel-rgb-224-policy-v1
model artifactdelay-model-v1market-ranker-v1warehouse-demand-v1damage-cnn-v1
action policywarning thresholdeligibility and slate rulealert thresholdquality check and review threshold
promotion policyslice recall and cost limitseligibility and limitscapacity-decision cost and forecast error limitsquality rejection and case-level review limits
monitordelayed labels and freshnessimpressions and returnsresiduals and alert reviewphoto quality and reviewer labels
previous releasedelivery-risk-v0market-ranker-v0warehouse-demand-v0damage-cnn-v0

If a threshold, crop, or gate policy is missing from the release, an incident reviewer can't reconstruct what served a request. Sculley et al. identify data dependencies, configuration, and feedback loops among sources of ML technical debt.[1] Restore the components relevant to the product together: an image cropper for vision, an imputer for tabular features, or a tokenizer for a text model. The table proposes a shared release schema; it does not claim the preceding synthetic capstones trained all four listed artifacts.

The tuple still doesn't grant traffic authority. An alias is a mutable pointer such as production or canary; only authorized controller code should move it. Limited live exposure (a ) follows offline gates. Rollback selects the previous tuple, not a fresh training run. In this lab, one versioned promotion policy also owns the canary and rollback thresholds; the model package contains the prediction threshold and a digest of the builder source.

Six local pipeline boundaries: typed records, two-clock feature selection, a saved training-fitted model, a failing recall slice, retained fingerprints, and simulated alias transitions. Hash comparisons establish local consistency; no traffic router or protected evidence store is supplied.
Trace the actual local handoffs. The feature selection and model reload run; monitoring values are injected, and alias changes affect only a Python dictionary.

Pause before the transition map: which boundary should reject a candidate with failed slice recall? Registration alone must never move production traffic.

Diagram showing Register v1 prod stays v0, Offline slice and cost gates, fail, and hold_offline.
Register v1 prod stays v0, Offline slice and cost gates, fail, and hold_offline.

Registration gives v1 an identity while the local production pointer stays on v0. An offline failure stops at hold_offline; a pass opens the local canary pointer. Supplied monitoring windows then exercise promotion and rollback. None of these pointers routes a real request.

Compute the failing slice by hand

Before any registry code, make the first decision by hand. delivery-risk-recall-fail is a deliberately bad candidate and the local production pointer selects delivery-risk-v0. These hand-entered rows isolate the gate's behavior; they're not outputs of the trained model we'll build next. Their cost columns are supplied units, independent of the warning-cost formula used later. The candidate is cheaper overall, but the critical slice contains the late deliveries that the warning policy exists to catch. Would you open a canary?

entitycritical slicelate deliverypredicted latebaseline costcandidate cost
eval-1yes112524
eval-2yes102524
eval-3no002524
eval-4no002524

Only the first two rows are late positives in the critical slice. Count those rows before looking at total cost. Critical-slice recall is true positives on late critical rows divided by all late critical rows:

critical-slice recall=12=0.50\text{critical-slice recall} = \frac{1}{2} = 0.50

The policy floor is 0.900.90, so recall fails at 0.500.50. Cost did improve: 96/100−1=−0.0496 / 100 - 1 = -0.04, which clears max_cost_delta = 0.

The cheaper cost still doesn't buy a canary. One required gate is enough to hold the candidate offline, store the failed evidence, and leave production on delivery-risk-v0. That is the decision the controller must preserve when someone later tries to pass a friendly summary instead of these rows.

Build the package before trusting its manifest

A manifest that says no_leakage=True isn't evidence. Start with prediction requests, carrier scans, and delayed outcomes. The runnable pipeline source implements the full local path using Python's standard library; save it as assets/production_pipeline.py alongside the notebook or script running these cells.

For eval-1, the request time is May 28 at 12:00 UTC. Three scans compete:

Scan observedScan available to the serviceDelayEligible at noon?
10:0011:009 minutesYes
11:3012:3099 minutesNo: arrived after prediction
13:0013:01100 minutesNo: future observation

The feature builder filters on both timestamps, then chooses the most recent eligible observation for that entity. Joining the latest scan today could insert information unavailable at yesterday's prediction. A backward as-of join selects an earlier or equal timestamp; it still needs sorted keys, entity grouping, availability filtering, duplicate handling, and any freshness policy.[2] This lab normalizes aware timestamps to UTC before rejecting same-instant scan revisions. A missing scan becomes a missing feature. It implements no maximum scan-age threshold; add and test one if the product requires it.

Two-clock point-in-time admission gate for eval-1 on May 28 UTC. The 10:00 scan available at 11:00 passes both clocks and is admitted with a 9-minute delay. The 11:30 scan available at 12:30 fails availability and is rejected to prevent ingestion lag leakage. The 13:00 scan fails both clocks and is rejected to prevent lookahead leakage.
The 11:30 observation looks historical on the event clock, but its 12:30 arrival makes it unavailable for a noon prediction. Dual-clock admission rejects it to prevent ingestion lag leakage.

Training requests precede May 25, and their outcome labels must also have arrived by May 25. Evaluation requests occur on May 28; their labels are available by the May 31 evaluation snapshot. Label time determines whether a row may be used for fitting or scoring. It never makes the label an input feature.

The six training delays are 0, 1, 2, 8, 9, 10. Fit the imputation mean and scale on those rows only: center 55, range 1010, transformed input z=(x−5)/10z=(x-5)/10. Here the divisor is the range, not a standard deviation. Computing these parameters from evaluation rows makes that split influence the estimator. Recomputing them on serving batches can also change a request's prediction when its batch membership changes. Save the training-fitted state and reuse it for evaluation and serving.[3]

A one-feature least-squares model fits a numeric score to the binary late label. The score isn't a calibrated probability; the fixed warning threshold is 0.50.5. No parameter or threshold is selected using the four evaluation labels. Scikit-learn's guidance applies equally to this hand-written estimator: fit transformations on training data, then reuse them unchanged for evaluation and serving.[3]

Trace the fit on these six rows. Their transformed features are (−0.5,−0.4,−0.3,0.3,0.4,0.5)(-0.5,-0.4,-0.3,0.3,0.4,0.5) and labels are (0,0,0,1,1,1)(0,0,0,1,1,1), so zˉ=0\bar z=0 and yˉ=0.5\bar y=0.5. The least-squares slope is ∑i(zi−zˉ)(yi−yˉ)/∑i(zi−zˉ)2=1.2/1.0=1.2\sum_i(z_i-\bar z)(y_i-\bar y)/\sum_i(z_i-\bar z)^2=1.2/1.0=1.2; the intercept is yˉ−1.2zˉ=0.5\bar y-1.2\bar z=0.5. The saved score therefore equals 0.5+1.2(x−5)/100.5+1.2(x-5)/10. At delays 9, 8, 1, and 0 it gives scores 0.98, 0.86, 0.02, and -0.10, which become warnings [1, 1, 0, 0]. That negative score is another reason to avoid calling this estimator a probability model.

A missing input is imputed to the training mean 5, giving score 0.5. The serving rule uses >= 0.5, so it warns at equality. This is the fixture's explicit fallback, not evidence that a missing scan predicts lateness. A real warning policy must evaluate missing-input cases and decide whether to warn, abstain, or use a separate fallback.

The JSON package stores feature names, fitted imputation and scaling, coefficients, threshold, training IDs, cutoff, a digest of the admitted training rows, builder-source digest, and Python version. The separate full-fixture digest covers the input records used for this retrospective lab, including later evaluation outcomes. Neither proves that the run occurred on those historical dates. With general estimators, persist fitted preprocessing with the estimator and record compatible dependencies; scikit-learn does not support cross-version loading and warns against untrusted pickle-based artifacts.[4] The numeric JSON loader checks both its expected digest and its own schema, finite parameters, positive scale, fixed threshold, and identities. A matching checksum alone cannot establish a valid model.

Run the pipeline and inspect its observable boundaries. The bad scans must not change eval-1 from 9 minutes to 99 or 100. Both serialized models must reproduce their pre-save predictions after reload.

00-build-and-reload.py
from assets.production_pipeline import (
    canonical_bytes, finite_number, fixture, load_bundle, nonblank,
    point_in_time_rows, predict, run_pipeline, timestamp,
)

built = run_pipeline("model-packages")
print("point-in-time eval-1:", built["evaluation"][0]["delay_minutes"])
print("training-only center:", built["packages"]["v1"]["bundle"]["center"])
print("reloaded predictions:", [row["predicted_late"] for row in built["evaluation"]])
print("baseline / candidate cost:",
      sum(row["baseline_cost"] for row in built["evaluation"]),
      sum(row["candidate_cost"] for row in built["evaluation"]))
assert built["evaluation"][0]["delay_minutes"] == 9
assert built["packages"]["v1"]["bundle"]["center"] == 5
Output
point-in-time eval-1: 9
training-only center: 5
reloaded predictions: [1, 1, 0, 0]
baseline / candidate cost: 50.0 4.0

Under the explicit cost policy, warning costs 2 units and missing a late delivery costs 25. An always-silent baseline costs 50 across two missed late deliveries; the candidate warns on both, costing 4. These are assumed costs on an easy synthetic fixture, not estimated savings. The fixture defines both critical_slice and late_delivery from the same delay threshold. A real critical cohort needs a product rule fixed before examining outcomes. Four separable rows demonstrate the plumbing, not generalization or production readiness.

Before registering anything, deliberately break the joins and serving contract. Duplicating an outcome must fail rather than multiply evaluation rows. Missing outcomes must not become negative labels. Corrupting saved bytes must fail before inference. Predict each rejection, then run the checks.

00-check-data-and-reload-boundaries.py
from hashlib import sha256
import json
from pathlib import Path

def expect_rejection(name, operation):
    try:
        operation()
    except ValueError:
        print(name + ": rejected")
    else:
        raise AssertionError(name + " unexpectedly passed")

requests, scans, outcomes = fixture()
expect_rejection("duplicate outcome", lambda: point_in_time_rows(
    requests, scans, outcomes + [outcomes[0]], "2026-05-31T00:00:00Z"))
expect_rejection("immature labels", lambda: point_in_time_rows(
    requests, scans, outcomes, "2026-05-29T00:00:00Z"))
expect_rejection("serving schema", lambda: predict(
    built["packages"]["v1"]["bundle"], {"delay_hours": 9}))
package = built["packages"]["v1"]
path = Path(package["path"])
saved = path.read_bytes()
path.write_bytes(saved + b" ")
expect_rejection("changed model bytes", lambda: load_bundle(path, package["sha256"]))
path.write_bytes(saved)
missing_prediction = predict(package["bundle"], {"delay_minutes": None})
assert missing_prediction == predict(package["bundle"], {"delay_minutes": 5})
print("missing input uses training mean:", missing_prediction)

requests, scans, outcomes = fixture()
same_instant = {**scans[6], "observed_at": "2026-05-28T10:00:00+00:00", "delay_minutes": 88}
expect_rejection("equivalent scan clocks", lambda: point_in_time_rows(
    requests, [same_instant] + scans, outcomes, "2026-05-31T00:00:00Z"))
expect_rejection("huge serving input", lambda: predict(package["bundle"], {"delay_minutes": 10**400}))
malformed_path = Path("model-packages/invalid.json")
for name, patch in (("checksum-valid zero scale", {"scale": 0}),
                    ("checksum-valid NaN coefficient", {"slope": float("nan")})):
    payload = json.dumps({**package["bundle"], **patch}).encode()
    malformed_path.write_bytes(payload)
    expect_rejection(name, lambda: load_bundle(malformed_path, sha256(payload).hexdigest()))
malformed_path.unlink()
assert package["bundle"]["source_sha256"] == built["training_sha256"]
assert built["training_sha256"] != built["source_sha256"]
Output
duplicate outcome: rejected
immature labels: rejected
serving schema: rejected
changed model bytes: rejected
missing input uses training mean: 1
equivalent scan clocks: rejected
huge serving input: rejected
checksum-valid zero scale: rejected
checksum-valid NaN coefficient: rejected

Register a candidate without moving production

Register the release tuple before deriving evidence. This example hashes a canonical JSON serialization of its dataclass values. The policy, artifact, and release fingerprints let later calls check whether those registered values still match the values used by the evaluator.

If the policy changes under the same ID, promotion detects the mismatch when it rechecks the stored bindings. If model bytes change, reload checks them against the registered expected digest. No background process invalidates receipts immediately. A writer who can alter both the evidence and trusted expectations can defeat these consistency checks. Production needs controlled writers, retained artifacts and receipts, and atomic alias updates; the dictionaries here implement none of that infrastructure. The helper named immutable_fingerprint computes a digest, not an immutability guarantee.

The first cell turns the four-row into registered artifacts and aliases. Run it, then check the output: four releases should exist while production remains on delivery-risk-v0. Registration prepares evidence; it doesn't authorize traffic.

01-register-release-registry.py
from dataclasses import asdict, dataclass, fields, replace
from hashlib import sha256
import json
from math import isfinite

@dataclass(frozen=True)
class PromotionPolicy:
    policy_id: str
    required_offline_gates: tuple[str, ...]
    required_schema: tuple[str, ...]
    min_critical_slice_recall: float
    max_cost_delta: float
    min_canary_windows: int
    max_error_rate: float
    max_p95_latency_ms: int
    max_late_warning_cost_delta: float

    def __post_init__(self):
        nonblank(self.policy_id, "policy_id")
        expected_gates = {"schema_valid", "no_leakage", "critical_slice_pass", "cost_improves"}
        if type(self.required_offline_gates) is not tuple:
            raise ValueError("required_offline_gates must be a tuple")
        for name in self.required_offline_gates:
            nonblank(name, "gate name")
        if len(self.required_offline_gates) != 4 or set(self.required_offline_gates) != expected_gates:
            raise ValueError("this controller requires all four offline gates")
        if type(self.required_schema) is not tuple or not self.required_schema:
            raise ValueError("required_schema must be a nonempty tuple")
        for name in self.required_schema:
            nonblank(name, "schema field")
        if len(set(self.required_schema)) != len(self.required_schema):
            raise ValueError("duplicate required schema field")
        for name in ("min_critical_slice_recall", "max_cost_delta", "max_error_rate",
                     "max_p95_latency_ms", "max_late_warning_cost_delta"):
            finite_number(getattr(self, name), name)
        if not 0 <= self.min_critical_slice_recall <= 1 or not 0 <= self.max_error_rate <= 1:
            raise ValueError("recall and error-rate limits must be in [0, 1]")
        if self.max_p95_latency_ms < 0 or self.max_cost_delta < -1 or self.max_late_warning_cost_delta < -1:
            raise ValueError("invalid latency or relative-cost limit")
        if type(self.min_canary_windows) is not int or self.min_canary_windows < 1:
            raise ValueError("min_canary_windows must be a positive integer")

@dataclass(frozen=True)
class OfflineEvalRow:
    entity_id: str
    critical_slice: bool
    late_delivery: int
    predicted_late: int
    baseline_cost: float
    candidate_cost: float
    event_time: str = "2026-05-28T12:00:00Z"

    def __post_init__(self):
        nonblank(self.entity_id, "entity_id")
        timestamp(self.event_time)
        if type(self.critical_slice) is not bool:
            raise ValueError("critical_slice must be Boolean")
        for name in ("late_delivery", "predicted_late"):
            value = getattr(self, name)
            if type(value) is not int or value not in (0, 1):
                raise ValueError(f"{name} must be an integer zero or one")
        for name in ("baseline_cost", "candidate_cost"):
            value = getattr(self, name)
            finite_number(value, name)
            if value < 0:
                raise ValueError(f"{name} must be nonnegative")

@dataclass(frozen=True)
class CandidateArtifacts:
    artifact_id: str
    data_snapshot: str
    feature_version: str
    model_artifact: str
    action_policy_version: str
    observed_schema: tuple[str, ...]
    training_entity_ids: tuple[str, ...]
    evaluation_entity_ids: tuple[str, ...]
    evaluation_rows: tuple[OfflineEvalRow, ...]

    def __post_init__(self):
        for name in ("artifact_id", "data_snapshot", "feature_version", "model_artifact", "action_policy_version"):
            nonblank(getattr(self, name), name)
        for name in ("observed_schema", "training_entity_ids", "evaluation_entity_ids"):
            values = getattr(self, name)
            if type(values) is not tuple:
                raise ValueError(f"{name} must be a tuple")
            for value in values:
                nonblank(value, name)
            if len(set(values)) != len(values):
                raise ValueError(f"duplicate value in {name}")
        if type(self.evaluation_rows) is not tuple or any(type(row) is not OfflineEvalRow for row in self.evaluation_rows):
            raise ValueError("evaluation_rows must be a tuple of validated OfflineEvalRow records")

@dataclass(frozen=True)
class Release:
    release_id: str
    data_snapshot: str
    feature_version: str
    model_artifact: str
    action_policy_version: str
    promotion_policy_version: str
    previous_release: str | None
    candidate_artifact_id: str
    candidate_artifact_fingerprint: str

@dataclass(frozen=True)
class OfflineEvidence:
    evidence_id: str
    candidate: str
    artifact_id: str
    artifact_fingerprint: str
    release_fingerprint: str
    promotion_policy_version: str
    promotion_policy_fingerprint: str
    missing_schema_fields: tuple[str, ...]
    leaked_entity_ids: tuple[str, ...]
    critical_slice_positives: int
    critical_slice_true_positives: int
    critical_slice_recall: float | None
    cost_delta: float | None

    def gates(self, policy: PromotionPolicy) -> dict[str, bool]:
        return {
            "schema_valid": not self.missing_schema_fields,
            "no_leakage": not self.leaked_entity_ids,
            "critical_slice_pass": (
                self.critical_slice_positives > 0
                and self.critical_slice_recall >= policy.min_critical_slice_recall
            ),
            "cost_improves": self.cost_delta is not None and self.cost_delta <= policy.max_cost_delta,
        }

@dataclass(frozen=True)
class OfflineReceipt:
    receipt_id: str
    candidate: str
    production_before: str
    promotion_policy_version: str | None
    promotion_policy_fingerprint: str | None
    offline_evidence_id: str | None
    release_fingerprint: str | None
    artifact_fingerprint: str | None
    failed_gates: tuple[str, ...]
    decision: str

def immutable_fingerprint(value: object) -> str:
    payload = json.dumps(asdict(value), sort_keys=True, separators=(",", ":"), allow_nan=False)
    return sha256(payload.encode("utf-8")).hexdigest()

POLICIES = {
    "eta-promotion-v1": PromotionPolicy(
        policy_id="eta-promotion-v1",
        required_offline_gates=(
            "schema_valid",
            "no_leakage",
            "critical_slice_pass",
            "cost_improves",
        ),
        required_schema=(
            "entity_id",
            "event_time",
            "critical_slice",
            "late_delivery",
            "predicted_late",
            "baseline_cost",
            "candidate_cost",
        ),
        min_critical_slice_recall=0.90,
        max_cost_delta=0.0,
        min_canary_windows=2,
        max_error_rate=0.01,
        max_p95_latency_ms=250,
        max_late_warning_cost_delta=0.0,
    )
}

stable_artifact = CandidateArtifacts(
    artifact_id="eta-artifact-v0",
    data_snapshot=built["source_sha256"],
    feature_version="eta-features-v1",
    model_artifact=built["packages"]["v0"]["sha256"],
    action_policy_version="eta-threshold-v1",
    observed_schema=POLICIES["eta-promotion-v1"].required_schema,
    training_entity_ids=tuple(row["entity_id"] for row in built["training"]),
    evaluation_entity_ids=(),
    evaluation_rows=(),
)
recall_fail_artifact = CandidateArtifacts(
    artifact_id="eta-artifact-recall-fail",
    data_snapshot="carrier-events-through-2026-05-31",
    feature_version="eta-features-v1",
    model_artifact="delay-model-recall-fail",
    action_policy_version="eta-threshold-v1",
    observed_schema=POLICIES["eta-promotion-v1"].required_schema,
    training_entity_ids=("train-1", "train-2"),
    evaluation_entity_ids=("eval-1", "eval-2", "eval-3", "eval-4"),
    evaluation_rows=(
        OfflineEvalRow("eval-1", True, 1, 1, 25.0, 24.0),
        OfflineEvalRow("eval-2", True, 1, 0, 25.0, 24.0),
        OfflineEvalRow("eval-3", False, 0, 0, 25.0, 24.0),
        OfflineEvalRow("eval-4", False, 0, 0, 25.0, 24.0),
    ),
)
leaky_artifact = CandidateArtifacts(
    artifact_id="eta-artifact-leaky",
    data_snapshot="carrier-events-through-2026-05-31",
    feature_version="eta-features-v1",
    model_artifact="delay-model-leaky",
    action_policy_version="eta-threshold-v1",
    observed_schema=POLICIES["eta-promotion-v1"].required_schema,
    training_entity_ids=("train-1", "eval-1"),
    evaluation_entity_ids=("eval-1", "eval-2", "eval-3", "eval-4"),
    evaluation_rows=(
        OfflineEvalRow("eval-1", True, 1, 1, 25.0, 24.0),
        OfflineEvalRow("eval-2", True, 1, 1, 25.0, 24.0),
        OfflineEvalRow("eval-3", False, 0, 0, 25.0, 24.0),
        OfflineEvalRow("eval-4", False, 0, 0, 25.0, 24.0),
    ),
)
passing_artifact = CandidateArtifacts(
    artifact_id="eta-artifact-v1",
    data_snapshot=built["source_sha256"],
    feature_version="eta-features-v1",
    model_artifact=built["packages"]["v1"]["sha256"],
    action_policy_version="eta-threshold-v1",
    observed_schema=POLICIES["eta-promotion-v1"].required_schema,
    training_entity_ids=tuple(row["entity_id"] for row in built["training"]),
    evaluation_entity_ids=("eval-1", "eval-2", "eval-3", "eval-4"),
    evaluation_rows=tuple(
        OfflineEvalRow(row["entity_id"], row["critical_slice"], row["late_delivery"],
                       row["predicted_late"], row["baseline_cost"], row["candidate_cost"],
                       row["predict_at"])
        for row in built["evaluation"]
    ),
)
artifacts = {
    artifact.artifact_id: artifact
    for artifact in (
        stable_artifact,
        recall_fail_artifact,
        leaky_artifact,
        passing_artifact,
    )
}

def release_from_artifact(
    release_id: str,
    artifact: CandidateArtifacts,
    previous_release: str | None,
) -> Release:
    return Release(
        release_id=release_id,
        data_snapshot=artifact.data_snapshot,
        feature_version=artifact.feature_version,
        model_artifact=artifact.model_artifact,
        action_policy_version=artifact.action_policy_version,
        promotion_policy_version="eta-promotion-v1",
        previous_release=previous_release,
        candidate_artifact_id=artifact.artifact_id,
        candidate_artifact_fingerprint=immutable_fingerprint(artifact),
    )

registry = {
    "delivery-risk-v0": release_from_artifact(
        "delivery-risk-v0", stable_artifact, None
    ),
    "delivery-risk-recall-fail": release_from_artifact(
        "delivery-risk-recall-fail", recall_fail_artifact, "delivery-risk-v0"
    ),
    "delivery-risk-leaky": release_from_artifact(
        "delivery-risk-leaky", leaky_artifact, "delivery-risk-v0"
    ),
    "delivery-risk-v1": release_from_artifact(
        "delivery-risk-v1", passing_artifact, "delivery-risk-v0"
    ),
}
aliases = {"production": "delivery-risk-v0"}
offline_evidence: dict[str, OfflineEvidence] = {}
offline_receipts: dict[str, OfflineReceipt] = {}
active_offline_receipts: dict[str, str] = {}

def reload_release(release_id: str):
    release = registry[release_id]
    package = next((item for item in built["packages"].values()
                    if item["sha256"] == release.model_artifact), None)
    if package is None:
        raise ValueError("release has no loadable model package")
    return load_bundle(package["path"], release.model_artifact)

print("registry:", list(registry))
print("production:", aliases["production"])
print(
    "policy fingerprint bound:",
    len(immutable_fingerprint(POLICIES["eta-promotion-v1"])) == 64,
)
Output
registry: ['delivery-risk-v0', 'delivery-risk-recall-fail', 'delivery-risk-leaky', 'delivery-risk-v1']
production: delivery-risk-v0
policy fingerprint bound: True

The registry now contains candidates, but it contains no verdict about their rows. Predict what should happen when evaluation sees a recall failure, a leakage failure, and a clean candidate.

evaluate_candidate checks the registered row schema and split membership, derives recall and cost, and binds the result to artifact, release, and policy fingerprints. The trained candidate's rows came from the reload path; the two injected failures remain controller fixtures. In a real system, the evaluator must own inference and dataset access, not accept caller-written predictions as independent evidence.

02-evaluate-candidate-offline.py
def release_matches_artifact(
    release: Release,
    artifact: CandidateArtifacts,
) -> bool:
    return (
        release.data_snapshot,
        release.feature_version,
        release.model_artifact,
        release.action_policy_version,
    ) == (
        artifact.data_snapshot,
        artifact.feature_version,
        artifact.model_artifact,
        artifact.action_policy_version,
    )

def evidence_binding_failures(
    candidate: Release,
    evidence: OfflineEvidence,
) -> list[str]:
    failed: list[str] = []
    policy = POLICIES.get(candidate.promotion_policy_version)
    artifact = artifacts.get(candidate.candidate_artifact_id)
    if evidence.candidate != candidate.release_id:
        failed.append("offline_evidence_candidate_mismatch")
    if evidence.promotion_policy_version != candidate.promotion_policy_version:
        failed.append("offline_evidence_policy_version_mismatch")
    if (
        policy is None
        or evidence.promotion_policy_fingerprint != immutable_fingerprint(policy)
    ):
        failed.append("offline_evidence_policy_fingerprint_mismatch")
    if evidence.release_fingerprint != immutable_fingerprint(candidate):
        failed.append("offline_evidence_release_fingerprint_mismatch")
    if evidence.artifact_id != candidate.candidate_artifact_id:
        failed.append("offline_evidence_artifact_id_mismatch")
    if evidence.artifact_fingerprint != candidate.candidate_artifact_fingerprint:
        failed.append("offline_evidence_artifact_fingerprint_mismatch")
    if (
        artifact is None
        or immutable_fingerprint(artifact)
        != candidate.candidate_artifact_fingerprint
    ):
        failed.append("registered_artifact_fingerprint_mismatch")
    elif not release_matches_artifact(candidate, artifact):
        failed.append("release_artifact_tuple_mismatch")
    return failed

def evaluate_candidate(candidate_id: str) -> OfflineEvidence:
    candidate = registry[candidate_id]
    policy = POLICIES[candidate.promotion_policy_version]
    artifact = artifacts[candidate.candidate_artifact_id]
    if immutable_fingerprint(artifact) != candidate.candidate_artifact_fingerprint:
        raise ValueError("registered artifact changed after release registration")
    if not release_matches_artifact(candidate, artifact):
        raise ValueError("release tuple does not match candidate artifact")

    actual_schema = {field.name for field in fields(OfflineEvalRow)}
    missing_schema = tuple(sorted(set(policy.required_schema) - actual_schema))
    row_ids = [row.entity_id for row in artifact.evaluation_rows]
    if len(row_ids) != len(set(row_ids)) or set(row_ids) != set(artifact.evaluation_entity_ids):
        raise ValueError("evaluation row IDs do not match the declared split")
    for row in artifact.evaluation_rows:
        if row.late_delivery not in (0, 1) or row.predicted_late not in (0, 1):
            raise ValueError("evaluation labels and predictions must be binary")
        if any(not isfinite(value) or value < 0 for value in (row.baseline_cost, row.candidate_cost)):
            raise ValueError("evaluation costs must be finite and nonnegative")
    leaked_entities = tuple(
        sorted(
            set(artifact.training_entity_ids)
            & set(artifact.evaluation_entity_ids)
        )
    )
    critical_positives = [
        row for row in artifact.evaluation_rows
        if row.critical_slice and row.late_delivery == 1
    ]
    critical_true_positives = sum(
        row.predicted_late == 1 for row in critical_positives
    )
    critical_recall = (
        critical_true_positives / len(critical_positives)
        if critical_positives
        else None
    )
    baseline_cost = sum(row.baseline_cost for row in artifact.evaluation_rows)
    candidate_cost = sum(row.candidate_cost for row in artifact.evaluation_rows)
    finite_number(baseline_cost, "total baseline cost")
    finite_number(candidate_cost, "total candidate cost")
    cost_delta = (
        candidate_cost / baseline_cost - 1.0
        if baseline_cost > 0
        else None
    )
    if cost_delta is not None:
        finite_number(cost_delta, "relative cost delta")

    evidence = OfflineEvidence(
        evidence_id=f"offline-evidence-{len(offline_evidence) + 1}",
        candidate=candidate_id,
        artifact_id=artifact.artifact_id,
        artifact_fingerprint=immutable_fingerprint(artifact),
        release_fingerprint=immutable_fingerprint(candidate),
        promotion_policy_version=policy.policy_id,
        promotion_policy_fingerprint=immutable_fingerprint(policy),
        missing_schema_fields=missing_schema,
        leaked_entity_ids=leaked_entities,
        critical_slice_positives=len(critical_positives),
        critical_slice_true_positives=critical_true_positives,
        critical_slice_recall=critical_recall,
        cost_delta=cost_delta,
    )
    offline_evidence[evidence.evidence_id] = evidence
    return evidence

failing_evidence = evaluate_candidate("delivery-risk-recall-fail")
leaky_evidence = evaluate_candidate("delivery-risk-leaky")
passing_evidence = evaluate_candidate("delivery-risk-v1")
policy = POLICIES["eta-promotion-v1"]
print("failing gates:", failing_evidence.gates(policy))
print("leaky gates:", leaky_evidence.gates(policy))
print("passing gates:", passing_evidence.gates(policy))
print("stored evidence ids:", list(offline_evidence))
Output
failing gates: {'schema_valid': True, 'no_leakage': True, 'critical_slice_pass': False, 'cost_improves': True}
leaky gates: {'schema_valid': True, 'no_leakage': False, 'critical_slice_pass': True, 'cost_improves': True}
passing gates: {'schema_valid': True, 'no_leakage': True, 'critical_slice_pass': True, 'cost_improves': True}
stored evidence ids: ['offline-evidence-1', 'offline-evidence-2', 'offline-evidence-3']
03-open-canary-after-offline-gates.py
def open_canary(candidate_id: str, offline_evidence_id: str) -> OfflineReceipt:
    candidate = registry.get(candidate_id)
    production_before = aliases["production"]
    failed = []
    policy = POLICIES.get(candidate.promotion_policy_version) if candidate else None
    evidence = offline_evidence.get(offline_evidence_id)
    if candidate is None:
        failed.append("candidate_not_registered")
    elif policy is None:
        failed.append("promotion_policy_not_registered")
    elif evidence is None:
        failed.append("offline_evidence_not_registered")
    else:
        failed.extend(evidence_binding_failures(candidate, evidence))
        gates = evidence.gates(policy)
        failed.extend(
            gate for gate in policy.required_offline_gates if not gates.get(gate, False)
        )
    if candidate is not None and candidate.previous_release != production_before:
        failed.append("previous_release_mismatch")
    if aliases.get("canary") not in (None, candidate_id):
        failed.append("another_canary_is_active")

    receipt = OfflineReceipt(
        receipt_id=f"offline-receipt-{len(offline_receipts) + 1}",
        candidate=candidate_id,
        production_before=production_before,
        promotion_policy_version=(
            candidate.promotion_policy_version if candidate is not None else None
        ),
        promotion_policy_fingerprint=(
            evidence.promotion_policy_fingerprint if evidence is not None else None
        ),
        offline_evidence_id=offline_evidence_id if evidence is not None else None,
        release_fingerprint=(
            evidence.release_fingerprint if evidence is not None else None
        ),
        artifact_fingerprint=(
            evidence.artifact_fingerprint if evidence is not None else None
        ),
        failed_gates=tuple(sorted(failed)),
        decision="hold_offline" if failed else "open_canary",
    )
    offline_receipts[receipt.receipt_id] = receipt
    if receipt.decision == "open_canary":
        aliases["canary"] = candidate_id
        active_offline_receipts[candidate_id] = receipt.receipt_id
    return receipt

bad_offline_receipt = open_canary(
    "delivery-risk-recall-fail", failing_evidence.evidence_id
)
accepted_offline_receipt = open_canary("delivery-risk-v1", passing_evidence.evidence_id)
print("failed:", bad_offline_receipt.decision, bad_offline_receipt.failed_gates)
print("accepted:", accepted_offline_receipt.decision, accepted_offline_receipt.offline_evidence_id)
print("aliases:", json.dumps(aliases, sort_keys=True))
Output
failed: hold_offline ('critical_slice_pass',)
accepted: open_canary offline-evidence-3
aliases: {"canary": "delivery-risk-v1", "production": "delivery-risk-v0"}

A failed offline slice leaves production unchanged. The gate named no_leakage checks only train/evaluation entity overlap. It doesn't detect future features or related physical cases assigned different IDs. The builder checks its supplied feature and label clocks and fits preprocessing on training rows; it has no real parcel identity audit. The three evidence records distinguish a hand-entered recall failure, an overlap failure, and the candidate built from reloaded predictions. No positive critical cases means recall is None; zero baseline cost makes the relative cost delta None. Their gates hold rather than invent evidence from a zero denominator.

Now the controller can decide whether limited traffic may open. open_canary receives an evidence ID, follows it back to the registered candidate, and records candidate, production base, policy, release, and artifact fingerprints. Passing opens only the canary alias. Nothing in this cell can overwrite production.

The printed aliases now confirm that the bad candidate stayed offline and the trained candidate opened only canary. An ID selects stored evidence; it isn't a security credential. Only the trusted controller may create those records or mutate aliases.

Wait for canary evidence

Offline evidence answers one question: does the registered candidate clear static gates? It can't answer whether late deliveries cost more after real requests arrive. Fast checks catch broken schemas, errors, and latency spikes; delayed labels answer the quality question later. A canary needs both streams.

A canary limits initial exposure while checking a new release. An uses an assignment and analysis design to estimate an outcome difference. A canary can use randomized assignment and health aborts within an A/B experiment; the distinction is the question the evidence can answer, not mutually exclusive infrastructure. Google Cloud places these online checks after offline validation.[5]

Health and labeled outcomes can arrive on different schedules. A carrier's late-delivery label needs a defined event and availability rule; it need not wait for eventual delivery if a missed deadline already establishes lateness. The injected windows use three states under this authored policy:

  • hold_canary: service checks pass, but there are too few windows or the latest delayed-quality metric is missing. Keep the local canary pointer and gather evidence.
  • abort_canary: any supplied window breaches a service limit, a mature window breaches the cost limit, or bindings or telemetry are invalid. Remove this candidate's local canary pointer.
  • ready_for_promotion: required window, binding, and metric checks pass. This permits a later local promotion call, not a real deployment.

The example limits are 250 ms, 1% errors, at least two ordered windows, and a nonpositive relative cost delta. They are policy assumptions, not measured service-level agreements or statistically justified acceptance thresholds.

Here, the controller tests that decision logic using supplied synthetic summaries. It doesn't route requests, wait seven days, collect latency, or join production labels. A real monitor must join request ID, served release, prediction time, and outcome availability, then calculate rates over a stated cohort. It also needs minimum sample counts, label coverage, freshness limits, and uncertainty criteria chosen before the rollout. A comparison of two summaries alone isn't a causal cost estimate.

Start with the receipt contract. Before any metric is evaluated, notice which facts must travel with a window: release ID, observed day, request count, service health, and delayed-label state. Predict the decision for no windows and for one unlabeled hour.

04-canary-window-contract.py
@dataclass(frozen=True)
class CanaryWindow:
    window_id: str
    release_id: str
    observed_day: int
    requests: int
    error_rate: float
    p95_latency_ms: int
    delayed_labels_ready: bool
    late_warning_cost_delta: float | None

    def __post_init__(self):
        nonblank(self.window_id, "window_id")
        nonblank(self.release_id, "release_id")

@dataclass(frozen=True)
class CanaryReceipt:
    receipt_id: str
    candidate: str
    production_before: str
    offline_receipt_id: str
    promotion_policy_version: str | None
    promotion_policy_fingerprint: str | None
    release_fingerprint: str | None
    artifact_fingerprint: str | None
    windows: tuple[CanaryWindow, ...]
    failed_gates: tuple[str, ...]
    decision: str

canary_receipts: dict[str, CanaryReceipt] = {}
latest_canary_receipts: dict[str, str] = {}

def finite_metric(value) -> bool:
    try:
        finite_number(value, "metric")
    except ValueError:
        return False
    return True

def valid_window(window: CanaryWindow) -> bool:
    return (
        type(window) is CanaryWindow
        and type(window.requests) is int and window.requests > 0
        and type(window.observed_day) is int and window.observed_day >= 0
        and finite_metric(window.error_rate) and 0 <= window.error_rate <= 1
        and finite_metric(window.p95_latency_ms) and window.p95_latency_ms >= 0
        and (window.late_warning_cost_delta is None
             or finite_metric(window.late_warning_cost_delta) and window.late_warning_cost_delta >= -1)
        and type(window.delayed_labels_ready) is bool
    )

print("canary policy windows:", POLICIES["eta-promotion-v1"].min_canary_windows)

The contract exists, but it has no observations yet. Evaluation will now receive the accepted offline receipt plus an ordered list of windows. It can abort on corrupted or measured-negative evidence, but it must hold when the list is too short or labels are still missing.

05-evaluate-canary-evidence.py
def accepted_offline_binding_failures(
    candidate: Release,
    receipt: OfflineReceipt,
) -> list[str]:
    failed: list[str] = []
    policy = POLICIES.get(candidate.promotion_policy_version)
    evidence = (
        offline_evidence.get(receipt.offline_evidence_id)
        if receipt.offline_evidence_id is not None
        else None
    )
    if receipt.candidate != candidate.release_id:
        failed.append("offline_receipt_candidate_mismatch")
    if active_offline_receipts.get(candidate.release_id) != receipt.receipt_id:
        failed.append("offline_receipt_superseded")
    if receipt.decision != "open_canary":
        failed.append("offline_receipt_not_accepted")
    if receipt.promotion_policy_version != candidate.promotion_policy_version:
        failed.append("offline_receipt_policy_version_mismatch")
    if (
        policy is None
        or receipt.promotion_policy_fingerprint != immutable_fingerprint(policy)
    ):
        failed.append("offline_receipt_policy_fingerprint_mismatch")
    if receipt.release_fingerprint != immutable_fingerprint(candidate):
        failed.append("offline_receipt_release_fingerprint_mismatch")
    if receipt.artifact_fingerprint != candidate.candidate_artifact_fingerprint:
        failed.append("offline_receipt_artifact_fingerprint_mismatch")
    if evidence is None:
        failed.append("offline_evidence_not_registered")
    else:
        failed.extend(evidence_binding_failures(candidate, evidence))
    return failed

def evaluate_canary(
    candidate_id: str,
    offline_receipt_id: str,
    windows: list[CanaryWindow],
) -> CanaryReceipt:
    if type(windows) is not list or any(type(window) is not CanaryWindow for window in windows):
        raise ValueError("windows must be a list of CanaryWindow records")
    candidate = registry.get(candidate_id)
    policy = POLICIES.get(candidate.promotion_policy_version) if candidate else None
    offline_receipt = offline_receipts.get(offline_receipt_id)
    failed = []
    abort_reasons = []
    if candidate is None:
        failed.append("candidate_not_registered")
        abort_reasons.append("candidate_not_registered")
    elif policy is None:
        failed.append("promotion_policy_not_registered")
        abort_reasons.append("promotion_policy_not_registered")
    if aliases.get("canary") != candidate_id:
        failed.append("canary_alias_missing")
    if offline_receipt is None or candidate is None:
        failed.append("accepted_offline_receipt_missing")
        abort_reasons.append("accepted_offline_receipt_missing")
    else:
        binding_failures = accepted_offline_binding_failures(
            candidate, offline_receipt
        )
        failed.extend(binding_failures)
        abort_reasons.extend(binding_failures)
    if (
        candidate is not None
        and candidate.previous_release != aliases["production"]
    ):
        failed.append("production_changed_during_canary")
        abort_reasons.append("production_changed_during_canary")
    if policy is not None and len(windows) < policy.min_canary_windows:
        failed.append("observation_window_incomplete")
    if len({window.window_id for window in windows}) != len(windows):
        failed.append("duplicate_window_id")
        abort_reasons.append("duplicate_window_id")
    observed_days = [window.observed_day for window in windows if valid_window(window)]
    if observed_days != sorted(set(observed_days)):
        failed.append("window_order_invalid")
        abort_reasons.append("window_order_invalid")
    if any(window.release_id != candidate_id for window in windows):
        failed.append("mixed_release_windows")
        abort_reasons.append("mixed_release_windows")
    if any(not valid_window(window) for window in windows):
        failed.append("invalid_window_metrics")
        abort_reasons.append("invalid_window_metrics")
    if any(type(window.requests) is not int or window.requests <= 0 for window in windows):
        failed.append("request_count_missing")
        abort_reasons.append("request_count_missing")
    if policy is not None and any(valid_window(window) and window.error_rate > policy.max_error_rate for window in windows):
        failed.append("error_rate_regression")
        abort_reasons.append("error_rate_regression")
    if policy is not None and any(
        valid_window(window) and window.p95_latency_ms > policy.max_p95_latency_ms for window in windows
    ):
        failed.append("latency_regression")
        abort_reasons.append("latency_regression")

    latest = windows[-1] if windows else None
    if latest is None or not latest.delayed_labels_ready or latest.late_warning_cost_delta is None:
        failed.append("delayed_quality_not_ready")
    if policy is not None and any(
        valid_window(window) and window.delayed_labels_ready
        and window.late_warning_cost_delta is not None
        and window.late_warning_cost_delta > policy.max_late_warning_cost_delta
        for window in windows
    ):
        failed.append("late_warning_cost_regression")
        abort_reasons.append("late_warning_cost_regression")

    decision = (
        "abort_canary"
        if abort_reasons
        else "hold_canary"
        if failed
        else "ready_for_promotion"
    )
    receipt = CanaryReceipt(
        receipt_id=f"canary-receipt-{len(canary_receipts) + 1}",
        candidate=candidate_id,
        production_before=aliases["production"],
        offline_receipt_id=offline_receipt_id,
        promotion_policy_version=(
            candidate.promotion_policy_version if candidate is not None else None
        ),
        promotion_policy_fingerprint=(
            offline_receipt.promotion_policy_fingerprint
            if offline_receipt is not None
            else None
        ),
        release_fingerprint=(
            offline_receipt.release_fingerprint
            if offline_receipt is not None
            else None
        ),
        artifact_fingerprint=(
            offline_receipt.artifact_fingerprint
            if offline_receipt is not None
            else None
        ),
        windows=tuple(windows),
        failed_gates=tuple(sorted(failed)),
        decision=decision,
    )
    canary_receipts[receipt.receipt_id] = receipt
    latest_canary_receipts[candidate_id] = receipt.receipt_id
    if decision == "abort_canary" and aliases.get("canary") == candidate_id:
        aliases.pop("canary", None)
    return receipt

first_hour = CanaryWindow("first-hour", "delivery-risk-v1", 0, 500, 0.002, 118, False, None)
day_seven = CanaryWindow("day-seven", "delivery-risk-v1", 7, 4200, 0.003, 124, True, -0.08)

empty_receipt = evaluate_canary("delivery-risk-v1", accepted_offline_receipt.receipt_id, [])
early_receipt = evaluate_canary(
    "delivery-risk-v1", accepted_offline_receipt.receipt_id, [first_hour]
)
ready_receipt = evaluate_canary(
    "delivery-risk-v1", accepted_offline_receipt.receipt_id, [first_hour, day_seven]
)
print("empty decision:", empty_receipt.decision)
print("early decision:", early_receipt.decision)
print("ready decision:", ready_receipt.decision)

The evaluator has created three receipts. Print them before interpreting the states: the empty list, the first hour, and the complete pair test different evidence boundaries.

06-print-canary-receipts.py
print("empty:", empty_receipt.decision, empty_receipt.failed_gates)
print("early:", early_receipt.decision, early_receipt.failed_gates)
print(
    "ready:",
    ready_receipt.decision,
    [window.window_id for window in ready_receipt.windows],
    ready_receipt.windows[-1].late_warning_cost_delta,
)
Output
empty: hold_canary ('delayed_quality_not_ready', 'observation_window_incomplete')
early: hold_canary ('delayed_quality_not_ready', 'observation_window_incomplete')
ready: ready_for_promotion ['first-hour', 'day-seven'] -0.08

late_warning_cost_delta=-0.08 is an injected eight-percent improvement for exercising the controller. It isn't computed from the four offline rows, whose assumed cost change was 4/50−1=−0.924/50-1=-0.92. Offline and online cohorts differ; real canary metrics must be derived from actual assigned traffic and mature outcomes. The zero-regression threshold here is a policy assumption, not a universal rule.

The empty and first-hour cases return hold_canary. The complete pair returns ready_for_promotion because service limits pass, the latest quality metric is present, and no supplied mature window has a cost regression. A later good delta cannot hide an earlier failing window in the same evaluation history.

A supplied latency, error-rate, or mature cost regression returns abort_canary and removes this candidate's local canary pointer. Invalid metrics or mismatched bindings also abort. Wrong record types are rejected before evaluation. Argo distinguishes successful, failed, and inconclusive analysis runs, which can continue, abort, or pause a rollout.[6] A missing measurement and a failing measurement remain different states.

Promote last, then prove rollback

Canary readiness still isn't an alias move. Promotion fetches a stored receipt, follows it to offline evidence, recomputes policy, release, and artifact bindings, and checks the current production pointer. This is a sequential state check, not an atomic compare-and-swap. A changed pointer holds promotion; concurrent writers and a pointer that moves away and back require the generations and transaction described below.

Rollback selects a retained previous release. It doesn't fit new weights. Reload time, runtime readiness, routing convergence, and external feature compatibility determine how long recovery takes; there is no instantaneous or millisecond guarantee here. The next cells exercise missing and early receipts, a local promotion, and a cost-regression rollback. Their production alias remains an in-memory pointer.

First define the alias event and production-receipt shapes. The helper below doesn't move anything; it gives later decisions a place to record why an alias changed.

07-promote-with-stored-receipt.py
@dataclass(frozen=True)
class AliasEvent:
    action: str
    from_release: str
    to_release: str
    evidence_receipt_id: str
    reasons: tuple[str, ...]

@dataclass(frozen=True)
class ProductionReceipt:
    receipt_id: str
    window: CanaryWindow
    release_fingerprint: str | None
    policy_fingerprint: str | None
    rollback_target_fingerprint: str | None
    failed_gates: tuple[str, ...]
    decision: str

audit_events: list[AliasEvent] = []
production_receipts: dict[str, ProductionReceipt] = {}
latest_production_receipts: dict[str, str] = {}
production_start_days: dict[str, int] = {"delivery-risk-v0": 0}
latest_valid_production_days: dict[str, int] = {"delivery-risk-v0": 0}

def promote(candidate_id: str, canary_receipt_id: str) -> dict[str, object]:
    candidate = registry.get(candidate_id)
    canary_receipt = canary_receipts.get(canary_receipt_id)
    failed = []
    if candidate is None:
        failed.append("candidate_not_registered")
    if aliases.get("canary") != candidate_id:
        failed.append("canary_alias_missing")
    if canary_receipt is None:
        failed.append("canary_receipt_not_registered")
    elif candidate is not None:
        if latest_canary_receipts.get(candidate_id) != canary_receipt_id:
            failed.append("canary_receipt_superseded")
        policy = POLICIES.get(candidate.promotion_policy_version)
        if canary_receipt.candidate != candidate_id:
            failed.append("canary_receipt_candidate_mismatch")
        if canary_receipt.decision != "ready_for_promotion":
            failed.append("canary_receipt_not_ready")
        if (
            canary_receipt.promotion_policy_version
            != candidate.promotion_policy_version
        ):
            failed.append("canary_receipt_policy_version_mismatch")
        if (
            policy is None
            or canary_receipt.promotion_policy_fingerprint
            != immutable_fingerprint(policy)
        ):
            failed.append("canary_receipt_policy_fingerprint_mismatch")
        if (
            canary_receipt.release_fingerprint
            != immutable_fingerprint(candidate)
        ):
            failed.append("canary_receipt_release_fingerprint_mismatch")
        if (
            canary_receipt.artifact_fingerprint
            != candidate.candidate_artifact_fingerprint
        ):
            failed.append("canary_receipt_artifact_fingerprint_mismatch")

        offline_receipt = offline_receipts.get(canary_receipt.offline_receipt_id)
        if offline_receipt is None:
            failed.append("offline_receipt_not_registered")
        else:
            failed.extend(
                accepted_offline_binding_failures(candidate, offline_receipt)
            )
            if (
                canary_receipt.promotion_policy_fingerprint
                != offline_receipt.promotion_policy_fingerprint
                or canary_receipt.release_fingerprint
                != offline_receipt.release_fingerprint
                or canary_receipt.artifact_fingerprint
                != offline_receipt.artifact_fingerprint
            ):
                failed.append("canary_offline_binding_mismatch")
    if (
        candidate is not None
        and candidate.previous_release != aliases["production"]
    ):
        failed.append("production_changed_since_canary_open")
    if (
        canary_receipt is not None
        and canary_receipt.production_before != aliases["production"]
    ):
        failed.append("production_changed_since_canary_receipt")
    if failed:
        return {"action": "hold_promotion", "reasons": sorted(failed)}

    try:
        reload_release(candidate_id)
        reload_release(aliases["production"])
    except (ValueError, OSError):
        return {"action": "hold_promotion", "reasons": ["model_reload_failed"]}

    previous = aliases["production"]
    aliases["previous_production"] = previous
    aliases["production"] = candidate_id
    production_start_days[candidate_id] = canary_receipt.windows[-1].observed_day
    latest_valid_production_days[candidate_id] = production_start_days[candidate_id]
    aliases.pop("canary")
    event = AliasEvent("promote", previous, candidate_id, canary_receipt_id, ())
    audit_events.append(event)
    return asdict(event)

print("promote helper ready")

Promotion logic is defined, but no alias has moved. Monitoring must first decide whether a production window is healthy, incomplete, or measurably worse. The next cell keeps incomplete labels as hold_monitor and reserves rollback for explicit regressions.

08-monitor-production-and-rollback.py
def production_gate_results(window: CanaryWindow) -> tuple[list[str], list[str]]:
    """Return (failed_gates, rollback_reasons).

    Incomplete delayed labels are recorded but never authorize rollback.
    Only measured regressions (errors, latency, labeled cost) can restore
    the previous production alias.
    """
    release = registry.get(window.release_id)
    if release is None:
        return (["production_window_release_not_registered"], [])
    policy = POLICIES.get(release.promotion_policy_version)
    if policy is None:
        return (["production_policy_not_registered"], [])
    if not valid_window(window):
        return (["invalid_window_metrics"], [])
    failed: list[str] = []
    rollback_reasons: list[str] = []
    if window.error_rate > policy.max_error_rate:
        failed.append("error_rate_regression")
        rollback_reasons.append("error_rate_regression")
    if window.p95_latency_ms > policy.max_p95_latency_ms:
        failed.append("latency_regression")
        rollback_reasons.append("latency_regression")
    if not window.delayed_labels_ready or window.late_warning_cost_delta is None:
        # Missing evidence: hold the monitor; do not treat as quality failure.
        failed.append("delayed_quality_not_ready")
    elif (
        window.late_warning_cost_delta is None
        or window.late_warning_cost_delta > policy.max_late_warning_cost_delta
    ):
        failed.append("late_warning_cost_regression")
        rollback_reasons.append("late_warning_cost_regression")
    return failed, rollback_reasons

def evaluate_production(window: CanaryWindow) -> ProductionReceipt:
    if type(window) is not CanaryWindow:
        raise ValueError("production monitoring requires a CanaryWindow record")
    failed, rollback_reasons = production_gate_results(window)
    release_mismatch = window.release_id != aliases["production"]
    has_day = type(window.observed_day) is int and window.observed_day >= 0
    older = has_day and window.observed_day < max(
        production_start_days.get(window.release_id, 0),
        latest_valid_production_days.get(window.release_id, 0),
    )
    if older:
        failed.append("production_window_older_than_current_evidence")
    if release_mismatch:
        failed.append("production_window_release_mismatch")
    decision = (
        "hold_rollback"
        if release_mismatch or older
        else "rollback_required"
        if rollback_reasons
        else "hold_monitor"
        if failed
        else "keep_production"
    )
    receipt = ProductionReceipt(
        receipt_id=f"production-receipt-{len(production_receipts) + 1}",
        window=window,
        release_fingerprint=(immutable_fingerprint(registry[window.release_id])
                             if window.release_id in registry else None),
        policy_fingerprint=(immutable_fingerprint(POLICIES[registry[window.release_id].promotion_policy_version])
                            if window.release_id in registry
                            and registry[window.release_id].promotion_policy_version in POLICIES else None),
        rollback_target_fingerprint=(immutable_fingerprint(registry[aliases["previous_production"]])
                                     if aliases.get("previous_production") in registry else None),
        failed_gates=tuple(sorted(failed)),
        decision=decision,
    )
    production_receipts[receipt.receipt_id] = receipt
    if not older:
        latest_production_receipts[window.release_id] = receipt.receipt_id
        if not release_mismatch and valid_window(window):
            latest_valid_production_days[window.release_id] = window.observed_day
    return receipt

def rollback_if_needed(production_receipt_id: str) -> dict[str, object]:
    receipt = production_receipts.get(production_receipt_id)
    if receipt is None:
        return {"action": "hold_rollback", "reason": "production_receipt_not_registered"}
    if latest_production_receipts.get(receipt.window.release_id) != production_receipt_id:
        return {"action": "hold_rollback", "reason": "production_receipt_superseded"}
    if receipt.decision == "hold_rollback":
        return {"action": "hold_rollback", "reasons": list(receipt.failed_gates)}
    if receipt.decision == "hold_monitor":
        # Incomplete labels or other non-regression holds: leave production alone.
        return {
            "action": "hold_monitor",
            "release": aliases["production"],
            "reasons": list(receipt.failed_gates),
        }
    if receipt.window.release_id != aliases["production"]:
        return {"action": "hold_rollback", "reason": "production_changed_since_monitor_receipt"}
    if receipt.decision == "keep_production":
        return {"action": "keep_production", "release": aliases["production"]}
    if receipt.decision != "rollback_required":
        return {"action": "hold_rollback", "reason": "production_receipt_decision_invalid"}

    previous = aliases.get("previous_production")
    if previous is None or previous not in registry:
        return {"action": "hold_rollback", "reason": "previous_production_not_registered"}
    failed_release = aliases["production"]
    current = registry[failed_release]
    policy = POLICIES.get(current.promotion_policy_version)
    if (receipt.release_fingerprint != immutable_fingerprint(current)
            or policy is None or receipt.policy_fingerprint != immutable_fingerprint(policy)
            or previous != current.previous_release
            or receipt.rollback_target_fingerprint != immutable_fingerprint(registry[previous])):
        return {"action": "hold_rollback", "reason": "rollback_binding_changed"}
    try:
        reload_release(previous)
    except (ValueError, OSError):
        return {"action": "hold_rollback", "reason": "rollback_model_reload_failed"}
    aliases["production"] = previous
    production_start_days[previous] = receipt.window.observed_day
    latest_valid_production_days[previous] = production_start_days[previous]
    aliases["rollback_from"] = failed_release
    event = AliasEvent(
        "rollback",
        failed_release,
        previous,
        receipt.receipt_id,
        receipt.failed_gates,
    )
    audit_events.append(event)
    return asdict(event)

print("rollback helper ready")

Now exercise the promotion boundary. The missing receipt and early receipt should hold; only the stored ready receipt can move production from v0 to v1. Predict which alias and audit fields change before running the call.

09-attempt-promotion-paths.py
print("fabricated promotion:", promote("delivery-risk-v1", "canary-receipt-missing"))
print("early promotion:", promote("delivery-risk-v1", early_receipt.receipt_id))
print("approved promotion:", promote("delivery-risk-v1", ready_receipt.receipt_id))
assert aliases["production"] == "delivery-risk-v1"
assert predict(reload_release(aliases["production"]), {"delay_minutes": 9}) == 1
Output
fabricated promotion: {'action': 'hold_promotion', 'reasons': ['canary_receipt_not_registered']}
early promotion: {'action': 'hold_promotion', 'reasons': ['canary_receipt_not_ready', 'canary_receipt_superseded']}
approved promotion: {'action': 'promote', 'from_release': 'delivery-risk-v0', 'to_release': 'delivery-risk-v1', 'evidence_receipt_id': 'canary-receipt-3', 'reasons': ()}

The local production pointer selects the candidate after the approved call. Feed the monitor an invented day-eight window with a labeled cost regression. Service metrics still pass, so this rollback decision must come from the supplied quality metric and must reference the selected release.

10-detect-production-regression.py
degraded = CanaryWindow("production-day-eight", "delivery-risk-v1", 8, 900, 0.004, 130, True, 0.14)
degraded_receipt = evaluate_production(degraded)
rollback_result = rollback_if_needed(degraded_receipt.receipt_id)
print("production decision:", degraded_receipt.decision)
print("rollback action:", rollback_result["action"])
assert aliases["production"] == "delivery-risk-v0"
assert predict(reload_release(aliases["production"]), {"delay_minutes": 9}) == 0
Output
production decision: rollback_required
rollback action: rollback

Print the receipt and aliases last. The audit should contain one promotion and one rollback, each tied to a stored receipt, with production back on v0.

11-audit-alias-movements.py
print("production receipt:", degraded_receipt.decision, degraded_receipt.failed_gates)
print("rollback:", rollback_result)
print("aliases:", json.dumps(aliases, sort_keys=True))
print(
    "audit:",
    [(event.action, event.from_release, event.to_release, event.evidence_receipt_id) for event in audit_events],
)
Output
production receipt: rollback_required ('late_warning_cost_regression',)
rollback: {'action': 'rollback', 'from_release': 'delivery-risk-v1', 'to_release': 'delivery-risk-v0', 'evidence_receipt_id': 'production-receipt-1', 'reasons': ('late_warning_cost_regression',)}
aliases: {"previous_production": "delivery-risk-v0", "production": "delivery-risk-v0", "rollback_from": "delivery-risk-v1"}
audit: [('promote', 'delivery-risk-v0', 'delivery-risk-v1', 'canary-receipt-3'), ('rollback', 'delivery-risk-v1', 'delivery-risk-v0', 'production-receipt-1')]

The local serving probe now reloads delivery-risk-v0 and changes its nine-minute-delay prediction back from warning to silence. This verifies package selection and reload, not a load-balancer rollback. Preprocessing, coefficients, and threshold travel together. A production rollout must also verify deployed revision identity, runtime compatibility, readiness, routing convergence, and any incompatible external feature dependencies.

Production monitoring mirrors the canary hold-versus-abort rule. A measured error-rate, latency, or labeled cost regression returns rollback_required and can restore the previous alias. Incomplete delayed labels return hold_monitor: keep production on the live release and wait for outcomes. Missing evidence isn't negative evidence, so it mustn't look like a quality failure.

That policy isn't a promise to wait indefinitely. A real system needs a deadline for missing labels or telemetry and a fallback when quality can't be measured. Invalid numbers like NaN aren't healthy metrics. This controller aborts a canary on malformed telemetry but holds production monitoring for investigation; the appropriate production fallback depends on the service's risk.

Test the failures that a happy path misses

A ready receipt can become stale even if its contents never change. A later hold supersedes it. An abort for candidate B must not remove candidate A's canary. A rollback target must still match the package that was recorded when the failure was evaluated. The following independent mutations restore controller state after each check so they don't accidentally authorize the next test.

12-test-controller-boundaries.py
from contextlib import contextmanager
from copy import deepcopy

@contextmanager
def controller_sandbox():
    stores = [registry, artifacts, POLICIES, aliases, offline_evidence, offline_receipts,
              active_offline_receipts, canary_receipts, latest_canary_receipts,
              production_receipts, latest_production_receipts, production_start_days,
              latest_valid_production_days]
    saved = [deepcopy(store) for store in stores]
    saved_audit = list(audit_events)
    try:
        yield
    finally:
        for store, original in zip(stores, saved):
            store.clear()
            store.update(original)
        audit_events[:] = saved_audit

with controller_sandbox():
    aliases.update(production="delivery-risk-v0", canary="delivery-risk-v1")
    invalid = replace(day_seven, error_rate=float("nan"))
    result = evaluate_canary("delivery-risk-v1", accepted_offline_receipt.receipt_id, [first_hour, invalid])
    assert result.decision == "abort_canary"
    assert "invalid_window_metrics" in result.failed_gates
    assert "canary" not in aliases
print("NaN canary metrics: aborted")

with controller_sandbox():
    aliases.update(production="delivery-risk-v0", canary="delivery-risk-v1")
    evaluate_canary("unknown-candidate", "missing", [])
    assert aliases["canary"] == "delivery-risk-v1"
    evaluate_canary("delivery-risk-v1", accepted_offline_receipt.receipt_id, [first_hour])
    result = promote("delivery-risk-v1", ready_receipt.receipt_id)
    assert result["action"] == "hold_promotion"
    assert "canary_receipt_superseded" in result["reasons"]
print("unrelated abort and stale ready receipt: contained")

with controller_sandbox():
    aliases.update(production="delivery-risk-v1", previous_production="delivery-risk-v0")
    incomplete = evaluate_production(replace(degraded, late_warning_cost_delta=None))
    assert rollback_if_needed(incomplete.receipt_id)["action"] == "hold_monitor"
    failure = evaluate_production(degraded)
    POLICIES["eta-promotion-v1"] = replace(policy, max_p95_latency_ms=500)
    assert rollback_if_needed(failure.receipt_id)["reason"] == "rollback_binding_changed"
    assert aliases["production"] == "delivery-risk-v1"
print("missing cost and changed rollback policy: held")

with controller_sandbox():
    aliases.update(production="delivery-risk-v1", previous_production="delivery-risk-v0")
    failure = evaluate_production(degraded)
    evaluate_production(replace(degraded, window_id="later-healthy", observed_day=9,
                                late_warning_cost_delta=-0.01))
    assert rollback_if_needed(failure.receipt_id)["reason"] == "production_receipt_superseded"
print("old rollback receipt after newer monitor: held")

with controller_sandbox():
    aliases.update(production="delivery-risk-v0", canary="delivery-risk-v1")
    latest_canary_receipts["delivery-risk-v1"] = ready_receipt.receipt_id
    path = Path(built["packages"]["v1"]["path"])
    payload = path.read_bytes()
    try:
        path.write_bytes(payload + b" ")
        assert promote("delivery-risk-v1", ready_receipt.receipt_id)["reasons"] == ["model_reload_failed"]
        assert aliases["production"] == "delivery-risk-v0"
    finally:
        path.write_bytes(payload)
print("changed serialized candidate: held before alias move")

with controller_sandbox():
    aliases.update(production="delivery-risk-v0", canary="delivery-risk-v1")
    earlier_bad = replace(day_seven, window_id="day-six-regression", observed_day=6,
                          late_warning_cost_delta=0.25)
    result = evaluate_canary("delivery-risk-v1", accepted_offline_receipt.receipt_id,
                             [earlier_bad, day_seven])
    assert result.decision == "abort_canary"
    assert "late_warning_cost_regression" in result.failed_gates
print("earlier mature regression after later improvement: aborted")

with controller_sandbox():
    aliases.update(production="delivery-risk-v1", previous_production="delivery-risk-v0")
    newer = evaluate_production(replace(degraded, window_id="newer-day-nine", observed_day=9,
                                       late_warning_cost_delta=-0.01))
    older = evaluate_production(replace(degraded, window_id="older-day-one", observed_day=1))
    assert older.decision == "hold_rollback"
    assert "production_window_older_than_current_evidence" in older.failed_gates
    assert latest_production_receipts["delivery-risk-v1"] == newer.receipt_id
    assert rollback_if_needed(older.receipt_id)["action"] == "hold_rollback"
    assert aliases["production"] == "delivery-risk-v1"
    malformed = evaluate_production(replace(degraded, window_id="malformed-day-ten",
        observed_day=10, error_rate=float("nan")))
    assert malformed.decision == "hold_monitor"
    late_failure = evaluate_production(replace(degraded, window_id="late-day-eight"))
    assert late_failure.decision == "hold_rollback"
    assert latest_valid_production_days["delivery-risk-v1"] == 9
    assert rollback_if_needed(late_failure.receipt_id)["action"] == "hold_rollback"
    assert aliases["production"] == "delivery-risk-v1"
print("older monitor cannot replace newer evidence: held")

for name, patch in (("critical_slice", {"critical_slice": "false"}),
                    ("late_delivery", {"late_delivery": True}),
                    ("predicted_late", {"predicted_late": 1.0}),
                    ("event_time", {"event_time": ""})):
    try:
        replace(passing_artifact.evaluation_rows[0], **patch)
    except ValueError:
        pass
    else:
        raise AssertionError(f"invalid {name} admitted to offline rows")
print("offline row types and event clocks: rejected")

with controller_sandbox():
    aliases.update(production="delivery-risk-v0", canary="delivery-risk-v1")
    result = evaluate_canary("delivery-risk-v1", accepted_offline_receipt.receipt_id,
                             [first_hour, replace(day_seven, p95_latency_ms=10**400)])
    assert result.decision == "abort_canary"
    assert "invalid_window_metrics" in result.failed_gates
print("huge canary metric: aborted without numeric overflow")
Output
NaN canary metrics: aborted
unrelated abort and stale ready receipt: contained
missing cost and changed rollback policy: held
old rollback receipt after newer monitor: held
changed serialized candidate: held before alias move
earlier mature regression after later improvement: aborted
older monitor cannot replace newer evidence: held
offline row types and event clocks: rejected
huge canary metric: aborted without numeric overflow

These tests establish single-process behavior. Checking an alias and then assigning it in Python doesn't prevent a concurrent controller from changing it between those operations. A production implementation needs a transaction or compare-and-swap against an alias generation, with the audit event committed atomically. Generations also prevent an old receipt from becoming valid again after an alias moves away and back. Give receipts a freshness limit and a rollout-attempt identity; this lab's active-receipt tracking isn't a distributed lock.

The monitor retains the latest valid observation day separately from its latest receipt. Malformed telemetry can hold monitoring, but cannot erase that day. A window with a valid day earlier than promotion or retained valid evidence holds without superseding the receipt pointer. Integer days cannot order updates within a day, establish an absolute measurement time, or distinguish repeated rollout attempts. Real window IDs, timestamps, availability, and attempt generations need their own contract. The caller must supply the full canary history here; submitting only later passing windows does not prove that earlier windows were healthy.

Three injected monitor windows. Day 0 lacks a quality metric and holds canary. Day 7 has a supplied negative 8 percent cost delta and clears the fixture checks. Day 8 has a supplied positive 14 percent delta and requests rollback of the local production pointer.
Invented summaries exercise missing, passing, and regressing quality. They do not establish measured savings, traffic exposure, or recovery time. Missing quality is not a zero cost delta.

💡 Key insight: A hash binds evidence to bytes; it doesn't establish that the evidence is true. Independently measured outcomes and controlled writers are still necessary.

Join fast and delayed monitoring

The controller route stays the same while the evidence changes by product. Before reading the table, name one signal that arrives immediately, one that needs a label, and the event that should restore the previous release.

Gate typeETARankingForecastVision
immediate data healthscan freshnesseligible candidate supplylatest counts loadedphoto quality
immediate service healthlatency/errorsscoring latencyforecast API availabilityimage scoring latency
delayed qualitylate warning costpurchase/return experiment and peak residualreviewer-confirmed damage
rollback eventstale warning spikeblocked listing exposurebroken alert floodunsupported escalations

For scoring systems with delayed labels, canary monitoring pauses wider promotion until enough outcomes arrive. After promotion, incomplete labels hold the production monitor rather than rolling back; only measured regressions restore the previous alias. A model that hasn't failed yet isn't the same as a model that has passed.

⚠️ Common mistake: Treating missing delayed labels as measured failure, or treating a passing canary as a business-lift estimate. Define a deadline and fallback for missing evidence; use an appropriate experiment design for outcome comparisons.

Continuous training is appropriate when a schedule or monitored condition creates a candidate run. Google Cloud's MLOps guidance places automated data validation, model validation, metadata, serving, monitoring, and training triggers around that promotion, with online canary or experiment checks before a candidate serves all traffic.[5] Retraining on a clock isn't a skip-the-gate button. Automation earns trust only when each handoff remains traceable.

Practice: break the release controller

Change one boundary, predict the decision, then run the relevant function inside controller_sandbox(). The happy-path assertions deliberately fail if you mutate their expected outcome; don't interpret that as a broken controller. Reset the fixture before the next mutation so each diagnosis starts from the same evidence.

  1. Change the third argument in the delivery-risk-v1 release_from_artifact(...) call from "delivery-risk-v0" to "delivery-risk-v-missing".
  2. In recall_fail_artifact, change the second critical row's predicted_late value from 0 to 1. Rerun and confirm recall is derived as 1.0 from rows rather than accepted as an argument.
  3. Pass [day_seven, first_hour] to one evaluate_canary call. Confirm that the receipt records window_order_invalid.
  4. Change the day_seven constructor's delayed_labels_ready argument from True to False.
  5. Change the day_seven constructor's late_warning_cost_delta argument from -0.08 to 0.05.
  6. Replace ready_receipt.receipt_id with "canary-receipt-missing" in the approved promotion call.
  7. Before approved promotion, set aliases["production"] = "delivery-risk-v1" to simulate an out-of-band alias move. Confirm that promotion holds, then reset it to "delivery-risk-v0".
  8. After ready_receipt is stored but before promotion, set POLICIES["eta-promotion-v1"] = replace(POLICIES["eta-promotion-v1"], max_p95_latency_ms=500). The ID stays the same, but promotion must hold on fingerprint mismatch. Rerun from the start to restore the policy.
  9. After ready_receipt is stored but before promotion, set artifacts["eta-artifact-v1"] = replace(passing_artifact, model_artifact="delay-model-v1-repacked"). Promotion must hold because the registered package no longer matches the artifact fingerprint. Rerun from the start to restore it.
  10. Change the degraded constructor's late_warning_cost_delta argument from 0.14 to -0.01.
  11. Change the 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.
  12. Replace degraded_receipt.receipt_id with "production-receipt-missing" in the rollback call. Which authorization check holds the alias?
  13. Change the degraded constructor so delayed_labels_ready=False and late_warning_cost_delta=None (reset the cost delta and release ID to the original passing-service values first). Confirm the decision is hold_monitor, rollback_if_needed leaves production on delivery-risk-v1, and no rollback audit event is appended.

Practice answer sketches

Trace any attempted alias move backward before you submit: candidate, artifact, offline evidence, canary receipt, production receipt, then alias event. If one link is missing, stale, or bound to a different release, the correct output is a hold. If measured evidence breaches a policy, the output is an abort or rollback with a receipt that explains why.

Submission: prove the handoffs

Extend the supplied builder and controller into the proposed layout below. These additional files are not created by the cells. Make each handoff inspectable: what was registered, which evidence passed or held, who could move an alias, and which receipt supported rollback. Local fixtures verify mechanisms; real rollout evidence requires actual cohorts and observations.

text
production-ml-platform/
  contracts/
    release_manifest.schema.json
    promotion_policy.json
  pipelines/
    validate_snapshot.py
    train_candidate.py
    evaluate_candidate.py
    promote_alias.py
  registry/
    releases.jsonl
  monitoring/
    live_windows.py
    rollback_policy.py
  receipts/
    offline_gate_report.json
    canary_monitor_report.json
    alias_audit.jsonl
  projects/
    eta/
    ranking/
    forecast/
    vision/
  tests/
    test_failed_gate_never_promotes.py
    test_empty_monitor_window_holds.py
    test_unregistered_receipt_never_promotes.py
    test_alias_race_blocks_promotion.py
    test_delayed_labels_block_promotion.py
    test_rollback_restores_manifest.py
ArtifactAcceptance condition
release schemaidentifies data, features, model, action policy, promotion policy, previous release, and candidate-artifact fingerprint
registryretains stable and candidate release values with controlled writers
append-only receiptsrecord derived offline metrics plus policy, release, and artifact fingerprints, and why each candidate passed, held, aborted, promoted, or rolled back
alias promotion codefollows stored receipts back to bound evidence and rechecks production before moving alias
monitor policydefines canary pause, promote, abort, rollback
testsexecute joins, train-only fitting, reload, malformed metrics, stale windows/receipts, alias changes, and rollback; test concurrent races when a transactional store is implemented

The controller refuses an alias move unless it's bound to stored evidence. Document QA carries the same habit into a document-answering service: an answer needs cited policy text, or an abstention.

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.An incident team restores the damage-cnn-v0 weight file, but the failed cropper and review threshold from damage-cnn-v1 remain active. Which release design prevents this rollback failure?
2.In the release registry, delivery-risk-v1.previous_release is delivery-risk-v-missing while production still points to delivery-risk-v0. What should open_canary do?
3.A registered ETA artifact's held-out rows derive passing schema, leakage, and cost checks, but critical-slice recall is 0.50 against the policy-owned 0.90 floor. Production is delivery-risk-v0. What is the correct result?
4.Canary service health is within limits. One evaluation receives no observation windows. Another has enough ordered windows, but its latest labeled window has late_warning_cost_delta=0.05 while the maximum is 0.0. How should the decisions differ?
5.A prediction was made at noon. One scan was observed at 10:00 and available at 11:00; another was observed at 11:30 but available at 12:30. Which value belongs in the historical feature?
6.An API caller passes a real canary receipt ID for delivery-risk-v1, and the stored receipt says ready_for_promotion. Before promote runs, production no longer equals the receipt's production_before. What should happen?
7.Production is delivery-risk-v1. One monitor window for delivery-risk-v1 has passing health and late_warning_cost_delta=-0.01; another window has regression reasons but release_id='delivery-risk-v0'. Which rollback behavior is correct?
8.Production is delivery-risk-v1. A production monitor window for that release has healthy error rate and latency, but delayed_labels_ready=False and late_warning_cost_delta=None. What should the controller do?
9.A ready canary receipt was created under eta-promotion-v1. Someone replaces that policy with more permissive thresholds under the same ID before promotion. What should promote do?
10.The training delays have mean 5. After seeing evaluation delays, a developer recomputes the imputation mean and overwrites the saved preprocessing. What is the correct repair?

10 questions remaining.

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.