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            // Default to no continuation in progress (backward compatibility)
39            continuation: super::state::ContinuationState::new(),
40        }
41    }
42}
43
44/// Migrate checkpoint phase to reducer phase.
45fn migrate_phase(phase: CheckpointPhase) -> PipelinePhase {
46    match phase {
47        CheckpointPhase::Rebase => PipelinePhase::Planning,
48        CheckpointPhase::Planning => PipelinePhase::Planning,
49        CheckpointPhase::Development => PipelinePhase::Development,
50        CheckpointPhase::Review => PipelinePhase::Review,
51        CheckpointPhase::Fix => PipelinePhase::Review,
52        CheckpointPhase::ReviewAgain => PipelinePhase::Review,
53        CheckpointPhase::CommitMessage => PipelinePhase::CommitMessage,
54        CheckpointPhase::FinalValidation => PipelinePhase::FinalValidation,
55        CheckpointPhase::Complete => PipelinePhase::Complete,
56        CheckpointPhase::PreRebase => PipelinePhase::Planning,
57        CheckpointPhase::PreRebaseConflict => PipelinePhase::Planning,
58        CheckpointPhase::PostRebase => PipelinePhase::CommitMessage,
59        CheckpointPhase::PostRebaseConflict => PipelinePhase::CommitMessage,
60        CheckpointPhase::Interrupted => PipelinePhase::Interrupted,
61    }
62}
63
64/// Migrate checkpoint rebase state to reducer rebase state.
65fn migrate_rebase_state(rebase_state: &CheckpointRebaseState) -> RebaseState {
66    match rebase_state {
67        CheckpointRebaseState::NotStarted => RebaseState::NotStarted,
68        CheckpointRebaseState::PreRebaseInProgress { upstream_branch } => RebaseState::InProgress {
69            original_head: "HEAD".to_string(),
70            target_branch: upstream_branch.clone(),
71        },
72        CheckpointRebaseState::PreRebaseCompleted { commit_oid } => RebaseState::Completed {
73            new_head: commit_oid.clone(),
74        },
75        CheckpointRebaseState::PostRebaseInProgress { upstream_branch } => {
76            RebaseState::InProgress {
77                original_head: "HEAD".to_string(),
78                target_branch: upstream_branch.clone(),
79            }
80        }
81        CheckpointRebaseState::PostRebaseCompleted { commit_oid } => RebaseState::Completed {
82            new_head: commit_oid.clone(),
83        },
84        CheckpointRebaseState::HasConflicts { files } => RebaseState::Conflicted {
85            original_head: "HEAD".to_string(),
86            target_branch: "main".to_string(),
87            files: files
88                .iter()
89                .map(|s| std::path::PathBuf::from(s.clone()))
90                .collect(),
91            resolution_attempts: 0,
92        },
93        CheckpointRebaseState::Failed { .. } => RebaseState::Skipped,
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_migrate_phase_planning() {
103        let phase = migrate_phase(CheckpointPhase::Planning);
104        assert_eq!(phase, PipelinePhase::Planning);
105    }
106
107    #[test]
108    fn test_migrate_phase_development() {
109        let phase = migrate_phase(CheckpointPhase::Development);
110        assert_eq!(phase, PipelinePhase::Development);
111    }
112
113    #[test]
114    fn test_migrate_phase_review() {
115        let phase = migrate_phase(CheckpointPhase::Review);
116        assert_eq!(phase, PipelinePhase::Review);
117    }
118
119    #[test]
120    fn test_migrate_phase_commit_message() {
121        let phase = migrate_phase(CheckpointPhase::CommitMessage);
122        assert_eq!(phase, PipelinePhase::CommitMessage);
123    }
124
125    #[test]
126    fn test_migrate_phase_complete() {
127        let phase = migrate_phase(CheckpointPhase::Complete);
128        assert_eq!(phase, PipelinePhase::Complete);
129    }
130
131    #[test]
132    fn test_migrate_rebase_state_not_started() {
133        let state = migrate_rebase_state(&CheckpointRebaseState::NotStarted);
134        assert!(matches!(state, RebaseState::NotStarted));
135    }
136
137    #[test]
138    fn test_migrate_rebase_state_pre_in_progress() {
139        let state = migrate_rebase_state(&CheckpointRebaseState::PreRebaseInProgress {
140            upstream_branch: "main".to_string(),
141        });
142        assert!(matches!(state, RebaseState::InProgress { .. }));
143    }
144
145    #[test]
146    fn test_migrate_rebase_state_has_conflicts() {
147        let files = vec!["file1.rs".to_string(), "file2.rs".to_string()];
148        let state = migrate_rebase_state(&CheckpointRebaseState::HasConflicts {
149            files: files.clone(),
150        });
151        assert!(matches!(state, RebaseState::Conflicted { .. }));
152    }
153
154    #[test]
155    fn test_migrate_rebase_state_completed() {
156        let state = migrate_rebase_state(&CheckpointRebaseState::PreRebaseCompleted {
157            commit_oid: "abc123".to_string(),
158        });
159        assert!(matches!(state, RebaseState::Completed { .. }));
160    }
161}