<!-- Source: https://docs.biosimulant.com/how-to/library/write-lab-manifest -->

# How to Write a lab.yaml

`lab.yaml` composes models into a graph with shared runtime settings and explicit wiring.

Keep a `README.md` beside each lab root's `lab.yaml` for the narrative About content that users see in Biosimulant.

## Annotated example

```yaml
schema_version: "2.0"
title: "Predator-Prey Ecosystem"
description: "Lotka-Volterra style toy composition"

models:
  - path: ../models/environment
    alias: env
    parameters:
      seed: 42

  - path: ../models/organism-population
    alias: rabbits
    parameters:
      name: Rabbits
      initial_count: 100

  - package: biosimulant/predation
    version: 1.2.0
    alias: predation
    parameters:
      predation_rate: 0.01

wiring:
  - from: env.conditions
    to:
      - rabbits.conditions
  - from: rabbits.population_state
    to:
      - predation.prey_state

runtime:
  duration: 100.0
  communication_step: 0.1
  settle_steps: 0
```

## Key rules

- `runtime.communication_step` is required and must be positive
- `runtime.settle_steps` is optional, defaults to `0`, and must be a non-negative integer
- set input values when starting a run; persistent scalar defaults should be model or lab parameters when appropriate
- the old external tick field is not supported
- every model entry needs an `alias`
- model entries should not declare legacy per-module timing or ordering fields
- wiring references use `alias.port`
- `README.md`, when present beside `lab.yaml`, is loaded as the lab About content

Use `settle_steps` when the lab contains downstream report, export, or
visualisation modules that should consume outputs produced at the final
simulation boundary. One direct producer-to-visualisation edge usually needs
`settle_steps: 1`; a two-hop chain usually needs `settle_steps: 2`. Settling does
not extend simulated time.

## README.md

Use the root `README.md` for explanations that do not belong in structured manifest fields: what the lab is for, assumptions, expected inputs and outputs, run interpretation, citations, and caveats. Owned nested labs can have their own `README.md` beside their own `lab.yaml`, and Biosimulant uses that child README when the child lab is the active view.

If no README is present, the app shows generated About details from `lab.yaml`.

When a README needs screenshots, put them under the lab root, preferably `assets/`, and use relative Markdown image paths. Do not embed images as base64; Desktop and Web load the image files from the lab package. Use subfolders under `assets/` only for files tied to a narrow purpose.

## Model entries

| Field | Meaning |
|-------|---------|
| `path` | Relative path to a local model directory |
| `package` + `version` | Published model reference |
| `alias` | Local name used in wiring |
| `parameters` | Constructor overrides |

For package-backed models, `package` uses `namespace/name` and `version` must be the exact SemVer package version to hydrate. The equivalent CLI ref is `namespace/name@x.y.z`, for example `biosimulant/predation@1.2.0`.

## External I/O for child labs

```yaml
io:
  inputs:
    - name: stimulus
      maps_to: neuron.current
  outputs:
    - name: spikes
      maps_to: recorder.spikes
```

## Nested child labs

```yaml
children:
  - path: ../labs/microcircuit
    alias: circuit_a
    parameters: {}

  - package: your-namespace/nutrient-medium
    version: "1.1.0"
    alias: medium
```

Parent wiring uses `child_alias.external_port_name`:

```yaml
wiring:
  - from: stim.current
    to:
      - circuit_a.stimulus
```

During execution, Biosimulant flattens child labs with the same rules as the
open-source `biosimulant` runtime. Child model aliases are scoped as
`child_alias.model_alias`, and parent wiring references to
`child_alias.external_port_name` are remapped through the child lab's `io.maps_to`
contract. Child lab nesting is limited to depth 5, circular child references are
rejected, and unresolved child labs fail validation/execution instead of being
silently skipped.

Package-backed child Labs require a sibling `biosimulant.lock` entry with the
same `package`, exact `version`, and the Hub archive's `artifact_sha256`. Their
payloads resolve into this Lab's `.biosimulant/dependencies/` directory rather
than a shared machine cache. See [Compose Hub Labs Locally](/how-to/library/compose-hub-labs-locally).

**Info:**

  For Biosimulant upload and registry-backed hydration, use `biosimulant labs package`, `biosimulant labs pull`, and `biosimulant labs release ...`.

## Validate by building

```bash
biosimulant labs release validate biosimulant-packages.yaml
biosimulant labs release build biosimulant-packages.yaml --out dist/biosimulant-packages
```

## Next steps

- [lab.yaml Schema Reference](/references/library/lab-manifest)
- [Compose Nested Labs](/how-to/library/compose-nested-labs)
- [Use a BioWorld](/how-to/library/use-bioworld)
