<!-- Source: https://docs.biosimulant.com/standards/model-compatibility/model-yaml -->

# Adding compatibility to `model.yaml`

To make a model's ports comparable, add two things to its `model.yaml`:

- a top-level `compatibility` block that lists the profiles the model uses
- a `contract` on each port you want to describe

Nothing else in the file changes, and `schema_version` stays `"2.0"`. Structural fields such as `signal_type`, `dtype` and `shape` stay where they are. The contract adds what the data means.


### Before

    ```yaml
    schema_version: "2.0"
    standard: other

    biosim:
      entrypoint: src.model:Model
      communication_step: 1.0

    io:
      inputs:
        - name: expression
          signal_type: array
          dtype: float32
          shape: ["*"]
      outputs: []
    ```


### After: with compatibility

    ```yaml
    schema_version: "2.0" # unchanged
    standard: other

    compatibility:       # optional addition
      standard: https://biosimulant.com/standards/model-compatibility/v0.1
      profiles:
        - ref: https://biosimulant.com/standards/model-compatibility/profiles/transcriptome/gene-expression-counts/v0.1
          sha256: sha256:dac3977de0efcf18470b9be070fad85d57393bd73fe28844d6e6a8d1de8d0ee8

    biosim:
      entrypoint: src.model:Model
      communication_step: 1.0

    io:
      inputs:
        - name: expression
          signal_type: array
          dtype: float32
          shape: ["*"]
          contract:       # optional addition on the same port
            profile_refs:
              - https://biosimulant.com/standards/model-compatibility/profiles/transcriptome/gene-expression-counts/v0.1
            semantic:
              concept: gene_expression
              subject: biological_sample
            representation:
              kind: dense_vector
            identifiers:
              namespace: ensembl-gene
              namespace_version: release-pinned
            biological_context:
              species: NCBITaxon:9606
      outputs: []
    ```


## What the example says

- `compatibility.profiles` imports the gene expression counts profile, pinned by its `sha256`. You can find a profile's `ref` and `sha256` in the [catalogue](/standards/model-compatibility/catalogue) or with `biosimulant compatibility profiles list`.
- `profile_refs` applies that profile to the `expression` input.
- `semantic` says the values are gene expression measured in a biological sample.
- `representation.kind: dense_vector` says there's one value per gene.
- `identifiers` says genes are named with Ensembl gene IDs. In a real model, set `namespace_version` to the Ensembl release you used.
- `biological_context.species: NCBITaxon:9606` says the data is from humans, using the NCBI Taxonomy ID.

The gene expression counts profile requires all six of these contract fields. Check the file with:

```bash
biosimulant compatibility validate model.yaml
```

If the file is valid, the command prints:

```json
{
  "findings": [],
  "opted_in": true,
  "valid": true
}
```

If a required field is missing or a profile isn't imported, `valid` is `false`, each problem is listed under `findings`, and the command exits with status 1.

## Rules for contracts

- `compatibility.profiles` lists every profile the model uses, each pinned by `sha256`.
- `contract.profile_refs` applies profiles to one port. A port can only use profiles listed in `compatibility.profiles`.
- An input's `contract` describes what the input accepts, whatever form the data arrives in.
- If an input lists `accepted_profiles`, each entry can have its own `contract` that narrows the input's contract for that form. It can't loosen it.
- An output's `contract` describes what the output guarantees to send.
- A contract written inline can add detail to a referenced contract, but can't loosen it.
- A published profile reference needs a versioned URL and a matching `sha256`.
- Namespaced `extensions` are kept. Comparison ignores them unless it supports that namespace.

**Info:**

  Your Python code still defines how the model runs. When Biosimulant loads a packaged model, it checks that the ports in `model.yaml` match the ports your code declares, including signal type, data type and shape. If they don't match, loading fails. If they match, the contracts from `model.yaml` are attached to the model's ports.

## Models with and without compatibility

| `model.yaml` | Validation | Comparing its ports | Running the model |
|---|---|---|---|
| No `compatibility` block | Existing checks only | `UNKNOWN` (not enough information to decide) | Unchanged |
| Valid `compatibility` block | Existing checks plus compatibility checks | A report with a status and findings | Unchanged |
| Invalid `compatibility` block | The package build fails | Not available | The package isn't built |

Building a model that has a `compatibility` block needs the compatibility extra: `pip install 'biosimulant[compatibility]'`. The built package includes `payload/compatibility.lock.json`, and your source `model.yaml` isn't changed.

For every other `model.yaml` field, see the [`model.yaml` schema](/references/model-manifest).
