When building agentic workflows, a common pattern emerges: before the autonomous pipeline runs, someone needs to gather requirements. CodeMachine v0.8.0 introduces the Controller pattern, a first-class way to have a conversation with an AI product owner before your workflow executes.
This walkthrough demonstrates building a spec-driven development workflow where a PO agent gathers requirements, then hands off to specialized agents for analysis, architecture, and implementation.
The Problem
Multi-agent workflows typically start with a prompt and run autonomously. But real development doesn't work that way. Requirements need clarification. Scope needs calibration. Assumptions need validation.
The naive solution: prepend a "requirements gathering" step. But this has issues:
- Session management: The PO conversation should persist across interruptions
- Handoff clarity: When does conversation end and execution begin?
- Return capability: What if you need to talk to the PO mid-workflow?
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Controller Phase │
│ (Interactive PO conversation) │
│ │
│ User ←→ spec-po agent (session persisted) │
│ │
│ [Enter with empty input] → Confirmation Dialog │
└─────────────────────────────────────────────────────────────┘
│
▼ workflow:controller-continue
┌─────────────────────────────────────────────────────────────┐
│ Execution Phase │
│ (Autonomous agent pipeline) │
│ │
│ spec-analyst → spec-architect → spec-api-designer → ... │
│ │
│ [Press C] → Return to Controller Dialog │
└─────────────────────────────────────────────────────────────┘
│
▼ workflow:return-to-controller
┌─────────────────────────────────────────────────────────────┐
│ Return to Controller │
│ (Pause workflow, resume PO session) │
│ │
│ User ←→ spec-po agent (same session) │
│ │
│ [Enter with empty input] → Resume Workflow │
└─────────────────────────────────────────────────────────────┘
Defining a Controller
The controller() function declares which agent handles pre-workflow conversation:
export default {
name: 'Spec-Driven Development',
controller: controller('spec-po', {}),
specification: false,
steps: [
separator("∴ Discovery Phase ∴"),
resolveStep('spec-po', {}),
resolveStep('spec-analyst', {}),
separator("∴ Design Phase ∴"),
resolveStep('spec-architect', {}),
resolveStep('spec-api-designer', {}),
separator("∴ Implementation Phase ∴"),
resolveStep('spec-setup', {}),
resolveStep('spec-impl-orchestrator', {}),
separator("⟲ Review Loop ⟲"),
resolveStep('spec-tester', { interactive: false }),
resolveModule('spec-review', { interactive: false, loopSteps: 2 }),
],
};
Key points:
-
controller('spec-po', {})- Declares the PO agent for pre-workflow conversation -
Same agent in steps - The
spec-poagent appears both as controller AND as step 1 - Automatic skip - When controller phase runs, step 1 is auto-completed (no redundant execution)
Controller Options
The controller() function accepts options for engine and model overrides:
controller('spec-po', {
engine: 'claude', // Override engine
model: 'claude-4.5-sonnet' // Override model
})
This is useful when your PO conversation requires different capabilities than the workflow steps.
The Controller Conversation Flow
When you start a workflow with a controller:
┌──────────────────────────────────────────────────────────────┐
│ CodeMachine v0.8.0 │
├──────────────────────────────────────────────────────────────┤
│ │
│ ▸ spec-po (Running) │
│ │
│ What would you like to build today? │
│──────────────────────────────────────────────────────────────│
│ │
│ > A todo list app with Next.js and SQLite │
│ │
└──────────────────────────────────────────────────────────────┘
The conversation continues until you press Enter with an empty input. A confirmation dialog appears:
┌────────────────────────────────────────┐
│ Ready to start the workflow? │
│ │
│ [Start] [Continue Chat] │
└────────────────────────────────────────┘
Selecting "Start" triggers workflow:controller-continue, transitioning to the execution phase.
Session Persistence
The controller session persists to disk:
{
"controllerConfig": {
"agentId": "spec-po",
"sessionId": "ses_44785e25dffeDZqs8kVN7KbfIx",
"monitoringId": 1
},
"autonomousMode": "true"
}
This enables:
- Resume on crash: If the CLI crashes mid-conversation, the session resumes
- Return to controller: Mid-workflow return uses the same session
- Log viewing: Clicking the completed PO step shows the full conversation
Returning to Controller Mid-Workflow
Press C during workflow execution to pause and return to the controller:
┌──────────────────────────────────────────────────────────────┐
│ Workflow Pipeline (8 items) │
├──────────────────────────────────────────────────────────────┤
│ │
│ ✓ Hady [PO] (completed) │
│ ▸ Moaz [Analyst] (running) ← Paused │
│ ○ Atef [Architect] │
│ ○ Essam [API] │
│ │
├──────────────────────────────────────────────────────────────┤
│ [C] Controller [Esc] Stop │
└──────────────────────────────────────────────────────────────┘
When C is pressed:
- Current step is aborted
- Workflow state machine enters PAUSE
- Phase switches to "onboarding"
- Controller session resumes
After the conversation, pressing Enter resumes the workflow from the paused step.
Complete Workflow Example
Here's the full spec-driven workflow with controller:
export default {
name: 'Spec-Driven Development',
controller: controller('spec-po', {}),
specification: false,
tracks: {
question: 'What are we working on?',
options: {
new_project: { label: 'New Project' },
existing_app: { label: 'Existing App' },
refactor: { label: 'Refactor' },
},
},
conditionGroups: [
{
id: 'features',
question: 'What features does your project have?',
multiSelect: true,
conditions: {
has_ui: { label: 'Has UI' },
has_auth: { label: 'Has Authentication' },
has_db: { label: 'Has Database' },
},
},
],
steps: [
separator("∴ Discovery Phase ∴"),
resolveStep('spec-po', {}),
resolveStep('spec-analyst', {}),
separator("∴ Design Phase ∴"),
resolveStep('spec-architect', {}),
resolveStep('spec-api-designer', {}),
separator("∴ Implementation Phase ∴"),
resolveStep('spec-setup', {}),
resolveStep('spec-impl-orchestrator', {}),
separator("⟲ Review Loop ⟲"),
resolveStep('spec-tester', { interactive: false }),
resolveModule('spec-review', { interactive: false, loopSteps: 2 }),
],
subAgentIds: [
'spec-dev-data',
'spec-dev-api',
'spec-dev-ui',
'spec-dev-tests',
],
};
Key Benefits
| Requirement | Solution |
|---|---|
| Requirements clarification | Controller conversation before execution |
| Session persistence | SessionID stored in template.json |
| Clear handoff | Confirmation dialog before transition |
| Mid-workflow return |
C key pauses and resumes controller session |
| Log viewing | MonitoringID registered for completed step |
| Same agent, no duplication | Controller step auto-completed after phase |
Conclusion
The Controller pattern addresses a fundamental gap in agentic workflows: the need for human-AI conversation before autonomous execution. By treating this as a first-class primitive, with session persistence, clear handoff UX, and mid-workflow return capability, CodeMachine enables workflows that match how real development actually works.
CodeMachine on Github: moazbuilds/codemachinecli
Workflow Examples: github.com/hadywalied/codemachine_example_workflows
Documentation: codemachine.co
Top comments (0)