Skip to main content

ralph_workflow/reducer/
effect.rs

1//! Effect types and handlers for side effects.
2//!
3//! Effects represent side-effect operations that the reducer triggers.
4//! Effect handlers execute effects and emit events.
5
6use crate::agents::AgentRole;
7use crate::phases::PhaseContext;
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10
11use super::event::{CheckpointTrigger, ConflictStrategy, PipelineEvent, RebasePhase};
12
13/// Effects represent side-effect operations.
14///
15/// The reducer determines which effect to execute next based on state.
16/// Effect handlers execute effects and emit events.
17#[derive(Clone, Serialize, Deserialize, Debug)]
18pub enum Effect {
19    AgentInvocation {
20        role: AgentRole,
21        agent: String,
22        model: Option<String>,
23        prompt: String,
24    },
25
26    InitializeAgentChain {
27        role: AgentRole,
28    },
29
30    GeneratePlan {
31        iteration: u32,
32    },
33
34    RunDevelopmentIteration {
35        iteration: u32,
36    },
37
38    RunReviewPass {
39        pass: u32,
40    },
41
42    RunFixAttempt {
43        pass: u32,
44    },
45
46    RunRebase {
47        phase: RebasePhase,
48        target_branch: String,
49    },
50
51    ResolveRebaseConflicts {
52        strategy: ConflictStrategy,
53    },
54
55    GenerateCommitMessage,
56
57    CreateCommit {
58        message: String,
59    },
60
61    SkipCommit {
62        reason: String,
63    },
64
65    ValidateFinalState,
66
67    SaveCheckpoint {
68        trigger: CheckpointTrigger,
69    },
70
71    CleanupContext,
72
73    /// Restore PROMPT.md write permissions after pipeline completion.
74    ///
75    /// This effect is emitted during the Finalizing phase to restore
76    /// write permissions on PROMPT.md so users can edit it normally
77    /// after Ralph exits.
78    RestorePromptPermissions,
79}
80
81/// Trait for executing effects.
82///
83/// This trait allows mocking in tests by providing alternative implementations.
84pub trait EffectHandler<'ctx> {
85    fn execute(&mut self, effect: Effect, ctx: &mut PhaseContext<'_>) -> Result<PipelineEvent>;
86}
87
88#[cfg(test)]
89mod tests {
90    // #[test]
91    // fn test_effect_serialization() {
92    //     let effect = Effect::AgentInvocation {
93    //         role: AgentRole::Developer,
94    //         agent: "claude".to_string(),
95    //         model: None,
96    //         prompt: "test".to_string(),
97    //     };
98
99    //     let json = serde_json::to_string(&effect).unwrap();
100    //     let deserialized: Effect = serde_json::from_str(&json).unwrap();
101
102    //     match deserialized {
103    //         Effect::AgentInvocation {
104    //             role,
105    //             agent,
106    //             model,
107    //             prompt,
108    //         } => {
109    //             assert_eq!(role, AgentRole::Developer);
110    //             assert_eq!(agent, "claude");
111    //             assert_eq!(model.is_none());
112    //             assert_eq!(prompt, "test");
113    //         }
114    //         _ => panic!("Expected AgentInvocation effect"),
115    //     }
116    // }
117}