Skip to main content
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:
example.workflow.js
export default {
  steps: [
    resolveStep('analyst-agent'),
    resolveStep('developer-agent'),
    resolveStep('reviewer-agent'),
  ],
};

Full Structure

example.workflow.js
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:
OptionTypeDescription
enginestringAI engine to use
modelstringSpecific model override
modelReasoningEffort'low' | 'medium' | 'high'Reasoning effort level
interactivebooleanWait for user input between prompts
executeOncebooleanRun only once (skip on re-runs)
tracksstring[]Only run for specified tracks
conditionsstring[]Only run when ALL conditions are selected
conditionsAnystring[]Run when ANY condition is selected
agentNamestringOverride display name
promptPathstring | string[]Override default prompt path

Engines

EngineDescription
'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:
OptionTypeDescription
loopStepsnumberHow many steps to go back when looping
loopMaxIterationsnumberMaximum loop iterations
loopSkipstring[]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.
The controller approves or rejects step transitions.
main.agents.js
{
  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'],
    },
  ],
}
Agent TypeMCP Tools
Controllerapprove_step_transition, get_pending_proposal
Step Agentspropose_step_completion

See Autonomous Example

View a complete autonomous workflow with controller and step agents

Autonomous Mode

Control how the workflow runs:
ValueBehaviorToggle
'never'Always manual - user controls each stepLocked - can’t switch to auto
'always'Always autonomous - controller drives everythingLocked - can’t switch to manual
trueDefaults to autonomous modeUser can toggle to manual with Shift+Tab
falseDefaults to manual modeUser 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:
example.workflow.js
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:
main.agents.js
{
  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'],
    },
  ],
}
FieldDescription
serverMust be 'agent-coordination'
onlyLimit which MCP tools the agent can use
targetsRestrict 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