Skip to content

Overlay manifest (kind: Overlay)

Package: mas-runtime · Schema: overlay.schema.yaml · apiVersion: mas/v1

An overlay is a manifest that patches an Agent, MAS, Infra, Flavour, Experiment, or in-memory Workspace document without copying the whole file. Experiments reference overlays per scenario (scenarios[].overlays); the CLI applies them with -o path/to/overlay.yaml.

Merge semantics

Symmetrical partial or full override of Agent, MAS, or Flavour documents. Used as benchmark scenarios, runtime overlays, and UI overlay builder output.

Contract boundaries are expressed through schema fields such as design_pattern, governance, observability, workflow, and tools — there is no separate spec.contracts list.

CLI overlays

Command-line overrides become the same canonical Overlay document and are applied after all -o overlays:

mas-ctl chat agent.yaml --override 'agent:spec.context.role=reviewer'
mas-ctl run-mas mas.yaml \
  --override 'mas:spec.agency.agents[id=qa].spec.memory=long'

The $op operators and x-merge strategies are shared with file overlays.

Selector overrides in an overlay

An overlay may group the same root-qualified assignments accepted by the CLI in spec.overrides. They are applied in list order after spec.patch, so identity selectors and [*] wildcards can update several existing list entries without repeating the surrounding manifest structure:

apiVersion: mas/v1
kind: Overlay
metadata:
  name: reviewer-policy
spec:
  target: {kind: MAS}
  patch: {}
  overrides:
    - 'mas:spec.agency.agents[id=reviewer].spec.memory=long'
    - 'mas:spec.agency.agents[*].spec.context.environment=staging'

The root must match spec.target.kind (mas, agent, infra, flavour, experiment, or workspace). Values use YAML syntax, including $op collections. A selector that matches no entry remains an error.


Shape

apiVersion: mas/v1
kind: Overlay
metadata:
  name: cot-ablation
spec:
  target:
    kind: MAS          # Agent | MAS | Infra | Flavour | Experiment | Workspace
    name: optional-filter
  patch:
    workflow:
      entry: broker
      nodes:
        - id: broker
          delegates_to: [researcher]
    agents:
      $entry:            # workflow.entry after this overlay's workflow patch
        design_pattern: { type: cot, params: { max_steps: 10 } }
      $not-entry:        # every agency agent except $entry
        skills: { "$op": { add: [l9-concord-v2-receiver] } }
      $delegates:        # workflow.nodes[$entry].delegates_to
        skills: { "$op": { add: [peer-note] } }
      broker:
        tools: { "$op": { remove: [web-search] } }
    params:              # opaque; tools read ctx.runtime_params
      region: eu-west-1

spec.tools (outside patch) is a separate scenario-level tool injection field. target.kind must be Agent, MAS, Flavour, or Infra.


Target contract

spec.target is required and controls both validation and merge dispatch:

Field Required Semantics
kind yes Target document kind: Agent, MAS, Infra, Flavour, Experiment, or Workspace.
name no For an Agent overlay applied to a MAS, selects one agency row by id, name, or metadata.name. For a standalone target, the document is already selected by the command.

The target kind must match the root used by every spec.overrides expression. An overlay with an unsupported kind, an unknown target name, or a selector with no match fails instead of being silently ignored.

Patch contract

spec.patch is required and must be a mapping. Its allowed fields come from the target-specific schema fragment:

Target kind Patch schema Merge behavior
Agent overlay-agent-patch.schema.yaml Agent fields, $op, and x-merge metadata
MAS overlay-mas-patch.schema.yaml Workflow, agency entries, and MAS collection operators
Infra overlay-infra-patch.schema.yaml RFC 7396 merge of the Infra spec
Flavour overlay-flavour-patch.schema.yaml Deployment posture and plugin collection operators
Experiment, Workspace target document schema In-memory RFC 7396 merge

Plain values follow the field's declared merge strategy. Use an explicit $op mapping for collection operations such as replace, add, remove, clear, or merge. Unknown fields are rejected by schema validation; this keeps a typo from becoming a new configuration field.

Merge semantics

RFC 7396-style merge on the target resource. Flavour overlays still cannot select models or API keys (deployment posture only). Agent overlays may patch any Agent spec field the schema declares (including models[], sampling, reasoning, extra, description, behavior, tools_ref). spec.models[] deep-merges by id, then model, then main. Access fields (api_base, api_key_env) stay on infra.

