ralph_workflow/checkpoint/
restore.rs1use crate::checkpoint::execution_history::ExecutionHistory;
7use crate::checkpoint::state::{PipelineCheckpoint, PipelinePhase, RebaseState};
8use crate::config::Config;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct ResumeContext {
17 pub phase: PipelinePhase,
19 pub iteration: u32,
21 pub total_iterations: u32,
23 pub reviewer_pass: u32,
25 pub total_reviewer_passes: u32,
27 pub resume_count: u32,
29 pub rebase_state: RebaseState,
31 pub run_id: String,
33 pub prompt_history:
35 Option<std::collections::HashMap<String, crate::prompts::PromptHistoryEntry>>,
36 pub execution_history: Option<ExecutionHistory>,
38}
39
40impl ResumeContext {
41 #[must_use]
43 pub fn phase_name(&self) -> String {
44 match self.phase {
45 PipelinePhase::Rebase => "Rebase".to_string(),
46 PipelinePhase::Planning => "Planning".to_string(),
47 PipelinePhase::Development => format!(
48 "Development iteration {}/{}",
49 self.iteration, self.total_iterations
50 ),
51 PipelinePhase::Review => format!(
52 "Review (pass {}/{})",
53 self.reviewer_pass, self.total_reviewer_passes
54 ),
55 PipelinePhase::CommitMessage => "Commit Message Generation".to_string(),
56 PipelinePhase::FinalValidation => "Final Validation".to_string(),
57 PipelinePhase::Complete => "Complete".to_string(),
58 PipelinePhase::PreRebase => "Pre-Rebase".to_string(),
59 PipelinePhase::PreRebaseConflict => "Pre-Rebase Conflict".to_string(),
60 PipelinePhase::PostRebase => "Post-Rebase".to_string(),
61 PipelinePhase::PostRebaseConflict => "Post-Rebase Conflict".to_string(),
62 PipelinePhase::AwaitingDevFix => "Awaiting Dev Fix".to_string(),
63 PipelinePhase::Interrupted => "Interrupted".to_string(),
64 }
65 }
66}
67
68impl PipelineCheckpoint {
69 #[must_use]
74 pub fn resume_context(&self) -> ResumeContext {
75 ResumeContext {
76 phase: self.phase,
77 iteration: self.iteration,
78 total_iterations: self.total_iterations,
79 reviewer_pass: self.reviewer_pass,
80 total_reviewer_passes: self.total_reviewer_passes,
81 resume_count: self.resume_count,
82 rebase_state: self.rebase_state.clone(),
83 run_id: self.run_id.clone(),
84 prompt_history: self.prompt_history.clone(),
85 execution_history: self.execution_history.clone(),
86 }
87 }
88}
89
90pub fn apply_checkpoint_to_config(config: &mut Config, checkpoint: &PipelineCheckpoint) {
92 let cli_args = &checkpoint.cli_args;
93
94 config.developer_iters = cli_args.developer_iters;
97 config.reviewer_reviews = cli_args.reviewer_reviews;
98
99 if let Some(ref model) = checkpoint.developer_agent_config.model_override {
105 config.developer_model = Some(model.clone());
106 }
107 if let Some(ref model) = checkpoint.reviewer_agent_config.model_override {
108 config.reviewer_model = Some(model.clone());
109 }
110
111 if let Some(ref provider) = checkpoint.developer_agent_config.provider_override {
113 config.developer_provider = Some(provider.clone());
114 }
115 if let Some(ref provider) = checkpoint.reviewer_agent_config.provider_override {
116 config.reviewer_provider = Some(provider.clone());
117 }
118
119 config.developer_context = checkpoint.developer_agent_config.context_level;
121 config.reviewer_context = checkpoint.reviewer_agent_config.context_level;
122
123 if let Some(ref name) = checkpoint.git_user_name {
125 config.git_user_name = Some(name.clone());
126 }
127 if let Some(ref email) = checkpoint.git_user_email {
128 config.git_user_email = Some(email.clone());
129 }
130
131 config.isolation_mode = cli_args.isolation_mode;
133
134 config.verbosity = crate::config::types::Verbosity::from(cli_args.verbosity);
136
137 config.show_streaming_metrics = cli_args.show_streaming_metrics;
139
140 if let Some(ref parser) = cli_args.reviewer_json_parser {
142 config.reviewer_json_parser = Some(parser.clone());
143 }
144}
145
146pub use crate::checkpoint::environment::restore_environment_from_checkpoint;
150
151pub use crate::checkpoint::environment::restore_environment_impl;
154
155#[must_use]
157pub fn calculate_start_iteration(checkpoint: &PipelineCheckpoint, max_iterations: u32) -> u32 {
158 match checkpoint.phase {
159 PipelinePhase::Planning | PipelinePhase::Development => {
160 checkpoint.iteration.clamp(1, max_iterations)
161 }
162 _ => max_iterations,
164 }
165}
166
167#[must_use]
181pub fn calculate_start_reviewer_pass(checkpoint: &PipelineCheckpoint, max_passes: u32) -> u32 {
182 match checkpoint.phase {
183 PipelinePhase::Review => checkpoint.reviewer_pass.clamp(1, max_passes.max(1)),
184 PipelinePhase::Planning
186 | PipelinePhase::Development
187 | PipelinePhase::PreRebase
188 | PipelinePhase::PreRebaseConflict => 1,
189 _ => max_passes,
191 }
192}
193
194#[must_use]
198pub const fn should_skip_phase(phase: PipelinePhase, checkpoint: &PipelineCheckpoint) -> bool {
199 phase_rank(phase) < phase_rank(checkpoint.phase)
200}
201
202const fn phase_rank(phase: PipelinePhase) -> u32 {
206 match phase {
207 PipelinePhase::Planning => 0,
208 PipelinePhase::Development => 1,
209 PipelinePhase::CommitMessage => 3,
210 PipelinePhase::FinalValidation => 4,
211 PipelinePhase::Complete => 5,
212 PipelinePhase::AwaitingDevFix => 6,
213 PipelinePhase::Interrupted => 7,
214 PipelinePhase::Review
216 | PipelinePhase::PreRebase
217 | PipelinePhase::PreRebaseConflict
218 | PipelinePhase::Rebase
219 | PipelinePhase::PostRebase
220 | PipelinePhase::PostRebaseConflict => 2,
221 }
222}
223
224#[cfg(test)]
225#[derive(Debug, Clone)]
226pub struct RestoredContext {
227 pub phase: PipelinePhase,
228 pub resume_iteration: u32,
229 pub total_iterations: u32,
230 pub resume_reviewer_pass: u32,
231 pub total_reviewer_passes: u32,
232 pub developer_agent: String,
233 pub reviewer_agent: String,
234 pub cli_args: Option<crate::checkpoint::state::CliArgsSnapshot>,
235}
236
237#[cfg(test)]
238impl RestoredContext {
239 #[must_use]
241 pub fn from_checkpoint(checkpoint: &PipelineCheckpoint) -> Self {
242 let cli_args = if checkpoint.cli_args.developer_iters > 0
244 || checkpoint.cli_args.reviewer_reviews > 0
245 {
246 Some(checkpoint.cli_args.clone())
247 } else {
248 None
249 };
250
251 Self {
252 phase: checkpoint.phase,
253 resume_iteration: checkpoint.iteration,
254 total_iterations: checkpoint.total_iterations,
255 resume_reviewer_pass: checkpoint.reviewer_pass,
256 total_reviewer_passes: checkpoint.total_reviewer_passes,
257 developer_agent: checkpoint.developer_agent.clone(),
258 reviewer_agent: checkpoint.reviewer_agent.clone(),
259 cli_args,
260 }
261 }
262
263 #[must_use]
268 pub fn should_use_checkpoint_iterations(&self) -> bool {
269 self.cli_args
270 .as_ref()
271 .is_some_and(|args| args.developer_iters > 0)
272 }
273
274 #[must_use]
276 pub fn should_use_checkpoint_reviewer_passes(&self) -> bool {
277 self.cli_args
278 .as_ref()
279 .is_some_and(|args| args.reviewer_reviews > 0)
280 }
281}
282
283#[cfg(test)]
284mod tests;