Skip to main content

ralph_workflow/app/event_loop/
core.rs

1//! Main event loop implementation.
2//!
3//! This module contains the core orchestrate-handle-reduce cycle that drives
4//! the reducer-based pipeline. The event loop coordinates pure reducer logic
5//! with impure effect handlers, maintaining strict separation of concerns.
6//!
7//! ## Event Loop Cycle
8//!
9//! ```text
10//! State → Orchestrate → Effect → Handle → Event → Reduce → Next State
11//!         (pure)                 (impure)         (pure)
12//! ```
13//!
14//! The loop continues until reaching a terminal state (Interrupted, Completed)
15//! or until max iterations is exceeded.
16//!
17//! ## Architecture
18//!
19//! The event loop is organized into several modules:
20//! - `driver` - Main iteration loop implementing orchestrate→handle→reduce cycle
21//! - `recovery` - Defensive completion and max iterations handling
22//! - `error_handling` - Panic recovery and error routing
23//! - `trace` - Trace buffer and diagnostic dumps
24//! - `config` - Configuration and initialization
25
26use super::config::{create_initial_state_with_config, EventLoopConfig, EventLoopResult};
27use super::driver::run_event_loop_driver;
28use crate::phases::PhaseContext;
29use crate::reducer::{EffectHandler, MainEffectHandler, PipelineState};
30use anyhow::Result;
31
32/// Trait for handlers that maintain internal state.
33///
34/// This trait allows the event loop to update the handler's internal state
35/// after each event is processed.
36pub trait StatefulHandler {
37    /// Update the handler's internal state.
38    fn update_state(&mut self, state: PipelineState);
39}
40
41/// Run the main event loop for the reducer-based pipeline.
42///
43/// This function orchestrates pipeline execution by repeatedly:
44/// 1. Determining the next effect based on the current state
45/// 2. Executing the effect through the effect handler (which performs side effects)
46/// 3. Applying the resulting event to state through the reducer (pure function)
47/// 4. Repeating until a terminal state is reached or max iterations exceeded
48///
49/// The entire event loop is wrapped in panic recovery to ensure the pipeline
50/// never crashes due to agent failures (panics only; aborts/segfaults cannot be recovered).
51///
52/// # Arguments
53///
54/// * `ctx` - Phase context for effect handlers
55/// * `initial_state` - Optional initial state (if None, creates a new state)
56/// * `config` - Event loop configuration
57///
58/// # Returns
59///
60/// Returns the event loop result containing the completion status and final state.
61///
62/// # Errors
63///
64/// Returns error if the operation fails.
65pub fn run_event_loop(
66    ctx: &mut PhaseContext<'_>,
67    initial_state: Option<PipelineState>,
68    config: EventLoopConfig,
69) -> Result<EventLoopResult> {
70    let state = initial_state.unwrap_or_else(|| create_initial_state_with_config(ctx));
71    let mut handler = MainEffectHandler::new(state.clone());
72    run_event_loop_driver(ctx, Some(state), config, &mut handler)
73}
74
75/// Run the event loop with a custom effect handler.
76///
77/// This variant allows injecting a custom effect handler for testing.
78/// The handler must implement `EffectHandler` and `StatefulHandler` traits.
79///
80/// # Arguments
81///
82/// * `ctx` - Phase context for effect handlers
83/// * `initial_state` - Optional initial state (if None, creates a new state)
84/// * `config` - Event loop configuration
85/// * `handler` - Custom effect handler (e.g., `MockEffectHandler` for testing)
86///
87/// # Returns
88///
89/// Returns the event loop result containing the completion status and final state.
90///
91/// # Errors
92///
93/// Returns error if the operation fails.
94pub fn run_event_loop_with_handler<'ctx, H>(
95    ctx: &mut PhaseContext<'_>,
96    initial_state: Option<PipelineState>,
97    config: EventLoopConfig,
98    handler: &mut H,
99) -> Result<EventLoopResult>
100where
101    H: EffectHandler<'ctx> + StatefulHandler,
102{
103    run_event_loop_driver(ctx, initial_state, config, handler)
104}