Skip to main content

ralph_workflow/reducer/event/
pipeline_phase.rs

1//! Pipeline phase definitions.
2
3use serde::{Deserialize, Serialize};
4
5/// Pipeline phases for checkpoint tracking.
6///
7/// These phases represent the major stages of the Ralph pipeline.
8/// Reducers transition between phases based on events.
9///
10/// # Phase Transitions
11///
12/// ```text
13/// Planning → Development → Review → CommitMessage → FinalValidation → Finalizing → Complete
14///              ↓             ↓            ↓
15///         AwaitingDevFix → Interrupted
16/// ```
17///
18/// # Phase Descriptions
19///
20/// - **Planning**: Generate implementation plan for the iteration
21/// - **Development**: Execute plan, write code
22/// - **Review**: Review code changes, identify issues
23/// - **`CommitMessage`**: Generate commit message
24/// - **`FinalValidation`**: Final checks before completion
25/// - **Finalizing**: Cleanup operations (restore permissions, etc.)
26/// - **Complete**: Pipeline completed successfully
27/// - **`AwaitingDevFix`**: Terminal failure occurred, dev agent diagnosing
28/// - **Interrupted**: Pipeline terminated (success or failure)
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30pub enum PipelinePhase {
31    Planning,
32    Development,
33    Review,
34    CommitMessage,
35    FinalValidation,
36    /// Finalizing phase for cleanup operations before completion.
37    ///
38    /// This phase handles:
39    /// - Restoring PROMPT.md write permissions
40    /// - Any other cleanup that must go through the effect system
41    Finalizing,
42    Complete,
43    /// Awaiting development agent to fix pipeline failure.
44    ///
45    /// This phase occurs when the pipeline encounters a terminal failure condition
46    /// (e.g., agent chain exhausted) but before transitioning to Interrupted. It
47    /// signals that the development agent should be invoked to diagnose and fix
48    /// the failure root cause.
49    ///
50    /// ## Failure Handling Flow
51    ///
52    /// 1. `ErrorEvent::AgentChainExhausted` occurs in any phase
53    /// 2. Reducer transitions state to `AwaitingDevFix`
54    /// 3. Orchestration determines `Effect::TriggerDevFixFlow`
55    /// 4. Handler executes `TriggerDevFixFlow`:
56    ///    a. Writes completion marker to .`agent/tmp/completion_marker` (failure status)
57    ///    b. Emits `DevFixTriggered` event
58    ///    c. Dispatches dev-fix agent
59    ///    d. Emits `DevFixCompleted` event
60    ///    e. Emits `CompletionMarkerEmitted` event
61    /// 5. DevFixTriggered/DevFixCompleted events: no state change (stays in `AwaitingDevFix`)
62    /// 6. `CompletionMarkerEmitted` event: transitions to Interrupted
63    /// 7. Orchestration determines `Effect::SaveCheckpoint` for Interrupted
64    /// 8. Handler saves checkpoint, increments `checkpoint_saved_count`
65    /// 9. Event loop recognizes `is_complete()` == true and exits successfully
66    ///
67    /// ## Event Loop Termination Guarantees
68    ///
69    /// The event loop MUST NOT exit with completed=false when in `AwaitingDevFix` phase.
70    /// The failure handling flow is designed to always complete with:
71    /// - Completion marker written to filesystem
72    /// - State transitioned to Interrupted
73    /// - Checkpoint saved (`checkpoint_saved_count` > 0)
74    /// - Event loop returning completed=true
75    ///
76    /// If the event loop exits with completed=false from `AwaitingDevFix`, this indicates
77    /// a critical bug (e.g., max iterations reached before checkpoint saved).
78    ///
79    /// ## Completion Marker Requirement
80    ///
81    /// The completion marker MUST be written before transitioning to Interrupted.
82    /// This ensures external orchestration systems (CI, monitoring) can detect
83    /// pipeline termination even if the event loop exits unexpectedly.
84    ///
85    /// ## Agent Chain Exhaustion Handling
86    ///
87    /// When in `AwaitingDevFix` phase with an exhausted agent chain, orchestration
88    /// falls through to phase-specific logic (`TriggerDevFixFlow`) instead of reporting
89    /// exhaustion again. This prevents infinite loops where exhaustion is reported
90    /// repeatedly.
91    ///
92    /// Transitions:
93    /// - From: Any phase where `AgentChainExhausted` error occurs
94    /// - To: Interrupted (after dev-fix attempt completes or fails)
95    AwaitingDevFix,
96    Interrupted,
97}
98
99impl std::fmt::Display for PipelinePhase {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        match self {
102            Self::Planning => write!(f, "Planning"),
103            Self::Development => write!(f, "Development"),
104            Self::Review => write!(f, "Review"),
105            Self::CommitMessage => write!(f, "Commit Message"),
106            Self::FinalValidation => write!(f, "Final Validation"),
107            Self::Finalizing => write!(f, "Finalizing"),
108            Self::Complete => write!(f, "Complete"),
109            Self::AwaitingDevFix => write!(f, "Awaiting Dev Fix"),
110            Self::Interrupted => write!(f, "Interrupted"),
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_pipeline_phase_display() {
121        assert_eq!(format!("{}", PipelinePhase::Planning), "Planning");
122        assert_eq!(format!("{}", PipelinePhase::Development), "Development");
123        assert_eq!(format!("{}", PipelinePhase::Review), "Review");
124        assert_eq!(
125            format!("{}", PipelinePhase::CommitMessage),
126            "Commit Message"
127        );
128        assert_eq!(
129            format!("{}", PipelinePhase::FinalValidation),
130            "Final Validation"
131        );
132        assert_eq!(format!("{}", PipelinePhase::Finalizing), "Finalizing");
133        assert_eq!(format!("{}", PipelinePhase::Complete), "Complete");
134        assert_eq!(
135            format!("{}", PipelinePhase::AwaitingDevFix),
136            "Awaiting Dev Fix"
137        );
138        assert_eq!(format!("{}", PipelinePhase::Interrupted), "Interrupted");
139    }
140}