Skip to main content

ralph_workflow/app/boundary/
conflict_resolution.rs

1//! Conflict resolution boundary module.
2//!
3//! This module provides boundary functions for conflict resolution that involve
4//! runtime creation and execution. As a boundary module, it is exempt from
5//! functional programming lints.
6
7use crate::app::rebase::ConflictResolutionResult;
8use crate::config::Config;
9use crate::logger::{Colors, Logger};
10use crate::workspace::Workspace;
11use std::path::Path;
12use std::sync::Arc;
13
14pub struct ConflictResolutionRuntimeParams<'a> {
15    pub resolution_prompt: &'a str,
16    pub config: &'a Config,
17    pub logger: &'a Logger,
18    pub colors: Colors,
19    pub executor_arc: Arc<dyn crate::executor::ProcessExecutor>,
20    pub workspace: &'a dyn Workspace,
21    pub workspace_arc: Arc<dyn Workspace>,
22    pub reviewer_agent: &'a str,
23    pub registry: &'a crate::agents::AgentRegistry,
24}
25
26pub fn run_ai_conflict_resolution_with_runtime(
27    params: ConflictResolutionRuntimeParams<'_>,
28) -> anyhow::Result<ConflictResolutionResult> {
29    let ConflictResolutionRuntimeParams {
30        resolution_prompt,
31        config,
32        logger,
33        colors,
34        executor_arc,
35        workspace,
36        workspace_arc,
37        reviewer_agent,
38        registry,
39    } = params;
40    let mut timer = crate::app::runtime_factory::create_timer();
41    let executor_ref: &dyn crate::executor::ProcessExecutor = &*executor_arc;
42    let mut runtime = crate::app::runtime_factory::create_pipeline_runtime(
43        crate::app::runtime_factory::PipelineRuntimeFactoryParams {
44            timer: &mut timer,
45            logger,
46            colors: &colors,
47            config,
48            executor: executor_ref,
49            executor_arc: Arc::clone(&executor_arc),
50            workspace,
51            workspace_arc: Arc::clone(&workspace_arc),
52        },
53    );
54    let log_dir = ".agent/logs/rebase_conflict_resolution";
55    workspace.create_dir_all(Path::new(log_dir))?;
56    run_conflict_prompt_and_check(
57        resolution_prompt,
58        reviewer_agent,
59        log_dir,
60        registry,
61        workspace,
62        &mut runtime,
63    )
64}
65
66fn run_conflict_prompt_and_check(
67    resolution_prompt: &str,
68    reviewer_agent: &str,
69    log_dir: &str,
70    registry: &crate::agents::AgentRegistry,
71    workspace: &dyn Workspace,
72    runtime: &mut crate::pipeline::PipelineRuntime<'_>,
73) -> anyhow::Result<ConflictResolutionResult> {
74    let agent_config = registry
75        .resolve_config(reviewer_agent)
76        .ok_or_else(|| anyhow::anyhow!("Agent not found: {reviewer_agent}"))?;
77    let cmd_str = agent_config.build_cmd_with_model(true, true, true, None);
78    let log_prefix = format!("{log_dir}/conflict_resolution");
79    let model_index = 0usize;
80    let attempt = crate::pipeline::logfile::next_logfile_attempt_index(
81        Path::new(&log_prefix),
82        reviewer_agent,
83        model_index,
84        workspace,
85    );
86    let logfile = crate::pipeline::logfile::build_logfile_path_with_attempt(
87        &log_prefix,
88        reviewer_agent,
89        model_index,
90        attempt,
91    );
92    let prompt_cmd = crate::pipeline::PromptCommand {
93        label: reviewer_agent,
94        display_name: reviewer_agent,
95        cmd_str: &cmd_str,
96        prompt: resolution_prompt,
97        log_prefix: &log_prefix,
98        model_index: Some(model_index),
99        attempt: Some(attempt),
100        logfile: &logfile,
101        parser_type: agent_config.json_parser,
102        env_vars: &agent_config.env_vars,
103        completion_output_path: None,
104    };
105    let result = crate::pipeline::run_with_prompt(&prompt_cmd, runtime)?;
106    check_conflict_exit_and_remaining(result.exit_code)
107}
108
109fn check_conflict_exit_and_remaining(exit_code: i32) -> anyhow::Result<ConflictResolutionResult> {
110    if exit_code != 0 {
111        return Ok(ConflictResolutionResult::Failed);
112    }
113    let remaining_conflicts = crate::git_helpers::get_conflicted_files()?;
114    if remaining_conflicts.is_empty() {
115        Ok(ConflictResolutionResult::FileEditsOnly)
116    } else {
117        Ok(ConflictResolutionResult::Failed)
118    }
119}