-
-
Notifications
You must be signed in to change notification settings - Fork 0
Six Phase Workflow
The workflow engine is the heart of M31 Autonomous (M31A). Every coding task is routed through six phases, providing structured execution with verification, self-healing, and git integration.
Source: internal/workflow/engine.go
Initialize ──→ Discuss ──→ Plan ──→ Execute ──→ Verify ──→ Ship
│ │ │ │ │ │
│ │ │ │ │ └── Commit + ledger entry
│ │ │ │ └── Build + test verification
│ │ │ └── Task execution with tool calls
│ │ └── Structured task plan generation
│ └── Clarifying questions and answers
└── Project detection, context building
The engine supports four modes that control which phases run:
| Mode | Phases | Use Case |
|---|---|---|
auto (default) |
Classifies prompt complexity and adapts | Most tasks |
full |
All 6 phases: Init, Discuss, Plan, Execute, Verify, Ship | Complex features |
fast |
Skips Plan: Init, Discuss, Execute, Verify, Ship | Simple fixes |
direct |
Skips Discuss, Plan, Verify: Init, Execute, Ship | Trivial changes |
Mode is set via config (features.workflow_mode) or the /agent-mode command.
Source: internal/workflow/initialize.go
- Detects project type (Go, Node.js, Rust, Python, Ruby) via marker files
- Builds project context: framework, structure, dependencies
- Creates a git commit hash snapshot for rollback baseline
- Runs code intelligence indexing (
internal/codeintel/) - Stores project state in the session
Source: internal/workflow/discuss.go
- LLM generates clarifying questions about the task
- Questions are presented to the user in the TUI
- User answers are collected and saved to the project state
- Supports Q&A timeout (configurable, default 300s)
- Can be skipped with empty answers
The discuss phase uses streaming to present questions in real-time. The TUI renders each question as it arrives and collects answers via interactive prompts.
Source: internal/workflow/plan.go
- LLM generates a structured implementation plan in markdown
- Plan includes: task breakdown, file predictions, acceptance criteria
- Plans are parsed into
Taskstructs viainternal/workflow/plan_parser.go - Supports refinement: user feedback triggers plan regeneration (up to
MaxPlanRefinements = 5) - Plan content is saved to
STATE.mdin the session directory
Plan format (from prompts/plan-format.md):
- Task ID, description, action, category
- File predictions with create/modify/delete actions
- Dependencies between tasks
- Acceptance criteria per task
Source: internal/workflow/execute.go
- Tasks are executed in dependency order
- Each task sends tool calls to the LLM (Bash, FileRead, FileWrite, Edit, etc.)
- Tool results are fed back to the LLM for iterative refinement
- Self-healing: failed tasks are retried up to
MaxHealAttempts(2) times - Task runner supports parallel execution within independent groups (up to
DefaultMaxParallelTasks = 4) - Each completed task creates a git commit
The execute phase uses streamLLMWithTools() which sends tool definitions to the LLM and parses native tool_call chunks from the SSE stream. Tool calls are accumulated by index and finalized into structured ToolCall objects.
Source: internal/workflow/verify.go
- Runs build commands (auto-detected or configured)
- Runs test suites
- Checks file existence for predicted files
- Validates syntax of modified files
- Reports per-task pass/fail status
- Failed tasks can trigger self-healing loops
Verification commands are auto-detected based on project type:
| Project Type | Build Command | Test Command |
|---|---|---|
| Go | go build ./... |
go test ./... |
| Node.js | npm run build |
npm test |
| Rust | cargo build |
cargo test |
| Python | python -m build |
pytest |
Custom commands can be set in [verify] config section.
Source: internal/workflow/ship.go
- Creates final git commit with all verified changes
- Generates structured commit message from the plan
- Appends a ledger entry with session metrics
- Computes diff statistics
- Reports commit count and changed files
Transitions are validated via an explicit state machine:
var validPhaseTransitions = map[WorkflowPhase][]WorkflowPhase{
PhaseIdle: {PhaseInitialize},
PhaseInitialize: {PhaseDiscuss, PhaseExecute, PhaseIdle},
PhaseDiscuss: {PhasePlan, PhaseExecute, PhaseIdle},
PhasePlan: {PhaseExecute, PhasePlan, PhaseDiscuss, PhaseIdle},
PhaseExecute: {PhaseVerify, PhaseShip, PhaseIdle},
PhaseVerify: {PhaseShip, PhaseExecute, PhaseIdle},
PhaseShip: {PhaseIdle},
}Plan-to-Discuss cycles are capped at 3 (maxDiscussPlanCycles) to prevent infinite oscillation. The counter resets when the workflow moves to Execute, Ship, or Idle.
All prompts are embedded markdown files loaded at engine creation:
| File | Purpose |
|---|---|
prompts/base.md |
Base system prompt (cached for session lifetime) |
prompts/tool-use.md |
Tool usage instructions for the LLM |
prompts/plan-format.md |
Plan output format specification |
prompts/execute-task.md |
Task execution instructions |
prompts/discuss-questions.md |
Discuss phase question format |
prompts/self-heal.md |
Self-healing instructions for failed tasks |
prompts/demonstration-format.md |
Demonstration output format |
prompts/autonomous.md |
Autonomous mode instructions |
prompts/context-awareness.md |
Context awareness instructions |
prompts/code-quality.md |
Code quality standards |
prompts/code-intelligence.md |
Code intelligence integration |
Source: internal/workflow/prompts/
Each workflow phase can use a different model, checked in priority order:
-
Per-phase overrides set interactively via the TUI model picker (
SetPhaseModel) -
AgentsConfig from
config.toml([agents]section) -
Global default (
cfg.Agents.Default) - Engine's active model (fallback)
This allows, for example, using a cheap model for Discuss and Plan, and a powerful model for Execute and Verify.
When features.budget_limit_usd is set, the engine tracks cumulative cost across all phases using atomic operations. Each phase checks the budget before executing:
if cost >= cfg.Features.BudgetLimitUSD {
return error("budget limit exceeded")
}Cost is accumulated from the Cost field of each PhaseResult, stored as bits in a uint64 for lock-free atomic updates.
When a task fails verification, the engine can attempt self-healing:
- The failed task's error output is sent back to the LLM
- The LLM generates a fix using tool calls
- The fix is verified again
- Up to
MaxHealAttempts(2) retries before marking asStatusUnrecoverable
Self-healing is triggered automatically during Execute phase and can also be triggered manually via HealTask(taskID).