ralph-workflow 0.7.18

PROMPT-driven multi-agent orchestrator for git repos
Documentation
// Pipeline execution boundary — orchestrates I/O-heavy pipeline lifecycle.
//
// This file is included from runtime_execution_core.rs via include!("boundary.rs").
// Being in a path containing "boundary", it is exempt from the functional-programming
// lints (forbid_mut_binding, forbid_imperative_loops, forbid_mutating_receiver_methods).
//
// Mutation here is genuinely required: git helpers, agent-phase guards, prompt monitors,
// timers and phase contexts are OS-level or process-level handles whose APIs demand &mut.
// This is exactly the IMPURE→PURE→IMPURE boundary seam the architecture calls for.

/// Runs the pipeline with the default `MainEffectHandler`.
///
/// This is the production entry point - it creates a `MainEffectHandler` internally.
pub(super) fn run_pipeline_with_default_handler(ctx: &PipelineContext) -> anyhow::Result<()> {
    use crate::app::pipeline_setup::{setup_git_and_agent_phase, setup_phase_context_with_timer};

    let resume_state = load_resume_and_config_state(ctx)?;
    let mut git_helpers = crate::app::pipeline_setup::create_git_helpers_boundary();
    let mut agent_phase_guard = setup_git_and_agent_phase(&mut git_helpers, ctx);

    let (cloud_reporter, _heartbeat_guard) = create_cloud_runtime(&resume_state.config);

    print_welcome_banner(ctx.colors, &ctx.developer_display, &ctx.reviewer_display);
    print_pipeline_info_with_config(ctx, &resume_state.config);
    validate_prompt_and_setup_backup(ctx)?;

    let mut prompt_monitor = setup_prompt_monitor(ctx);
    let (_project_stack, review_guidelines) = detect_project_stack(
        &resume_state.config,
        &ctx.repo_root,
        &ctx.logger,
        ctx.colors,
    );
    print_review_guidelines(ctx, review_guidelines.as_ref());
    let _ = writeln!(std::io::stdout());

    let mut timer = crate::app::runtime_factory::create_timer();
    let mut phase_ctx = setup_phase_context_with_timer(
        &mut timer,
        ctx,
        &resume_state.config,
        review_guidelines.as_ref(),
        &resume_state.run_context,
        resume_state.resume_checkpoint.as_ref(),
        cloud_reporter.as_ref(),
    );
    save_start_commit_or_warn(ctx);

    let initial_phase = resume_state
        .resume_checkpoint
        .as_ref()
        .map_or(PipelinePhase::Planning, |checkpoint| checkpoint.phase);

    // Snapshot the initial prompt history from the checkpoint (if any) for the interrupt
    // context.
    //
    // Note: while the reducer event loop is active, Ctrl+C triggers a reducer-driven
    // graceful shutdown and checkpoint write from live `PipelineState` (including
    // prompt_history). The global interrupt context is only used for early interrupts
    // before the event loop starts.
    let initial_prompt_history: std::collections::HashMap<
        String,
        crate::prompts::PromptHistoryEntry,
    > = resume_state
        .resume_checkpoint
        .as_ref()
        .and_then(|c| c.prompt_history.clone())
        .unwrap_or_default();

    setup_interrupt_context_for_pipeline(
        initial_phase,
        resume_state.config.developer_iters,
        resume_state.config.reviewer_reviews,
        &phase_ctx.execution_history,
        &initial_prompt_history,
        &resume_state.run_context,
        std::sync::Arc::clone(&ctx.workspace),
    );

    let _interrupt_guard = defer_clear_interrupt_context();

    update_interrupt_context_from_phase(
        &phase_ctx.execution_history,
        initial_prompt_history,
        initial_phase,
        resume_state.config.developer_iters,
        resume_state.config.reviewer_reviews,
        &resume_state.run_context,
        std::sync::Arc::clone(&ctx.workspace),
    );

    let initial_state = compute_initial_state(
        &phase_ctx,
        resume_state.resume_checkpoint.as_ref(),
        ctx.args.rebase_flags.with_rebase,
    );

    let loop_result = run_event_loop_with_default_handler(&mut phase_ctx, initial_state)?;
    log_event_loop_outcome(ctx, &loop_result);

    let exit_after_cleanup_due_to_sigint = should_exit_due_to_sigint(&loop_result);

    save_complete_checkpoint_if_needed(
        ctx,
        &resume_state.config,
        &resume_state.run_context,
        &phase_ctx,
        &loop_result,
    );

    report_cloud_completion(
        ctx,
        &resume_state.config,
        cloud_reporter.as_ref(),
        &loop_result,
        &timer,
    )?;

    finish_pipeline(
        ctx,
        &resume_state.config,
        &timer,
        &mut agent_phase_guard,
        &mut prompt_monitor,
        &loop_result,
        exit_after_cleanup_due_to_sigint,
    )
}