This guide covers advanced workflow features: step types, configuration options, tracks, conditions, modules, and controllers.
Workflow Definition
The workflow definition file (.workflow.js) is the core of your package. It defines what happens when someone runs your workflow.
Minimum Workflow
A workflow only needs steps to run:
export default {
steps: [
resolveStep('analyst-agent'),
resolveStep('developer-agent'),
resolveStep('reviewer-agent'),
],
};
Full Structure
export default {
name: 'My Workflow',
autonomousMode: 'never',
specification: false,
controller: controller('my-controller', {}),
tracks: { /* ... */ },
conditionGroups: [ /* ... */ ],
steps: [ /* ... */ ],
subAgentIds: [ /* ... */ ],
};
Step Functions
Three functions are available globally in workflow files:
resolveStep
Resolves a main agent from config/main.agents.js:
resolveStep('agent-id')
resolveStep('agent-id', { engine: 'claude', interactive: false })
resolveModule
Resolves a module from config/modules.js with loop behavior:
resolveModule('review-module', { loopSteps: 2, loopMaxIterations: 5 })
separator
Creates a visual phase separator in the workflow timeline:
separator("Planning Phase")
separator("⟲ Development Cycle ⟲")
controller
Defines a controller agent for autonomous execution:
controller('controller-agent-id', { engine: 'claude' })
Step Options
Configure how each step behaves:
| Option | Type | Description |
|---|
engine | string | AI engine to use |
model | string | Specific model override |
modelReasoningEffort | 'low' | 'medium' | 'high' | Reasoning effort level |
interactive | boolean | Wait for user input between prompts |
executeOnce | boolean | Run only once (skip on re-runs) |
tracks | string[] | Only run for specified tracks |
conditions | string[] | Only run when ALL conditions are selected |
conditionsAny | string[] | Run when ANY condition is selected |
agentName | string | Override display name |
promptPath | string | string[] | Override default prompt path |
Engines
| Engine | Description |
|---|
'claude' | Anthropic Claude (default) |
'codex' | OpenAI Codex |
'cursor' | Cursor AI |
'ccr' | Claude Code Runner |
'opencode' | OpenCode |
Examples
// Basic step
resolveStep('analyst')
// With engine override
resolveStep('developer', { engine: 'codex' })
// Non-interactive step (auto-continues)
resolveStep('code-gen', { interactive: false })
// Conditional step
resolveStep('ui-designer', { conditions: ['has-ui'] })
// Track-specific step
resolveStep('mobile-dev', { tracks: ['mobile-app'] })
// Execute once (skip on workflow resume)
resolveStep('init', { executeOnce: true })
Module Options
Modules extend step options with loop behavior:
| Option | Type | Description |
|---|
loopSteps | number | How many steps to go back when looping |
loopMaxIterations | number | Maximum loop iterations |
loopSkip | string[] | Agent IDs to skip when looping back |
// Loop back 2 steps, max 5 iterations
resolveModule('review-agent', {
loopSteps: 2,
loopMaxIterations: 5
})
// Loop back 6 steps, skip certain agents
resolveModule('task-checker', {
loopSteps: 6,
loopMaxIterations: 20,
loopSkip: ['runtime-prep', 'init']
})
Modules are agents that can loop back to earlier steps. Use them for review cycles or iterative refinement until a condition is met.
Tracks
Tracks let users choose different workflow paths. Only one track can be selected at runtime.
tracks: {
question: 'Choose your project type:',
options: {
'landing-page': {
label: 'Landing Page',
description: 'Single page website'
},
'full-app': {
label: 'Full Application',
description: 'Complete web application'
},
'api-only': {
label: 'API Only',
description: 'Backend API without frontend'
},
},
},
Use tracks when you have mutually exclusive paths that significantly change the workflow.
Track-Filtered Steps
steps: [
resolveStep('planner'),
resolveStep('frontend-dev', { tracks: ['landing-page', 'full-app'] }),
resolveStep('backend-dev', { tracks: ['full-app', 'api-only'] }),
resolveStep('deployer'),
]
Condition Groups
Conditions are feature toggles that enable or disable specific steps. Unlike tracks, multiple conditions can be selected.
conditionGroups: [
{
id: 'features',
question: 'What features do you need?',
multiSelect: true,
conditions: {
'has-api': { label: 'API', description: 'REST or GraphQL API' },
'has-auth': { label: 'Auth', description: 'User authentication' },
'has-db': { label: 'Database', description: 'Database integration' },
},
},
],
Nested Conditions
Conditions can have children that appear based on parent selection:
conditionGroups: [
{
id: 'deployment',
question: 'Where will you deploy?',
multiSelect: false,
conditions: {
'cloud': { label: 'Cloud', description: 'AWS, GCP, or Azure' },
'self-hosted': { label: 'Self-hosted', description: 'Your own servers' },
},
children: {
'cloud': {
question: 'Which cloud provider?',
multiSelect: false,
conditions: {
'aws': { label: 'AWS', description: 'Amazon Web Services' },
'gcp': { label: 'GCP', description: 'Google Cloud Platform' },
'azure': { label: 'Azure', description: 'Microsoft Azure' },
},
},
},
},
],
Track-Specific Conditions
Condition groups can be limited to specific tracks:
conditionGroups: [
{
id: 'advanced-features',
question: 'Select advanced features:',
multiSelect: true,
tracks: ['full-app'], // Only shown when 'full-app' track is selected
conditions: {
'microservices': { label: 'Microservices', description: 'Service-based architecture' },
'caching': { label: 'Caching', description: 'Redis or Memcached' },
},
},
],
Condition-Filtered Steps
steps: [
resolveStep('planner'),
resolveStep('api-architect', { conditions: ['has-api'] }),
resolveStep('auth-setup', { conditions: ['has-auth'] }),
resolveStep('db-designer', { conditionsAny: ['has-db', 'has-api'] }),
]
Use conditions when ALL listed conditions must be selected. Use conditionsAny when ANY of the listed conditions enables the step.
Controllers
Controllers are special agents that drive workflows autonomously. They can approve step transitions without user input.
export default {
name: 'My Workflow',
controller: controller('my-controller-agent', { engine: 'claude' }),
autonomousMode: 'always',
// ...
};
Controllers are in beta. The workflow can still run without a controller in manual mode.
Workflow Signals MCP
Controllers and step agents communicate through the workflow-signals MCP. Both must be configured for autonomous mode to work.
Controller Agent
Step Agents
The controller approves or rejects step transitions.{
id: 'my-controller-agent',
name: 'Project Controller',
role: 'controller',
promptPath: path.join(promptsDir, 'controller', 'main.md'),
mcp: [
{
server: 'workflow-signals',
only: ['approve_step_transition', 'get_pending_proposal'],
},
],
}
Step agents propose when they’re done.{
id: 'developer',
name: 'Developer',
promptPath: path.join(promptsDir, 'developer', 'main.md'),
mcp: [
{
server: 'workflow-signals',
only: ['propose_step_completion'],
},
],
}
| Agent Type | MCP Tools |
|---|
| Controller | approve_step_transition, get_pending_proposal |
| Step Agents | propose_step_completion |
See Autonomous Example
View a complete autonomous workflow with controller and step agents
Autonomous Mode
Control how the workflow runs:
| Value | Behavior | Toggle |
|---|
'never' | Always manual - user controls each step | Locked - can’t switch to auto |
'always' | Always autonomous - controller drives everything | Locked - can’t switch to manual |
true | Defaults to autonomous mode | User can toggle to manual with Shift+Tab |
false | Defaults to manual mode | User can toggle to auto with Shift+Tab |
export default {
name: 'My Workflow',
autonomousMode: true, // Defaults to auto, user can toggle
// ...
};
Use true or false for most workflows - this gives users flexibility to switch modes. Use 'always' or 'never' only when you want to lock the mode.
Sub-Agents
Sub-agents are helper agents that main agents can invoke for specialized tasks. Both the workflow and main agent must be configured.
Workflow Configuration
Declare available sub-agents in your workflow file:
export default {
name: 'My Workflow',
steps: [
resolveStep('orchestrator', { interactive: false }),
],
subAgentIds: [
'code-generator',
'test-runner',
'doc-writer',
],
};
Agent Coordination MCP
The main agent that orchestrates sub-agents needs the agent-coordination MCP:
{
id: 'orchestrator',
name: 'Orchestrator',
description: 'Coordinates sub-agents for specialized tasks',
promptPath: path.join(promptsDir, 'orchestrator', 'main.md'),
mcp: [
{
server: 'agent-coordination',
only: ['run_agents', 'get_agent_status', 'list_available_agents'],
targets: ['code-generator', 'test-runner', 'doc-writer'],
},
],
}
| Field | Description |
|---|
server | Must be 'agent-coordination' |
only | Limit which MCP tools the agent can use |
targets | Restrict which sub-agents can be invoked |
Both subAgentIds in the workflow and agent-coordination MCP on the main agent must be configured for sub-agent orchestration to work.
Sub-agents are defined in config/sub.agents.js.
See Sub-Agent Example
View a complete workflow using sub-agents
Specification Mode
Enable specification mode to require a spec file before running:
export default {
name: 'My Workflow',
specification: true,
// ...
};
When specification: true, CodeMachine prompts for a specification file path at workflow start.
Next Steps
Workflow Examples
See complete real-world workflow examples