Skip to main content

ralph_workflow/reducer/
migration.rs

1//! Checkpoint migration from v3 format to reducer state.
2//!
3//! Implements migration from existing checkpoint format to new PipelineState.
4
5use crate::checkpoint::PipelineCheckpoint;
6use crate::checkpoint::PipelinePhase as CheckpointPhase;
7use crate::checkpoint::RebaseState as CheckpointRebaseState;
8
9use super::event::PipelinePhase;
10use super::state::{PipelineState, RebaseState};
11
12/// Convert v3 checkpoint to reducer PipelineState.
13///
14/// Migrates existing checkpoint structure to new state format,
15/// ensuring backward compatibility with saved checkpoints.
16impl From<PipelineCheckpoint> for PipelineState {
17    fn from(checkpoint: PipelineCheckpoint) -> Self {
18        let rebase_state = migrate_rebase_state(&checkpoint.rebase_state);
19
20        let agent_chain = super::state::AgentChainState::initial();
21
22        PipelineState {
23            phase: migrate_phase(checkpoint.phase),
24            previous_phase: None, // No previous phase for migrated checkpoints
25            iteration: checkpoint.iteration,
26            total_iterations: checkpoint.total_iterations,
27            reviewer_pass: checkpoint.reviewer_pass,
28            total_reviewer_passes: checkpoint.total_reviewer_passes,
29            review_issues_found: false, // Default to false for migrated checkpoints
30            context_cleaned: false,     // Default to false for migrated checkpoints
31            agent_chain,
32            rebase: rebase_state,
33            commit: super::state::CommitState::NotStarted,
34            execution_history: checkpoint
35                .execution_history
36                .map(|h| h.steps)
37                .unwrap_or_default(),
38        }
39    }
40}
41
42/// Migrate checkpoint phase to reducer phase.
43fn migrate_phase(phase: CheckpointPhase) -> PipelinePhase {
44    match phase {
45        CheckpointPhase::Rebase => PipelinePhase::Planning,
46        CheckpointPhase::Planning => PipelinePhase::Planning,
47        CheckpointPhase::Development => PipelinePhase::Development,
48        CheckpointPhase::Review => PipelinePhase::Review,
49        CheckpointPhase::Fix => PipelinePhase::Review,
50        CheckpointPhase::ReviewAgain => PipelinePhase::Review,
51        CheckpointPhase::CommitMessage => PipelinePhase::CommitMessage,
52        CheckpointPhase::FinalValidation => PipelinePhase::FinalValidation,
53        CheckpointPhase::Complete => PipelinePhase::Complete,
54        CheckpointPhase::PreRebase => PipelinePhase::Planning,
55        CheckpointPhase::PreRebaseConflict => PipelinePhase::Planning,
56        CheckpointPhase::PostRebase => PipelinePhase::CommitMessage,
57        CheckpointPhase::PostRebaseConflict => PipelinePhase::CommitMessage,
58        CheckpointPhase::Interrupted => PipelinePhase::Interrupted,
59    }
60}
61
62/// Migrate checkpoint rebase state to reducer rebase state.
63fn migrate_rebase_state(rebase_state: &CheckpointRebaseState) -> RebaseState {
64    match rebase_state {
65        CheckpointRebaseState::NotStarted => RebaseState::NotStarted,
66        CheckpointRebaseState::PreRebaseInProgress { upstream_branch } => RebaseState::InProgress {
67            original_head: "HEAD".to_string(),
68            target_branch: upstream_branch.clone(),
69        },
70        CheckpointRebaseState::PreRebaseCompleted { commit_oid } => RebaseState::Completed {
71            new_head: commit_oid.clone(),
72        },
73        CheckpointRebaseState::PostRebaseInProgress { upstream_branch } => {
74            RebaseState::InProgress {
75                original_head: "HEAD".to_string(),
76                target_branch: upstream_branch.clone(),
77            }
78        }
79        CheckpointRebaseState::PostRebaseCompleted { commit_oid } => RebaseState::Completed {
80            new_head: commit_oid.clone(),
81        },
82        CheckpointRebaseState::HasConflicts { files } => RebaseState::Conflicted {
83            original_head: "HEAD".to_string(),
84            target_branch: "main".to_string(),
85            files: files
86                .iter()
87                .map(|s| std::path::PathBuf::from(s.clone()))
88                .collect(),
89            resolution_attempts: 0,
90        },
91        CheckpointRebaseState::Failed { .. } => RebaseState::Skipped,
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_migrate_phase_planning() {
101        let phase = migrate_phase(CheckpointPhase::Planning);
102        assert_eq!(phase, PipelinePhase::Planning);
103    }
104
105    #[test]
106    fn test_migrate_phase_development() {
107        let phase = migrate_phase(CheckpointPhase::Development);
108        assert_eq!(phase, PipelinePhase::Development);
109    }
110
111    #[test]
112    fn test_migrate_phase_review() {
113        let phase = migrate_phase(CheckpointPhase::Review);
114        assert_eq!(phase, PipelinePhase::Review);
115    }
116
117    #[test]
118    fn test_migrate_phase_commit_message() {
119        let phase = migrate_phase(CheckpointPhase::CommitMessage);
120        assert_eq!(phase, PipelinePhase::CommitMessage);
121    }
122
123    #[test]
124    fn test_migrate_phase_complete() {
125        let phase = migrate_phase(CheckpointPhase::Complete);
126        assert_eq!(phase, PipelinePhase::Complete);
127    }
128
129    #[test]
130    fn test_migrate_rebase_state_not_started() {
131        let state = migrate_rebase_state(&CheckpointRebaseState::NotStarted);
132        assert!(matches!(state, RebaseState::NotStarted));
133    }
134
135    #[test]
136    fn test_migrate_rebase_state_pre_in_progress() {
137        let state = migrate_rebase_state(&CheckpointRebaseState::PreRebaseInProgress {
138            upstream_branch: "main".to_string(),
139        });
140        assert!(matches!(state, RebaseState::InProgress { .. }));
141    }
142
143    #[test]
144    fn test_migrate_rebase_state_has_conflicts() {
145        let files = vec!["file1.rs".to_string(), "file2.rs".to_string()];
146        let state = migrate_rebase_state(&CheckpointRebaseState::HasConflicts {
147            files: files.clone(),
148        });
149        assert!(matches!(state, RebaseState::Conflicted { .. }));
150    }
151
152    #[test]
153    fn test_migrate_rebase_state_completed() {
154        let state = migrate_rebase_state(&CheckpointRebaseState::PreRebaseCompleted {
155            commit_oid: "abc123".to_string(),
156        });
157        assert!(matches!(state, RebaseState::Completed { .. }));
158    }
159}