Skip to content

Workflow Authoring Contracts

This document defines the canonical, strict YAML contracts for workflow bundles.

The runtime validates these files with Pydantic (extra="forbid"). Workflow bundles use the canonical YAML shapes documented here.

Required Files

At minimum, a workflow should include:

  • orchestrator.yaml
  • agents.yaml
  • handoffs.yaml
  • context_variables.yaml
  • structured_outputs.yaml
  • tools.yaml
  • ui_config.yaml
  • hooks.yaml

extended_orchestration/mfj_extension.json is required when the workflow uses mid-flight journeys (MFJ).

a2a.yaml is optional.

Canonical Directory

app/workflows/{workflow_name}/
  orchestrator.yaml
  agents.yaml
  handoffs.yaml
  context_variables.yaml
  structured_outputs.yaml
  tools.yaml
  ui_config.yaml
  hooks.yaml
  extended_orchestration/
    mfj_extension.json    # MFJ config (only when needed)
  tools/
    *.py
  ui/
    *.js

For builder/system workflows, the same contract applies under the shared generation-core workflow root. The file shape is canonical; the owning root depends on whether the workflow is app-owned or generation-core-owned.

There is no canonical generated-workflow workflows/_shared folder. Generated tools are owned by one workflow and live under that workflow's tools/ directory. If multiple generated workflows need the same capability, either generate explicit workflow-local tools for each workflow or promote the reusable behavior into a framework-owned mozaiksai.core.* API with a documented contract.

Factory-owned builder infrastructure is different: shared builder-only Python modules may live under factory_app/workflows/_shared/ when multiple factory workflows consume them. That path is for the factory repo itself, not for generated workflow bundle output.

Generation vs Refinement

Workflow authors must treat initial generation and post-generation refinement as separate authoring modes.

Initial generation workflows:

  • create canonical state or the first canonical artifact set
  • define ownership boundaries that later refinement can reuse
  • stay focused on first-pass compilation, not universal revision handling

Refinement workflows:

  • start from a persisted artifact version
  • consume a classified change request
  • operate on explicit scoped units
  • widen scope only when the router decides the current unit boundary is insufficient

For builder workflows such as ValueEngine, DesignDocs, AgentGenerator, and AppGenerator, prompt design should stay clean:

  • do not route delivered-bundle adjustments back through intake by default
  • do not assume every change means "start over"
  • do emit structured ownership metadata that later refinement can consume

AppGenerator's build_tasks with owned_paths, depends_on, and acceptance_criteria are the current canonical example of refinement-ready metadata.

Detailed refinement-routing plans are internal. The public authoring contract is that workflows should emit scoped ownership metadata so later refinement can choose the smallest valid re-entry point.

Revision-aware workflow context

Builder workflows should not rely on one boolean like refinement_mode as the long-term revision contract.

The target workflow input contract is:

  • build_modeinitial or revision
  • revision_scopepatch, design, feature, or core
  • change_request_id — stable revision lineage id
  • artifact_kind
  • artifact_version_id
  • refinement_request
  • refinement_request_meta
  • change_intent
  • impact_set
  • sequence_status
  • revision_origin_workflow

Rules:

  • build_mode=revision means the workflow starts from persisted builder state, not from a blank-slate assumption
  • a core reroute into ValueEngine is still build_mode=revision
  • workflows should anchor first on the revision request and persisted upstream summaries, then decide what to preserve or regenerate
  • workflows should emit updated summaries, ownership metadata, and invalidation hints so later revisions stay structured

Canonical File Shapes

orchestrator.yaml

workflow_name: ExampleWorkflow
max_turns: 20
human_in_the_loop: true
workflow_startup_mode: AgentDriven
orchestration_pattern: Pipeline
initial_message_to_user: null
initial_message: "Start with ExampleHostAgent."
initial_agent: ExampleHostAgent
triggers:
  - type: chat
    description: Start from chat transport

Rules: - workflow_name must match directory name. - workflow_startup_mode must be one of: - AgentDriven - UserDriven - BackendOnly - initial_message is a hidden AG2/runtime seed. It is not user-facing transcript content and should not be used as visible copy. - initial_message_to_user is the optional user-facing startup prompt. When it is null, the first visible chat message should come from the workflow's actual initial agent output.

agents.yaml

agents:
  - name: ExampleHostAgent
    prompt_sections:
      - id: role
        heading: "[ROLE]"
        content: "You are a host."
    max_consecutive_auto_reply: 5
    structured_outputs_required: false

Rules: - Each agent needs name. - Each agent must provide either: - prompt_sections or prompt_sections_custom, or - system_message. - Auto-tool execution is derived from tools.yaml (agents with auto_tool_call: true tools); agents.yaml does not define a matching field.

handoffs.yaml

handoff_rules:
  - source_agent: user
    target_agent: ExampleHostAgent
    handoff_type: condition
    condition_type: string_llm
    condition: "When user starts the conversation."
    transition_target: AgentTarget
  - source_agent: ExampleHostAgent
    target_agent: user
    handoff_type: after_work
    transition_target: RevertToUserTarget

