MLDLC-KIT

The evidence contract

A project meets the gates by leaving evidence in MLflow and in two files. Nothing here depends on a particular data-validation or training library.

mldlc.yaml

project: churn                         # used in names and in the documents' front matter
lifecycle_dir: lifecycle               # where intent.md, spec.md, gates.yaml, releases/ live

mlflow:
  tracking_uri: http://127.0.0.1:5000  # optional; the MLFLOW_TRACKING_URI variable wins
  training_experiment: churn           # the experiment training runs go to (G3, G4, triggers)
  validation_experiment: churn-data-validation   # where validation runs go (G2)
  model_name: churn                    # the registered model (G3, G5)
  production_alias: production         # the alias that means "in production"

data:                                  # what identifies a version of the data
  type: dvc                            # dvc: md5 from a .dvc file | file: hash of one file | command | none
  path: data.dvc

code_paths: ["."]                      # uncommitted changes here make a training run "dirty"
baseline_metric: uplift_vs_baseline    # acceptance must require this metric's min > 0 (null to switch off)
allowed_metrics: ["*"]                 # globs: which metric names a spec may use
hooks: mldlc_hooks                     # optional python module next to this file
commands: {validate: ..., train: ..., test: ...}   # shown by `mldlc info`; the skills tell people to run them

The lifecycle folder is not counted as code: approvals and documents are protected by their own hashes, and counting them would make every approval mark the next training run dirty.

Run tags

mldlc.lineage_tags(project) returns these; call mlflow.set_tags(...) with them in a run. To use your own tag names, map them under evidence_tags: in mldlc.yaml.

Tag (default) Meaning Read by
data.version the data version the run used G2, G3
data.verified true when the files matched that version at run time (for DVC, dvc status is clean) G2, G3
git.sha the commit G3 evidence
git.dirty false when the code under code_paths was committed G3
validation.gate pass on a validation run that passed G2

mldlc.record_validation(project, passed, report) logs a validation run to validation_experiment with the lineage tags and validation.gate. G2 reads the latest one.

Metrics

Acceptance criteria and triggers in spec.md name metrics your training script logs, for example holdout_accuracy or uplift_vs_baseline. G4 reads them from the run that G3 recorded. Names are checked against allowed_metrics, so a typo in a spec cannot silently pass a gate.

The model

The training run must register a model under model_name (registered_model_name= when logging it). G3 records its version, and promote sets the production alias on it.

Hooks (optional)

mldlc_hooks.py connects the spec to the code. Every function is optional.

Function Used for
model_inputs() returns {"target": ..., "features": [...]}; G1 compares it with the spec's model-inputs block, so the spec cannot drift from the code
serving_columns() the columns a serving payload accepts; G1 requires it to equal the model inputs, so a column known only after the event is rejected
smoke_payload() a small valid DataFrame; the release check loads the registered model and scores it

Without hooks the checks still run, and mldlc check G1 tells you what it could not compare.

The documents

intent.md and spec.md have fixed sections (the templates list them) and front matter (intent_version, spec_version, owner). spec.md carries three machine-read blocks:

```yaml model-inputs
target: churned
features: [tenure_months, monthly_spend, support_tickets]
```

```yaml acceptance
metrics:
  holdout_accuracy: {min: 0.65}
  uplift_vs_baseline: {min: 0.03}
```

```yaml triggers
- name: accuracy below the bar
  metric: holdout_accuracy
  min: 0.65
  then: model            # the kind of change to open: intent | spec | data | model | evaluation | release
```

A trigger can also compare two metrics: metric, compare_to and max_drop.