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}