Rules: - handoff_type is after_work or condition. - condition is required when handoff_type: condition.

context_variables.yaml

definitions:
  host_complete:
    type: boolean
    description: True when host has enough input
    source:
      type: state
      default: false
      triggers:
        - type: agent_text
          agent: JokeHostAgent
          ui_hidden: true
          match:
            equals: NEXT
        - type: user_text
          match:
            contains: approved
  example_topic:
    type: string
    source:
      type: state
      default: null

agents:
  ExampleHostAgent:
    variables:
      - host_complete
      - example_topic

Rules: - definitions must be a mapping (name -> definition), not a list. - agents must be a mapping (agent_name -> {variables: [...]}), not a list. - Valid trigger types for state variables: - agent_text - user_text - ui_response - Valid source types: - config - data_reference - data_entity - computed - state - external - file

structured_outputs.yaml

registry:
  JokeWriterAgent: JokeCollection

models:
  JokeCollection:
    type: model
    fields:
      jokes:
        type: list
        items: str

Rules: - models.<Name>.type must be model. - registry values must reference existing models keys.

tools.yaml

tools:
  - agent: JokeWriterAgent
    file: save_jokes.py
    function: save_jokes
    tool_type: Agent_Tool
    auto_tool_call: true

  - agent: JokeCriticAgent
    file: display_ratings.py
    function: display_ratings
    tool_type: UI_Surface
    auto_tool_call: true
    ui:
      component: JokeRatingsCard
      mode: inline

  - agent: ReviewerAgent
    file: request_revision.py
    function: request_revision
    tool_type: UI_Tool
    auto_tool_call: false
    ui:
      component: RevisionRequestCard
      mode: artifact

lifecycle_tools:
  - trigger: after_chat
    file: cleanup.py
    function: finalize

Rules: - tools[].tool_type must be one of: - Agent_Tool - UI_Tool - UI_Surface - Agent_Tool is backend-only and must not declare ui. - UI_Tool is interactive and requires ui.component and ui.mode. - UI_Surface is one-way and requires ui.component and ui.mode. - ui_contract belongs only on UI_Tool. - Tool references use file and function.

extended_orchestration/mfj_extension.json

Only add this file if the workflow uses mid-flight journeys.

Minimal single-phase form:

{
  "version": 3,
  "mid_flight_journeys": [
    {
      "id": "my_journey",
      "description": "Brief human-readable summary of the fan-out purpose.",
      "decomposition_agent": "DecompositionAgent",
      "fan_out": {
        "spawn_mode": "workflow",
        "max_children": 5
      },
      "fan_in": {
        "resume_agent": "SummaryAgent",
        "inject_as": "mfj_my_results"
      }
    }
  ]
}

Multi-phase form (stages):

Use stages when one trigger agent powers multiple sequential fan-out → fan-in phases with a mid-flight user gate between them:

{
  "version": 3,
  "mid_flight_journeys": [
    {
      "id": "my_journey",
      "description": "Stage 1 plans, user approves, stage 2 implements.",
      "decomposition_agent": "DecompositionAgent",
      "fan_out": { "spawn_mode": "workflow", "max_children": 10 },
      "stages": [
        {
          "id": "plan",
          "child_initial_agent": "PlanningAgent",
          "resume_agent": "ReviewAgent",
          "inject_as": "mfj_plan_results"
        },
        {
          "id": "implement",
          "gate_agent": "ApprovalAgent",
          "child_initial_agent": "ImplementationAgent",
          "resume_agent": "PackagingAgent",
          "inject_as": "mfj_impl_results"
        }
      ]
    }
  ]
}

Rules: - inject_as values must start with mfj_. - gate_agent is required for every stage after the first. - stages and fan_in are mutually exclusive on the same journey. - The runtime auto-synthesizes context variable declarations for every inject_as key and the five _mfj_resume_* handshake fields. Do not manually declare these in context_variables.yaml. - The resume_agent for each stage must reference the inject_as key by name in its [CONTEXT] prompt section — the runtime injects the value but the agent must be authored to know what to do with it.

ui_config.yaml

visual_agents:
  - JokeHostAgent
  - user

hooks.yaml

hooks:
  - hook_type: update_agent_state
    hook_agent: JokeWriterAgent
    filename: hook_inject_preferences.py
    function: inject_preferences

Rules: - Valid hook_type values: - process_message_before_send - update_agent_state - process_last_received_message - process_all_messages_before_reply

Guardrails

  • Author YAML files directly; do not use .json declarative files for workflows.
  • Keep tool implementations in tools/*.py; declaratives only reference them.
  • Do not create or reference global shared workflow tool folders such as workflows/_shared or app.workflows._shared in generated workflow bundles.
  • Factory-owned shared builder infrastructure may live under factory_app/workflows/_shared/, but generated workflow packs must not emit or depend on that path.
  • Keep app-backend CRUD policy outside workflow declaratives.