use crate::checkpoint::execution_history::ExecutionHistory;
use crate::checkpoint::state::{PipelineCheckpoint, PipelinePhase, RebaseState};
use crate::config::Config;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResumeContext {
pub phase: PipelinePhase,
pub iteration: u32,
pub total_iterations: u32,
pub reviewer_pass: u32,
pub total_reviewer_passes: u32,
pub resume_count: u32,
pub rebase_state: RebaseState,
pub run_id: String,
pub prompt_history:
Option<std::collections::HashMap<String, crate::prompts::PromptHistoryEntry>>,
pub execution_history: Option<ExecutionHistory>,
}
impl ResumeContext {
#[must_use]
pub fn phase_name(&self) -> String {
match self.phase {
PipelinePhase::Rebase => "Rebase".to_string(),
PipelinePhase::Planning => "Planning".to_string(),
PipelinePhase::Development => format!(
"Development iteration {}/{}",
self.iteration, self.total_iterations
),
PipelinePhase::Review => format!(
"Review (pass {}/{})",
self.reviewer_pass, self.total_reviewer_passes
),
PipelinePhase::CommitMessage => "Commit Message Generation".to_string(),
PipelinePhase::FinalValidation => "Final Validation".to_string(),
PipelinePhase::Complete => "Complete".to_string(),
PipelinePhase::PreRebase => "Pre-Rebase".to_string(),
PipelinePhase::PreRebaseConflict => "Pre-Rebase Conflict".to_string(),
PipelinePhase::PostRebase => "Post-Rebase".to_string(),
PipelinePhase::PostRebaseConflict => "Post-Rebase Conflict".to_string(),
PipelinePhase::AwaitingDevFix => "Awaiting Dev Fix".to_string(),
PipelinePhase::Interrupted => "Interrupted".to_string(),
}
}
}
impl PipelineCheckpoint {
#[must_use]
pub fn resume_context(&self) -> ResumeContext {
ResumeContext {
phase: self.phase,
iteration: self.iteration,
total_iterations: self.total_iterations,
reviewer_pass: self.reviewer_pass,
total_reviewer_passes: self.total_reviewer_passes,
resume_count: self.resume_count,
rebase_state: self.rebase_state.clone(),
run_id: self.run_id.clone(),
prompt_history: self.prompt_history.clone(),
execution_history: self.execution_history.clone(),
}
}
}
pub fn apply_checkpoint_to_config(config: &mut Config, checkpoint: &PipelineCheckpoint) {
let cli_args = &checkpoint.cli_args;
config.developer_iters = cli_args.developer_iters;
config.reviewer_reviews = cli_args.reviewer_reviews;
if let Some(ref model) = checkpoint.developer_agent_config.model_override {
config.developer_model = Some(model.clone());
}
if let Some(ref model) = checkpoint.reviewer_agent_config.model_override {
config.reviewer_model = Some(model.clone());
}
if let Some(ref provider) = checkpoint.developer_agent_config.provider_override {
config.developer_provider = Some(provider.clone());
}
if let Some(ref provider) = checkpoint.reviewer_agent_config.provider_override {
config.reviewer_provider = Some(provider.clone());
}
config.developer_context = checkpoint.developer_agent_config.context_level;
config.reviewer_context = checkpoint.reviewer_agent_config.context_level;
if let Some(ref name) = checkpoint.git_user_name {
config.git_user_name = Some(name.clone());
}
if let Some(ref email) = checkpoint.git_user_email {
config.git_user_email = Some(email.clone());
}
config.isolation_mode = cli_args.isolation_mode;
config.verbosity = crate::config::types::Verbosity::from(cli_args.verbosity);
config.show_streaming_metrics = cli_args.show_streaming_metrics;
if let Some(ref parser) = cli_args.reviewer_json_parser {
config.reviewer_json_parser = Some(parser.clone());
}
}
pub use crate::checkpoint::environment::restore_environment_from_checkpoint;
pub use crate::checkpoint::environment::restore_environment_impl;
#[must_use]
pub fn calculate_start_iteration(checkpoint: &PipelineCheckpoint, max_iterations: u32) -> u32 {
match checkpoint.phase {
PipelinePhase::Planning | PipelinePhase::Development => {
checkpoint.iteration.clamp(1, max_iterations)
}
_ => max_iterations,
}
}
#[must_use]
pub fn calculate_start_reviewer_pass(checkpoint: &PipelineCheckpoint, max_passes: u32) -> u32 {
match checkpoint.phase {
PipelinePhase::Review => checkpoint.reviewer_pass.clamp(1, max_passes.max(1)),
PipelinePhase::Planning
| PipelinePhase::Development
| PipelinePhase::PreRebase
| PipelinePhase::PreRebaseConflict => 1,
_ => max_passes,
}
}
#[must_use]
pub const fn should_skip_phase(phase: PipelinePhase, checkpoint: &PipelineCheckpoint) -> bool {
phase_rank(phase) < phase_rank(checkpoint.phase)
}
const fn phase_rank(phase: PipelinePhase) -> u32 {
match phase {
PipelinePhase::Planning => 0,
PipelinePhase::Development => 1,
PipelinePhase::CommitMessage => 3,
PipelinePhase::FinalValidation => 4,
PipelinePhase::Complete => 5,
PipelinePhase::AwaitingDevFix => 6,
PipelinePhase::Interrupted => 7,
PipelinePhase::Review
| PipelinePhase::PreRebase
| PipelinePhase::PreRebaseConflict
| PipelinePhase::Rebase
| PipelinePhase::PostRebase
| PipelinePhase::PostRebaseConflict => 2,
}
}
#[cfg(test)]
#[derive(Debug, Clone)]
pub struct RestoredContext {
pub phase: PipelinePhase,
pub resume_iteration: u32,
pub total_iterations: u32,
pub resume_reviewer_pass: u32,
pub total_reviewer_passes: u32,
pub developer_agent: String,
pub reviewer_agent: String,
pub cli_args: Option<crate::checkpoint::state::CliArgsSnapshot>,
}
#[cfg(test)]
impl RestoredContext {
#[must_use]
pub fn from_checkpoint(checkpoint: &PipelineCheckpoint) -> Self {
let cli_args = if checkpoint.cli_args.developer_iters > 0
|| checkpoint.cli_args.reviewer_reviews > 0
{
Some(checkpoint.cli_args.clone())
} else {
None
};
Self {
phase: checkpoint.phase,
resume_iteration: checkpoint.iteration,
total_iterations: checkpoint.total_iterations,
resume_reviewer_pass: checkpoint.reviewer_pass,
total_reviewer_passes: checkpoint.total_reviewer_passes,
developer_agent: checkpoint.developer_agent.clone(),
reviewer_agent: checkpoint.reviewer_agent.clone(),
cli_args,
}
}
#[must_use]
pub fn should_use_checkpoint_iterations(&self) -> bool {
self.cli_args
.as_ref()
.is_some_and(|args| args.developer_iters > 0)
}
#[must_use]
pub fn should_use_checkpoint_reviewer_passes(&self) -> bool {
self.cli_args
.as_ref()
.is_some_and(|args| args.reviewer_reviews > 0)
}
}
#[cfg(test)]
mod tests;