Skip to content

Labs vs libraries

The example-library names in the examples below are synthetic documentation fixtures. MAS-Lab does not ship an example-library or an ioc library.

Labs, libraries, and local plugins

A lab is an experiment pack. A library is reusable code and YAML. Do not turn a lab into a library, and do not create a library when you only need an experiment.

Datasets. library-samples publishes the complete reusable pack for the reference app (trip-planner-benchmark). CI, unit tests, and functional tests pin that catalog (locator: samples) and cap rows with dataset.limit. They must not path: into labs/ — paper labs are not shipped with the packages. Experiment-specific packs (QA reasoning, MAS necessity, lifecycle queries, extensions) live under that lab’s datasets/ and are referenced only from the lab itself.

Developer discovery contract: library-discovery.md.


Lab

A lab is a *.lab/ folder with lab-config.yaml. It stays flat: scenarios, overlays, datasets, and pipelines. Refs use name:path (for example samples:tools/calc.tool.yaml).

Paper labs live under labs/. Create a lab when you are composing a campaign, not when you are publishing reusable tools.

Library

A library is a folder with library.yaml. Apps, tools, overlays, plugins, and Python live there. In a name:path ref, name is always a library name (samples, standard, lab, skills, ioa, or a lab-local name such as lib). It is never a filesystem path. Unknown names fail.

Shipped libraries in this repo include library-samples/, library-standard/, library-lab/, library-skills/, and library-ioa/. The name you write in YAML is a library scheme (samples, example-library), not necessarily the Python package name (mas-library-samples). Additional names can be listed as aliases: in library.yaml; the directory basename and name: field are also accepted. Labs then pin a MAS with library:app (for example example-library:my-app@v2). Versioning uses name@version (bare name is @latest). Slash is a path (example-library:apps/my-app/v2), not an id alias.

How to write those refs: writing-manifests.md.

A library may live inside a lab. That does not make the lab itself a library — do not put library.yaml on the lab root.

Local plugins

A plugin is a runtime or pipeline extension registered by a library. Put plugin Python and YAML in a library (lab-local or shipped). Do not invent a third top-level “plugins folder” beside lab vs library.

What you are adding Where it lives
One-off bench/pipeline step for this experiment That lab’s local library (catalogued in library.yaml)
Agent/runtime plugin (design pattern, memory, tool provider, …) Declared in that library’s library.yaml so discovery finds it
Experiment pack (scenarios, overlays, lab-owned datasets) The lab, not a library

A pipeline step is still a plugin of type step. The lab is the experiment surface; the library is what registers the step.

Enable without editing agent YAML. Workspace config.yaml plugins: and lab.enable_plugins turn on runtime plugins (gdb, checkpoint stores). Every catalog entry is commented in the init template — uncomment to enable. spec.observability and spec.governance stay on the agent.


When to use which

  1. Lab is sufficient when you only compose existing libraries: experiments, overlays, datasets, refs like samples:tools/calc.tool.yaml. No new reusable code.
  2. Local library inside the lab when this lab needs reusable manifests, tools, plugins, or code that are not (yet) worth a shipped package. Put them in a subdir with library.yaml, list it in lab.libraries, ref as name:path.
  3. Shipped / workspace library when more than one lab (or ctl/runtime generally) should reuse it: an installable package, or a path in workspace config.yaml manifest_libraries:.
  4. Local plugins live inside a library (option 2 or 3), not instead of one. If it is only a bench step for this experiment, keep it in the lab’s local library. If it is an agent/runtime plugin, declare it in that library’s library.yaml.

Lab-local library (in-repo pattern)

labs/lifecycle-control.lab and labs/extensions.lab keep reusable pipeline code in a package named after the lab and list that directory:

# lab-config.yaml
lab:
  name: lifecycle-control
  libraries:
    - lifecycle_control_lab/
# lifecycle_control_lab/library.yaml
apiVersion: mas/v1
kind: Library

name: lifecycle-control-lib
description: Lab-local pipeline figure steps for this lab.
version: "0.1.0"

The library name in refs is the listed basename: lifecycle_control_lab/ → lifecycle_control_lab. Immediate children of the lab root that contain library.yaml are picked up too.

The directory is also the Python package (lifecycle_control_lab.steps.…). Every library's parent directory shares one sys.path, so package names must be unique across the libraries a process loads: two labs that both ship a lib/ package would shadow each other. The plugin registry fails with both paths when it detects such a clash.

A listed directory without library.yaml is Python sys.path only. Do not leave an in-repo lab in that state.

How to write name:path

# Overlay / experiment / agent — prefix is a library name
tools:
  - samples:tools/calc.tool.yaml

application:
  app: example-library:my-app@v2
  # or: manifest: samples:apps/trip-planner/mas.yaml

# Workspace config.yaml — search folders (library root or parent of libraries)
manifest_libraries:
  - .
  - ./libraries

Lab-local names win over the same name in workspace config or an installed library. First-seen name wins.

lab.libraries, workspace manifest_libraries, and MAS_LIBRARY_PATHS are the same scan. Install (entry point) is the other door. library.yaml fills the catalog. Do not declare aliases (ioc for example-library).


See also