Skip to main content

ralph_workflow/reducer/
event.rs

1//! Pipeline event types for reducer architecture.
2//!
3//! Defines all possible events that can occur during pipeline execution.
4//! Each event represents a state transition that the reducer handles.
5
6use crate::agents::AgentRole;
7use crate::reducer::state::DevelopmentStatus;
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11/// Pipeline phases for checkpoint tracking.
12///
13/// These phases represent the major stages of the Ralph pipeline.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15pub enum PipelinePhase {
16    Planning,
17    Development,
18    Review,
19    CommitMessage,
20    FinalValidation,
21    /// Finalizing phase for cleanup operations before completion.
22    ///
23    /// This phase handles:
24    /// - Restoring PROMPT.md write permissions
25    /// - Any other cleanup that must go through the effect system
26    Finalizing,
27    Complete,
28    Interrupted,
29}
30
31impl std::fmt::Display for PipelinePhase {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::Planning => write!(f, "Planning"),
35            Self::Development => write!(f, "Development"),
36            Self::Review => write!(f, "Review"),
37            Self::CommitMessage => write!(f, "Commit Message"),
38            Self::FinalValidation => write!(f, "Final Validation"),
39            Self::Finalizing => write!(f, "Finalizing"),
40            Self::Complete => write!(f, "Complete"),
41            Self::Interrupted => write!(f, "Interrupted"),
42        }
43    }
44}
45
46/// Pipeline events representing all state transitions.
47///
48/// Each event captures an observable transition in pipeline execution.
49/// The reducer handles these events to compute new state.
50#[derive(Clone, Serialize, Deserialize, Debug)]
51pub enum PipelineEvent {
52    PipelineStarted,
53    PipelineResumed {
54        from_checkpoint: bool,
55    },
56    PipelineCompleted,
57    PipelineAborted {
58        reason: String,
59    },
60
61    PlanningPhaseStarted,
62    PlanningPhaseCompleted,
63
64    DevelopmentPhaseStarted,
65    DevelopmentIterationStarted {
66        iteration: u32,
67    },
68    PlanGenerationStarted {
69        iteration: u32,
70    },
71    PlanGenerationCompleted {
72        iteration: u32,
73        valid: bool,
74    },
75    DevelopmentIterationCompleted {
76        iteration: u32,
77        output_valid: bool,
78    },
79    ContextCleaned,
80    DevelopmentPhaseCompleted,
81
82    ReviewPhaseStarted,
83    ReviewPassStarted {
84        pass: u32,
85    },
86    ReviewCompleted {
87        pass: u32,
88        issues_found: bool,
89    },
90    FixAttemptStarted {
91        pass: u32,
92    },
93    FixAttemptCompleted {
94        pass: u32,
95        changes_made: bool,
96    },
97    ReviewPhaseCompleted {
98        early_exit: bool,
99    },
100
101    AgentInvocationStarted {
102        role: AgentRole,
103        agent: String,
104        model: Option<String>,
105    },
106    AgentInvocationSucceeded {
107        role: AgentRole,
108        agent: String,
109    },
110    AgentInvocationFailed {
111        role: AgentRole,
112        agent: String,
113        exit_code: i32,
114        error_kind: AgentErrorKind,
115        retriable: bool,
116    },
117    AgentFallbackTriggered {
118        role: AgentRole,
119        from_agent: String,
120        to_agent: String,
121    },
122    AgentModelFallbackTriggered {
123        role: AgentRole,
124        agent: String,
125        from_model: String,
126        to_model: String,
127    },
128    AgentRetryCycleStarted {
129        role: AgentRole,
130        cycle: u32,
131    },
132    AgentChainExhausted {
133        role: AgentRole,
134    },
135    AgentChainInitialized {
136        role: AgentRole,
137        agents: Vec<String>,
138    },
139    /// Agent hit rate limit (429) - should fallback to next agent immediately.
140    ///
141    /// Unlike other retriable errors (Network, Timeout), rate limits indicate
142    /// the current provider is temporarily exhausted. Rather than waiting and
143    /// retrying the same agent, we immediately switch to the next agent in the
144    /// chain to continue work without delay.
145    AgentRateLimitFallback {
146        role: AgentRole,
147        agent: String,
148        /// The prompt that was being executed when rate limit was hit.
149        /// This allows the next agent to continue the same work.
150        prompt_context: Option<String>,
151    },
152
153    RebaseStarted {
154        phase: RebasePhase,
155        target_branch: String,
156    },
157    RebaseConflictDetected {
158        files: Vec<PathBuf>,
159    },
160    RebaseConflictResolved {
161        files: Vec<PathBuf>,
162    },
163    RebaseSucceeded {
164        phase: RebasePhase,
165        new_head: String,
166    },
167    RebaseFailed {
168        phase: RebasePhase,
169        reason: String,
170    },
171    RebaseAborted {
172        phase: RebasePhase,
173        restored_to: String,
174    },
175    RebaseSkipped {
176        phase: RebasePhase,
177        reason: String,
178    },
179
180    CommitGenerationStarted,
181    CommitMessageGenerated {
182        message: String,
183        attempt: u32,
184    },
185    CommitMessageValidationFailed {
186        reason: String,
187        attempt: u32,
188    },
189    CommitCreated {
190        hash: String,
191        message: String,
192    },
193    CommitGenerationFailed {
194        reason: String,
195    },
196    CommitSkipped {
197        reason: String,
198    },
199
200    CheckpointSaved {
201        trigger: CheckpointTrigger,
202    },
203
204    /// Finalization phase started.
205    FinalizingStarted,
206
207    /// PROMPT.md write permissions have been restored.
208    ///
209    /// This event is emitted after the RestorePromptPermissions effect
210    /// successfully restores write permissions on PROMPT.md.
211    PromptPermissionsRestored,
212
213    /// Development iteration needs continuation due to partial/failed status.
214    ///
215    /// Emitted only when development output is valid (`output_valid == true`) and
216    /// status is not "completed" (i.e., "partial" or "failed"). Invalid XML/XSD
217    /// failures are handled separately and must not consume the continuation budget.
218    DevelopmentIterationContinuationTriggered {
219        /// Current iteration number.
220        iteration: u32,
221        /// Status from the agent ("partial" or "failed").
222        status: DevelopmentStatus,
223        /// Summary of what was accomplished.
224        summary: String,
225        /// Files changed in this attempt.
226        files_changed: Option<Vec<String>>,
227        /// Agent's recommended next steps.
228        next_steps: Option<String>,
229    },
230
231    /// Development iteration continuation succeeded.
232    ///
233    /// Emitted when a continuation attempt completes with status "completed".
234    /// This resets the continuation state and allows the iteration to complete.
235    DevelopmentIterationContinuationSucceeded {
236        /// Current iteration number.
237        iteration: u32,
238        /// Number of continuation attempts it took.
239        total_continuation_attempts: u32,
240    },
241}
242
243/// Rebase phase (initial or post-review).
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
245pub enum RebasePhase {
246    Initial,
247    PostReview,
248}
249
250/// Error kind for agent failures.
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
252pub enum AgentErrorKind {
253    Network,
254    Authentication,
255    RateLimit,
256    Timeout,
257    InternalError,
258    ModelUnavailable,
259    ParsingError,
260    FileSystem,
261}
262
263/// Conflict resolution strategy.
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
265pub enum ConflictStrategy {
266    Abort,
267    Continue,
268    Skip,
269}
270
271/// Checkpoint save trigger.
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
273pub enum CheckpointTrigger {
274    PhaseTransition,
275    IterationComplete,
276    BeforeRebase,
277    Interrupt,
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283
284    #[test]
285    fn test_pipeline_phase_display() {
286        assert_eq!(format!("{}", PipelinePhase::Planning), "Planning");
287        assert_eq!(format!("{}", PipelinePhase::Development), "Development");
288        assert_eq!(format!("{}", PipelinePhase::Review), "Review");
289        assert_eq!(
290            format!("{}", PipelinePhase::CommitMessage),
291            "Commit Message"
292        );
293        assert_eq!(
294            format!("{}", PipelinePhase::FinalValidation),
295            "Final Validation"
296        );
297        assert_eq!(format!("{}", PipelinePhase::Finalizing), "Finalizing");
298        assert_eq!(format!("{}", PipelinePhase::Complete), "Complete");
299        assert_eq!(format!("{}", PipelinePhase::Interrupted), "Interrupted");
300    }
301}