{
  "docVersion": 1,
  "id": "template-mlops-training-serving",
  "name": "MLOps — training to serving",
  "purpose": "An ML platform closing the loop: shared feature definitions materialized offline (training) and online (serving), a lineage-tracking registry, shadow-served challengers and drift-triggered retraining.",
  "nodes": {
    "raw": {
      "type": "datalake",
      "label": "Raw events",
      "x": 20,
      "y": 90,
      "runtime": "clicks · transactions · labels"
    },
    "fsoff": {
      "type": "featurestore",
      "label": "Feature store — offline",
      "x": 270,
      "y": 90,
      "runtime": "point-in-time correct",
      "notes": "Training reads features AS THEY WERE at label time — leakage from the future is the classic silent accuracy lie."
    },
    "train": {
      "type": "gpu",
      "label": "Training pipeline",
      "x": 520,
      "y": 90,
      "runtime": "scheduled + drift-triggered"
    },
    "registry": {
      "type": "registry",
      "label": "Model registry",
      "x": 770,
      "y": 90,
      "runtime": "versions · lineage · approvals",
      "color": "purple",
      "notes": "Every artifact records its training data span, feature versions and code commit. Rollback = repoint, not rebuild."
    },
    "fson": {
      "type": "featurestore",
      "label": "Feature store — online",
      "x": 270,
      "y": 300,
      "runtime": "same definitions · <10ms",
      "color": "green",
      "patterns": [
        "replicated"
      ]
    },
    "drift": {
      "type": "apm",
      "label": "Drift monitor",
      "x": 520,
      "y": 300,
      "runtime": "inputs + predictions watched",
      "color": "amber",
      "notes": "Compares live feature and prediction distributions against training baselines — drift beyond threshold triggers the pipeline."
    },
    "serve": {
      "type": "k8s",
      "label": "Serving — champion v12",
      "x": 770,
      "y": 300,
      "runtime": "100% of answers",
      "color": "blue",
      "patterns": [
        "autoscale",
        "stateless"
      ]
    },
    "shadow": {
      "type": "server",
      "label": "Challenger v13 — shadow",
      "x": 770,
      "y": 490,
      "runtime": "mirrored traffic · answers discarded",
      "color": "amber",
      "patterns": [
        "canary"
      ],
      "notes": "Predicts on real requests with zero user exposure. Promotion is a measured comparison, not a hopeful deploy."
    },
    "app": {
      "type": "browser",
      "label": "Product",
      "x": 1020,
      "y": 300,
      "runtime": "asks for predictions"
    }
  },
  "edges": {
    "e1": {
      "from": "raw",
      "to": "fsoff",
      "fromSide": "right",
      "toSide": "left",
      "label": "backfill features",
      "patterns": [
        "batch"
      ]
    },
    "e2": {
      "from": "fsoff",
      "to": "train",
      "fromSide": "right",
      "toSide": "left",
      "label": "point-in-time training sets",
      "animated": true
    },
    "e3": {
      "from": "train",
      "to": "registry",
      "fromSide": "right",
      "toSide": "left",
      "label": "candidate v13 + lineage"
    },
    "e4": {
      "from": "registry",
      "to": "serve",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "promote champion"
    },
    "e5": {
      "from": "registry",
      "to": "shadow",
      "fromSide": "right",
      "toSide": "top",
      "label": "deploy challenger",
      "style": "dashed"
    },
    "e6": {
      "from": "fsoff",
      "to": "fson",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "same definitions, synced",
      "style": "dashed"
    },
    "e7": {
      "from": "fson",
      "to": "serve",
      "fromSide": "right",
      "toSide": "left",
      "label": "online features <10ms",
      "animated": true,
      "patterns": [
        "timeout",
        "cache-aside"
      ]
    },
    "e8": {
      "from": "app",
      "to": "serve",
      "fromSide": "left",
      "toSide": "right",
      "label": "predict",
      "animated": true
    },
    "e9": {
      "from": "serve",
      "to": "shadow",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "mirror requests",
      "style": "dashed"
    },
    "e10": {
      "from": "serve",
      "to": "drift",
      "fromSide": "left",
      "toSide": "right",
      "label": "prediction stats"
    },
    "e11": {
      "from": "drift",
      "to": "train",
      "fromSide": "top",
      "toSide": "bottom",
      "label": "drift → retrain",
      "style": "dashed",
      "color": "amber"
    }
  },
  "boxes": {
    "b1": {
      "x": 240,
      "y": 30,
      "w": 680,
      "h": 210,
      "label": "training world — offline, point-in-time correct",
      "color": "blue",
      "opacity": 0.03
    },
    "b2": {
      "x": 240,
      "y": 250,
      "w": 680,
      "h": 360,
      "label": "serving world — online, milliseconds",
      "color": "green",
      "opacity": 0.03
    }
  },
  "texts": {
    "t1": {
      "x": 20,
      "y": 470,
      "text": "training-serving skew is THE silent killer:\none feature definition, materialized twice,\nbeats two teams' “identical” implementations\nevery single time.",
      "color": "amber",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}