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.yamlagents.yamlhandoffs.yamlcontext_variables.yamlstructured_outputs.yamltools.yamlui_config.yamlhooks.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_mode—initialorrevisionrevision_scope—patch,design,feature, orcorechange_request_id— stable revision lineage idartifact_kindartifact_version_idrefinement_requestrefinement_request_metachange_intentimpact_setsequence_statusrevision_origin_workflow
Rules:
build_mode=revisionmeans the workflow starts from persisted builder state, not from a blank-slate assumption- a
corereroute intoValueEngineis stillbuild_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¶
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
.jsondeclarative 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/_sharedorapp.workflows._sharedin 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.