Examples: llm-reasoning.yaml, llm-sampling.yaml.

On a MAS overlay, patch.agents.$entry is applied to spec.workflow.entry after this overlay's workflow patch (so a design-pattern overlay need not name the entry agent). A missing entry, an unknown entry id, or both $entry and that id as keys is an error.

A target.kind: Agent overlay applied to a MAS selects nested agency rows (spec.agency.agents[], or spec.agents[] when agency is absent). Optional spec.target.name selects one row by id, name, or metadata.name (the agency row id should match the agent YAML metadata.name when the same overlay is reused on both). Zero matches is an error (OverlayTargetError) — the overlay must attach somewhere. That is how labs reuse an Agent overlay such as with-guardrail on trip-planner: compose / run-mas keep Form A rows as {id, ref} and merge the overlay onto each referenced Agent YAML when that file is loaded (governance / observability union by plugin id, they do not replace the agent's existing lists). skills and tools follow the same rule: a {"$op": {"add": [...]}} value appends to what the agent YAML already declares, while a raw list is an explicit replace (and [] clears). A base MAS cannot declare skills (or tools, context, …) on an agency row; those fields live in the agent manifest. Overlay targeting ($entry, $all, $not-entry, $delegates, or a named id) is how a scenario changes them per agent — there is no MAS-level copy of those fields. spec.models on the MAS is the one agent-inherited MAS attribute (unresolved any is filled from experiment / workspace / CLI, not from a silent package default). Lab/bench MAS runs already attach a shared native sink; a fanned-out default native list joins that sink instead of opening a second events.jsonl. Custom observability paths stay per-agent.

On a MAS overlay, reserved patch.agents keys expand onto agency ids after this overlay's workflow patch:

Key Expands to
$entry spec.workflow.entry
$all every spec.agency.agents id
$not-entry every agency id except $entry
$delegates workflow.nodes[$entry].delegates_to

They compose in that order, then a named id wins. $entry plus that same id as a key is an error. $entry / $not-entry require spec.workflow.entry. An unknown $entry id is an error.

Merge semantics: later overlays in a scenario stack win on conflicting keys. Patches use RFC 7396 JSON merge; list fields such as tools accept an explicit {"$op": {replace|add|remove|clear: [...]}} form (handled explicitly by the runtime) alongside plain-list implicit replace.

For MAS overlays, patch.agents uses the same operations to modify the MAS participant list at spec.agency.agents. add and replace entries are {id, ref} agent references, remove is a list of agent ids, and clear: true empties the list:

target:
  kind: MAS
patch:
  agents:
    $op:
      remove: [schedule_agent]
      add:
        - id: generalist
          ref: ./agents/generalist.yaml

The former agents_add and agents_remove patch fields are not supported; use patch.agents.$op instead.

context (prompt/role text) gets the same $op sugar, but per chunk name. Each spec.context.<key> value may be a plain string/{ref} (implicit full replace, same as always) or a list of fragments — a $op patch operates on that fragment list, so an overlay can append or drop one line without restating the rest of the base prompt:

patch:
  context:
    role:
      $op:
        add:
          - "Confirm the budget before booking."
        # remove:
        #   - "Confirm the budget before booking."
        # replace:
        #   - "Whole new role text."
        # clear: true

Document-level x-* keys on the overlay (and under spec.patch) are copied onto the target document root, not into spec. Runtime merge of spec fields ignores these keys. Later overlays win; nested dicts are RFC 7396-merged.


Inspect the compiled spec

mas-ctl compile agent.yaml -o overlays/tools.yaml -o overlays/skills.yaml
mas-ctl compile mas.yaml -o overlays/linear.yaml -O ./compiled/

--layout bundle inlines MAS agents into one YAML file; the default for a directory --output keeps mas.yaml plus agents/*.yaml. See compile.


Experiment linkage

scenarios:
  - id: baseline
    overlays: {logic: [baseline], control: [], infra: []}
  - id: cot
    overlays: {logic: [cot, no-tools], control: [], infra: []}

Tool provider and connection configuration is not patchable through agent overlays. Remote MCP endpoints belong on infra ToolServerRegistry; agent and overlay schemas reject providers[]. See tool.md.


See also